examples: adding a gRPC client example.

This commit is contained in:
rithu john 2017-02-20 08:45:32 -08:00
parent bb896a8222
commit fa2f76bcdb
10 changed files with 327 additions and 4 deletions

View file

@ -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.

View file

@ -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

View file

@ -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
View file

@ -0,0 +1,5 @@
*.key
*.crt
*.csr
index.*
serial*

View 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

View 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
View 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

View 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)
}
}

View 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

View 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