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 | ## 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 New Issue
	
	Block a user