examples: adding a gRPC client example.
This commit is contained in:
parent
bb896a8222
commit
fa2f76bcdb
10 changed files with 327 additions and 4 deletions
|
@ -88,6 +88,8 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
A clear working example of the Dex gRPC client can be found [here][../examples/grpc-client/README.md].
|
||||||
|
|
||||||
## Authentication and access control
|
## Authentication and access control
|
||||||
|
|
||||||
The dex API does not provide any authentication or authorization beyond TLS client auth.
|
The dex API does not provide any authentication or authorization beyond TLS client auth.
|
||||||
|
|
5
Makefile
5
Makefile
|
@ -23,7 +23,7 @@ export GO15VENDOREXPERIMENT=1
|
||||||
|
|
||||||
LD_FLAGS="-w -X $(REPO_PATH)/version.Version=$(VERSION)"
|
LD_FLAGS="-w -X $(REPO_PATH)/version.Version=$(VERSION)"
|
||||||
|
|
||||||
build: bin/dex bin/example-app
|
build: bin/dex bin/example-app bin/grpc-client
|
||||||
|
|
||||||
bin/dex: check-go-version
|
bin/dex: check-go-version
|
||||||
@go install -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/dex
|
@go install -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/dex
|
||||||
|
@ -31,6 +31,9 @@ bin/dex: check-go-version
|
||||||
bin/example-app: check-go-version
|
bin/example-app: check-go-version
|
||||||
@go install -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/example-app
|
@go install -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/example-app
|
||||||
|
|
||||||
|
bin/grpc-client: check-go-version
|
||||||
|
@go install -v -ldflags $(LD_FLAGS) $(REPO_PATH)/examples/grpc-client
|
||||||
|
|
||||||
.PHONY: release-binary
|
.PHONY: release-binary
|
||||||
release-binary:
|
release-binary:
|
||||||
@go build -o _output/bin/dex -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/dex
|
@go build -o _output/bin/dex -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/dex
|
||||||
|
|
|
@ -24,8 +24,8 @@ web:
|
||||||
# from the HTTP endpoints.
|
# from the HTTP endpoints.
|
||||||
# grpc:
|
# grpc:
|
||||||
# addr: 127.0.0.1:5557
|
# addr: 127.0.0.1:5557
|
||||||
# tlsCert: /etc/dex/grpc.crt
|
# tlsCert: examples/grpc-client/server.crt
|
||||||
# tlsKey: /etc/dex/grpc.key
|
# tlsKey: examples/grpc-client/server.key
|
||||||
# tlsClientCA: /etc/dex/client.crt
|
# tlsClientCA: /etc/dex/client.crt
|
||||||
|
|
||||||
# Uncomment this block to enable configuration for the expiration time durations.
|
# Uncomment this block to enable configuration for the expiration time durations.
|
||||||
|
|
5
examples/grpc-client/.gitignore
vendored
Normal file
5
examples/grpc-client/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
*.key
|
||||||
|
*.crt
|
||||||
|
*.csr
|
||||||
|
index.*
|
||||||
|
serial*
|
62
examples/grpc-client/README.md
Normal file
62
examples/grpc-client/README.md
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
# Running a Dex gRPC client
|
||||||
|
|
||||||
|
Using gRPC, a client application can directly call methods on a server application as if it was a local object. The schema for Dex's gRPC API calls is defined in [`api/api.proto`][api-proto]. [`client.go`][client] is an example client program that makes a bunch of API calls to the dex server. For further details on the Dex API refer [`Documentation/api.md`][api-docs].
|
||||||
|
|
||||||
|
## Generating Credentials
|
||||||
|
|
||||||
|
Before running the client or the server, TLS credentials have to be setup for secure communication. Run the `cred-gen` script to create TLS credentials for running this example. This script generates a `ca.crt`, `server.crt`, `server.key`, `client.crt`, and `client.key`.
|
||||||
|
|
||||||
|
```
|
||||||
|
# Used to set certificate subject alt names.
|
||||||
|
export SAN=IP.1:127.0.0.1
|
||||||
|
|
||||||
|
# Run the script
|
||||||
|
./examples/grpc-client/cert-gen
|
||||||
|
```
|
||||||
|
To verify that the server and client certificates were signed by the CA, run the following commands:
|
||||||
|
|
||||||
|
```
|
||||||
|
openssl verify -CAfile ca.crt server.crt
|
||||||
|
openssl verify -CAfile ca.crt client.crt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Dex server
|
||||||
|
|
||||||
|
To expose the gRPC service, the gRPC option must be enabled via the dex config file as shown below.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Enables the gRPC API.
|
||||||
|
grpc:
|
||||||
|
addr: 127.0.0.1:5557
|
||||||
|
tlsCert: server.crt
|
||||||
|
tlsKey: server.key
|
||||||
|
|
||||||
|
```
|
||||||
|
Start an instance of the dex server with an in-memory data store:
|
||||||
|
|
||||||
|
```
|
||||||
|
./bin/dex serve examples/grpc-client/config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Dex client
|
||||||
|
|
||||||
|
Finally run the Dex client providing the CA certificate, client certificate and client key as arguments.
|
||||||
|
|
||||||
|
```
|
||||||
|
./bin/grpc-client -ca-crt=ca.crt -client-crt=client.crt -client-key=client.key
|
||||||
|
```
|
||||||
|
Running the gRPC client will cause the following API calls to be made to the server
|
||||||
|
1. CreatePassword
|
||||||
|
2. ListPasswords
|
||||||
|
3. DeletePassword
|
||||||
|
|
||||||
|
## Cleaning up
|
||||||
|
|
||||||
|
Run the following command to destroy all the credentials files that were created by the `cert-gen` script:
|
||||||
|
|
||||||
|
```
|
||||||
|
./examples/grpc-client/cert-destory
|
||||||
|
```
|
||||||
|
[api-proto]: ../../api/api.proto
|
||||||
|
[client]: client.go
|
||||||
|
[api-docs]: ../../Documentation/api.md
|
4
examples/grpc-client/cert-destroy
Executable file
4
examples/grpc-client/cert-destroy
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
rm -f ca.key ca.crt server.key server.csr server.crt client.key client.csr client.crt index.* serial*
|
||||||
|
rm -rf certs crl newcerts
|
35
examples/grpc-client/cert-gen
Executable file
35
examples/grpc-client/cert-gen
Executable file
|
@ -0,0 +1,35 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ -z $SAN ]
|
||||||
|
then echo "Set SAN with a DNS or IP(e.g. export SAN=IP.1:127.0.0.1,IP.2:172.18.0.2)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Creating CA, server cert/key, and client cert/key..."
|
||||||
|
|
||||||
|
# Creating basic files/directories
|
||||||
|
mkdir -p {certs,crl,newcerts}
|
||||||
|
touch index.txt
|
||||||
|
echo 1000 > serial
|
||||||
|
|
||||||
|
# CA private key (unencrypted)
|
||||||
|
openssl genrsa -out ca.key 4096
|
||||||
|
# Certificate Authority (self-signed certificate)
|
||||||
|
openssl req -config examples/grpc-client/openssl.conf -new -x509 -days 3650 -sha256 -key ca.key -extensions v3_ca -out ca.crt -subj "/CN=fake-ca"
|
||||||
|
|
||||||
|
# Server private key (unencrypted)
|
||||||
|
openssl genrsa -out server.key 2048
|
||||||
|
# Server certificate signing request (CSR)
|
||||||
|
openssl req -config examples/grpc-client/openssl.conf -new -sha256 -key server.key -out server.csr -subj "/CN=fake-server"
|
||||||
|
# Certificate Authority signs CSR to grant a certificate
|
||||||
|
openssl ca -batch -config examples/grpc-client/openssl.conf -extensions server_cert -days 365 -notext -md sha256 -in server.csr -out server.crt -cert ca.crt -keyfile ca.key
|
||||||
|
|
||||||
|
# Client private key (unencrypted)
|
||||||
|
openssl genrsa -out client.key 2048
|
||||||
|
# Signed client certificate signing request (CSR)
|
||||||
|
openssl req -config examples/grpc-client/openssl.conf -new -sha256 -key client.key -out client.csr -subj "/CN=fake-client"
|
||||||
|
# Certificate Authority signs CSR to grant a certificate
|
||||||
|
openssl ca -batch -config examples/grpc-client/openssl.conf -extensions usr_cert -days 365 -notext -md sha256 -in client.csr -out client.crt -cert ca.crt -keyfile ca.key
|
||||||
|
|
||||||
|
# Remove CSR's
|
||||||
|
rm *.csr
|
106
examples/grpc-client/client.go
Normal file
106
examples/grpc-client/client.go
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/coreos/dex/api"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newDexClient(hostAndPort, caPath, clientCrt, clientKey string) (api.DexClient, error) {
|
||||||
|
cPool := x509.NewCertPool()
|
||||||
|
caCert, err := ioutil.ReadFile(caPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid CA crt file: %s", caPath)
|
||||||
|
}
|
||||||
|
if cPool.AppendCertsFromPEM(caCert) != true {
|
||||||
|
return nil, fmt.Errorf("failed to parse CA crt")
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCert, err := tls.LoadX509KeyPair(clientCrt, clientKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid client crt file: %s", caPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientTLSConfig := &tls.Config{
|
||||||
|
RootCAs: cPool,
|
||||||
|
Certificates: []tls.Certificate{clientCert},
|
||||||
|
}
|
||||||
|
creds := credentials.NewTLS(clientTLSConfig)
|
||||||
|
|
||||||
|
conn, err := grpc.Dial(hostAndPort, grpc.WithTransportCredentials(creds))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("dail: %v", err)
|
||||||
|
}
|
||||||
|
return api.NewDexClient(conn), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
caCrt := flag.String("ca-crt", "", "CA certificate")
|
||||||
|
clientCrt := flag.String("client-crt", "", "Client certificate")
|
||||||
|
clientKey := flag.String("client-key", "", "Client key")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *clientCrt == "" || *caCrt == "" || *clientKey == "" {
|
||||||
|
log.Fatal("Please provide CA & client certificates and client key. Usage: ./client -ca_crt=<path ca.crt> -client_crt=<path client.crt> -client_key=<path client key>")
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := newDexClient("127.0.0.1:5557", *caCrt, *clientCrt, *clientKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed creating dex client: %v ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := api.Password{
|
||||||
|
Email: "test@example.com",
|
||||||
|
// bcrypt hash of the value "test1" with cost 10
|
||||||
|
Hash: []byte("$2a$10$XVMN/Fid.Ks4CXgzo8fpR.iU1khOMsP5g9xQeXuBm1wXjRX8pjUtO"),
|
||||||
|
Username: "test",
|
||||||
|
UserId: "test",
|
||||||
|
}
|
||||||
|
|
||||||
|
createReq := &api.CreatePasswordReq{
|
||||||
|
Password: &p,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create password.
|
||||||
|
if resp, err := client.CreatePassword(context.TODO(), createReq); err != nil || resp.AlreadyExists {
|
||||||
|
if resp.AlreadyExists {
|
||||||
|
log.Fatalf("Password %s already exists", createReq.Password.Email)
|
||||||
|
}
|
||||||
|
log.Fatalf("failed to create password: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("Created password with email %s", createReq.Password.Email)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all passwords.
|
||||||
|
resp, err := client.ListPasswords(context.TODO(), &api.ListPasswordReq{})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to list password: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Print("Listing Passwords:\n")
|
||||||
|
for _, pass := range resp.Passwords {
|
||||||
|
log.Printf("%+v", pass)
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteReq := &api.DeletePasswordReq{
|
||||||
|
Email: p.Email,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete password with email = test@example.com.
|
||||||
|
if resp, err := client.DeletePassword(context.TODO(), deleteReq); err != nil || resp.NotFound {
|
||||||
|
if resp.NotFound {
|
||||||
|
log.Fatalf("Password %s not found", deleteReq.Email)
|
||||||
|
}
|
||||||
|
log.Fatalf("failed to delete password: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("Deleted password with email %s", deleteReq.Email)
|
||||||
|
}
|
||||||
|
}
|
24
examples/grpc-client/config.yaml
Normal file
24
examples/grpc-client/config.yaml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
issuer: http://127.0.0.1:5556/dex
|
||||||
|
|
||||||
|
storage:
|
||||||
|
type: sqlite3
|
||||||
|
config:
|
||||||
|
file: examples/dex.db
|
||||||
|
|
||||||
|
# Configuration for the HTTP endpoints.
|
||||||
|
web:
|
||||||
|
http: 0.0.0.0:5556
|
||||||
|
|
||||||
|
grpc:
|
||||||
|
addr: 127.0.0.1:5557
|
||||||
|
tlsCert: server.crt
|
||||||
|
tlsKey: server.key
|
||||||
|
|
||||||
|
connectors:
|
||||||
|
- type: mockCallback
|
||||||
|
id: mock
|
||||||
|
name: Example
|
||||||
|
|
||||||
|
# Let dex keep a list of passwords which can be used to login to dex.
|
||||||
|
enablePasswordDB: true
|
||||||
|
|
82
examples/grpc-client/openssl.conf
Normal file
82
examples/grpc-client/openssl.conf
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
# OpenSSL configuration file.
|
||||||
|
# Adapted from https://github.com/coreos/matchbox/blob/master/examples/etc/matchbox/openssl.conf
|
||||||
|
|
||||||
|
# default environment variable values
|
||||||
|
SAN =
|
||||||
|
|
||||||
|
[ ca ]
|
||||||
|
# `man ca`
|
||||||
|
default_ca = CA_default
|
||||||
|
|
||||||
|
[ CA_default ]
|
||||||
|
# Directory and file locations.
|
||||||
|
dir = .
|
||||||
|
certs = $dir/certs
|
||||||
|
crl_dir = $dir/crl
|
||||||
|
new_certs_dir = $dir/newcerts
|
||||||
|
database = $dir/index.txt
|
||||||
|
serial = $dir/serial
|
||||||
|
# certificate revocation lists.
|
||||||
|
crlnumber = $dir/crlnumber
|
||||||
|
crl = $dir/crl/intermediate-ca.crl
|
||||||
|
crl_extensions = crl_ext
|
||||||
|
default_crl_days = 30
|
||||||
|
default_md = sha256
|
||||||
|
|
||||||
|
name_opt = ca_default
|
||||||
|
cert_opt = ca_default
|
||||||
|
default_days = 375
|
||||||
|
preserve = no
|
||||||
|
policy = policy_loose
|
||||||
|
|
||||||
|
[ policy_loose ]
|
||||||
|
# Allow the CA to sign a range of certificates.
|
||||||
|
countryName = optional
|
||||||
|
stateOrProvinceName = optional
|
||||||
|
localityName = optional
|
||||||
|
organizationName = optional
|
||||||
|
organizationalUnitName = optional
|
||||||
|
commonName = supplied
|
||||||
|
emailAddress = optional
|
||||||
|
|
||||||
|
[ req ]
|
||||||
|
# `man req`
|
||||||
|
default_bits = 4096
|
||||||
|
distinguished_name = req_distinguished_name
|
||||||
|
string_mask = utf8only
|
||||||
|
default_md = sha256
|
||||||
|
|
||||||
|
[ req_distinguished_name ]
|
||||||
|
countryName = Country Name (2 letter code)
|
||||||
|
stateOrProvinceName = State or Province Name
|
||||||
|
localityName = Locality Name
|
||||||
|
0.organizationName = Organization Name
|
||||||
|
organizationalUnitName = Organizational Unit Name
|
||||||
|
commonName = Common Name
|
||||||
|
|
||||||
|
# Certificate extensions (`man x509v3_config`)
|
||||||
|
|
||||||
|
[ v3_ca ]
|
||||||
|
subjectKeyIdentifier = hash
|
||||||
|
authorityKeyIdentifier = keyid:always,issuer
|
||||||
|
basicConstraints = critical, CA:true, pathlen:0
|
||||||
|
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
|
||||||
|
|
||||||
|
[ usr_cert ]
|
||||||
|
basicConstraints = CA:FALSE
|
||||||
|
nsCertType = client
|
||||||
|
nsComment = "OpenSSL Generated Client Certificate"
|
||||||
|
subjectKeyIdentifier = hash
|
||||||
|
authorityKeyIdentifier = keyid,issuer
|
||||||
|
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
|
||||||
|
extendedKeyUsage = clientAuth
|
||||||
|
|
||||||
|
[ server_cert ]
|
||||||
|
basicConstraints = CA:FALSE
|
||||||
|
nsCertType = server
|
||||||
|
nsComment = "OpenSSL Generated Server Certificate"
|
||||||
|
subjectKeyIdentifier = hash
|
||||||
|
authorityKeyIdentifier = keyid,issuer:always
|
||||||
|
keyUsage = critical, digitalSignature, keyEncipherment
|
||||||
|
extendedKeyUsage = serverAuth
|
||||||
|
subjectAltName = $ENV::SAN
|
Reference in a new issue