Merge pull request #812 from rithujohn191/example-client
examples: adding a gRPC client example.
This commit is contained in:
		| @@ -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 | ||||
|  | ||||
| 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)" | ||||
|  | ||||
| build: bin/dex bin/example-app | ||||
| build: bin/dex bin/example-app bin/grpc-client | ||||
|  | ||||
| bin/dex: check-go-version | ||||
| 	@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 | ||||
| 	@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 | ||||
| release-binary: | ||||
| 	@go build -o _output/bin/dex -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/dex | ||||
|   | ||||
| @@ -24,9 +24,9 @@ web: | ||||
| # from the HTTP endpoints. | ||||
| # grpc: | ||||
| #   addr: 127.0.0.1:5557 | ||||
| #   tlsCert: /etc/dex/grpc.crt | ||||
| #   tlsKey: /etc/dex/grpc.key | ||||
| #   tlsClientCA: /etc/dex/client.crt | ||||
| #  tlsCert: examples/grpc-client/server.crt | ||||
| #  tlsKey: examples/grpc-client/server.key | ||||
| #  tlsClientCA: /etc/dex/client.crt | ||||
|  | ||||
| # Uncomment this block to enable configuration for the expiration time durations. | ||||
| # expiry: | ||||
|   | ||||
							
								
								
									
										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 New Issue
	
	Block a user