Merge pull request #111 from timothy-spencer/jwtsigningkeyfile
added jwt-key-file option
This commit is contained in:
		
						commit
						46a0d7ca54
					
				|  | @ -2,6 +2,8 @@ | ||||||
| 
 | 
 | ||||||
| ## Changes since v3.2.0 | ## Changes since v3.2.0 | ||||||
| 
 | 
 | ||||||
|  | - [#111](https://github.com/pusher/oauth2_proxy/pull/111) Add option for telling where to find a login.gov JWT key file (@timothy-spencer) | ||||||
|  | 
 | ||||||
| # v3.2.0 | # v3.2.0 | ||||||
| 
 | 
 | ||||||
| ## Release highlights | ## Release highlights | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								Dockerfile
								
								
								
								
							
							
						
						
									
										10
									
								
								Dockerfile
								
								
								
								
							|  | @ -11,13 +11,19 @@ COPY . . | ||||||
| # Fetch dependencies | # Fetch dependencies | ||||||
| RUN dep ensure --vendor-only | RUN dep ensure --vendor-only | ||||||
| 
 | 
 | ||||||
| # Build binary | # Build binary and make sure there is at least an empty key file. | ||||||
| RUN ./configure && make build | #  This is useful for GCP App Engine custom runtime builds, because | ||||||
|  | #  you cannot use multiline variables in their app.yaml, so you have to | ||||||
|  | #  build the key into the container and then tell it where it is | ||||||
|  | #  by setting OAUTH2_PROXY_JWT_KEY_FILE=/etc/ssl/private/jwt_signing_key.pem | ||||||
|  | #  in app.yaml instead. | ||||||
|  | RUN ./configure && make build && touch jwt_signing_key.pem | ||||||
| 
 | 
 | ||||||
| # Copy binary to alpine | # Copy binary to alpine | ||||||
| FROM alpine:3.8 | FROM alpine:3.8 | ||||||
| COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt | ||||||
| COPY --from=builder /go/src/github.com/pusher/oauth2_proxy/oauth2_proxy /bin/oauth2_proxy | COPY --from=builder /go/src/github.com/pusher/oauth2_proxy/oauth2_proxy /bin/oauth2_proxy | ||||||
|  | COPY --from=builder /go/src/github.com/pusher/oauth2_proxy/jwt_signing_key.pem /etc/ssl/private/jwt_signing_key.pem | ||||||
| 
 | 
 | ||||||
| RUN addgroup -S -g 2000 oauth2proxy && adduser -S -u 2000 oauth2proxy -G oauth2proxy | RUN addgroup -S -g 2000 oauth2proxy && adduser -S -u 2000 oauth2proxy -G oauth2proxy | ||||||
| USER oauth2proxy | USER oauth2proxy | ||||||
|  |  | ||||||
|  | @ -11,13 +11,19 @@ COPY . . | ||||||
| # Fetch dependencies | # Fetch dependencies | ||||||
| RUN dep ensure --vendor-only | RUN dep ensure --vendor-only | ||||||
| 
 | 
 | ||||||
| # Build binary | # Build binary and make sure there is at least an empty key file. | ||||||
| RUN ./configure && GOARCH=arm64 make build | #  This is useful for GCP App Engine custom runtime builds, because | ||||||
|  | #  you cannot use multiline variables in their app.yaml, so you have to | ||||||
|  | #  build the key into the container and then tell it where it is | ||||||
|  | #  by setting OAUTH2_PROXY_JWT_KEY_FILE=/etc/ssl/private/jwt_signing_key.pem | ||||||
|  | #  in app.yaml instead. | ||||||
|  | RUN ./configure && GOARCH=arm64 make build && touch jwt_signing_key.pem | ||||||
| 
 | 
 | ||||||
| # Copy binary to alpine | # Copy binary to alpine | ||||||
| FROM arm64v8/alpine:3.8 | FROM arm64v8/alpine:3.8 | ||||||
| COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt | ||||||
| COPY --from=builder /go/src/github.com/pusher/oauth2_proxy/oauth2_proxy /bin/oauth2_proxy | COPY --from=builder /go/src/github.com/pusher/oauth2_proxy/oauth2_proxy /bin/oauth2_proxy | ||||||
|  | COPY --from=builder /go/src/github.com/pusher/oauth2_proxy/jwt_signing_key.pem /etc/ssl/private/jwt_signing_key.pem | ||||||
| 
 | 
 | ||||||
| RUN addgroup -S -g 2000 oauth2proxy && adduser -S -u 2000 oauth2proxy -G oauth2proxy | RUN addgroup -S -g 2000 oauth2proxy && adduser -S -u 2000 oauth2proxy -G oauth2proxy | ||||||
| USER oauth2proxy | USER oauth2proxy | ||||||
|  |  | ||||||
|  | @ -11,13 +11,19 @@ COPY . . | ||||||
| # Fetch dependencies | # Fetch dependencies | ||||||
| RUN dep ensure --vendor-only | RUN dep ensure --vendor-only | ||||||
| 
 | 
 | ||||||
| # Build binary | # Build binary and make sure there is at least an empty key file. | ||||||
| RUN ./configure && GOARCH=arm GOARM=6 make build | #  This is useful for GCP App Engine custom runtime builds, because | ||||||
|  | #  you cannot use multiline variables in their app.yaml, so you have to | ||||||
|  | #  build the key into the container and then tell it where it is | ||||||
|  | #  by setting OAUTH2_PROXY_JWT_KEY_FILE=/etc/ssl/private/jwt_signing_key.pem | ||||||
|  | #  in app.yaml instead. | ||||||
|  | RUN ./configure && GOARCH=arm GOARM=6 make build && touch jwt_signing_key.pem | ||||||
| 
 | 
 | ||||||
| # Copy binary to alpine | # Copy binary to alpine | ||||||
| FROM arm32v6/alpine:3.8 | FROM arm32v6/alpine:3.8 | ||||||
| COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt | ||||||
| COPY --from=builder /go/src/github.com/pusher/oauth2_proxy/oauth2_proxy /bin/oauth2_proxy | COPY --from=builder /go/src/github.com/pusher/oauth2_proxy/oauth2_proxy /bin/oauth2_proxy | ||||||
|  | COPY --from=builder /go/src/github.com/pusher/oauth2_proxy/jwt_signing_key.pem /etc/ssl/private/jwt_signing_key.pem | ||||||
| 
 | 
 | ||||||
| RUN addgroup -S -g 2000 oauth2proxy && adduser -S -u 2000 oauth2proxy -G oauth2proxy | RUN addgroup -S -g 2000 oauth2proxy && adduser -S -u 2000 oauth2proxy -G oauth2proxy | ||||||
| USER oauth2proxy | USER oauth2proxy | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								README.md
								
								
								
								
							
							
						
						
									
										13
									
								
								README.md
								
								
								
								
							|  | @ -216,6 +216,13 @@ Now start the proxy up with the following options: | ||||||
|   -jwt-key="${OAUTH2_PROXY_JWT_KEY}" |   -jwt-key="${OAUTH2_PROXY_JWT_KEY}" | ||||||
| ``` | ``` | ||||||
| You can also set all these options with environment variables, for use in cloud/docker environments. | You can also set all these options with environment variables, for use in cloud/docker environments. | ||||||
|  | One tricky thing that you may encounter is that some cloud environments will pass in environment | ||||||
|  | variables in a docker env-file, which does not allow multiline variables like a PEM file. | ||||||
|  | If you encounter this, then you can create a `jwt_signing_key.pem` file in the top level | ||||||
|  | directory of the repo which contains the key in PEM format and then do your docker build. | ||||||
|  | The docker build process will copy that file into your image which you can then access by | ||||||
|  | setting the `OAUTH2_PROXY_JWT_KEY_FILE=/etc/ssl/private/jwt_signing_key.pem` | ||||||
|  | environment variable, or by setting `-jwt-key-file=/etc/ssl/private/jwt_signing_key.pem` on the commandline. | ||||||
| 
 | 
 | ||||||
| Once it is running, you should be able to go to `http://localhost:4180/` in your browser, | Once it is running, you should be able to go to `http://localhost:4180/` in your browser, | ||||||
| get authenticated by the login.gov integration server, and then get proxied on to your | get authenticated by the login.gov integration server, and then get proxied on to your | ||||||
|  | @ -261,6 +268,7 @@ An example [oauth2_proxy.cfg](contrib/oauth2_proxy.cfg.example) config file is i | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| Usage of oauth2_proxy: | Usage of oauth2_proxy: | ||||||
|  |   -acr-values string:  optional, used by login.gov (default "http://idmanagement.gov/ns/assurance/loa/1") | ||||||
|   -approval-prompt string: OAuth approval_prompt (default "force") |   -approval-prompt string: OAuth approval_prompt (default "force") | ||||||
|   -authenticated-emails-file string: authenticate against emails via file (one per line) |   -authenticated-emails-file string: authenticate against emails via file (one per line) | ||||||
|   -azure-tenant string: go to a tenant-specific or common (tenant-independent) endpoint. (default "common") |   -azure-tenant string: go to a tenant-specific or common (tenant-independent) endpoint. (default "common") | ||||||
|  | @ -269,10 +277,10 @@ Usage of oauth2_proxy: | ||||||
|   -client-secret string: the OAuth Client Secret |   -client-secret string: the OAuth Client Secret | ||||||
|   -config string: path to config file |   -config string: path to config file | ||||||
|   -cookie-domain string: an optional cookie domain to force cookies to (ie: .yourcompany.com) |   -cookie-domain string: an optional cookie domain to force cookies to (ie: .yourcompany.com) | ||||||
|   -cookie-path string: an optional cookie path to force cookies to (ie: /foo) |  | ||||||
|   -cookie-expire duration: expire timeframe for cookie (default 168h0m0s) |   -cookie-expire duration: expire timeframe for cookie (default 168h0m0s) | ||||||
|   -cookie-httponly: set HttpOnly cookie flag (default true) |   -cookie-httponly: set HttpOnly cookie flag (default true) | ||||||
|   -cookie-name string: the name of the cookie that the oauth_proxy creates (default "_oauth2_proxy") |   -cookie-name string: the name of the cookie that the oauth_proxy creates (default "_oauth2_proxy") | ||||||
|  |   -cookie-path string: an optional cookie path to force cookies to (ie: /poc/)* (default "/") | ||||||
|   -cookie-refresh duration: refresh the cookie after this duration; 0 to disable |   -cookie-refresh duration: refresh the cookie after this duration; 0 to disable | ||||||
|   -cookie-secret string: the seed string for secure cookies (optionally base64 encoded) |   -cookie-secret string: the seed string for secure cookies (optionally base64 encoded) | ||||||
|   -cookie-secure: set secure (HTTPS) cookie flag (default true) |   -cookie-secure: set secure (HTTPS) cookie flag (default true) | ||||||
|  | @ -290,6 +298,8 @@ Usage of oauth2_proxy: | ||||||
|   -htpasswd-file string: additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -s" for SHA encryption |   -htpasswd-file string: additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -s" for SHA encryption | ||||||
|   -http-address string: [http://]<addr>:<port> or unix://<path> to listen on for HTTP clients (default "127.0.0.1:4180") |   -http-address string: [http://]<addr>:<port> or unix://<path> to listen on for HTTP clients (default "127.0.0.1:4180") | ||||||
|   -https-address string: <addr>:<port> to listen on for HTTPS clients (default ":443") |   -https-address string: <addr>:<port> to listen on for HTTPS clients (default ":443") | ||||||
|  |   -jwt-key string: private key in PEM format used to sign JWT, so that you can say something like -jwt-key="${OAUTH2_PROXY_JWT_KEY}": required by login.gov | ||||||
|  |   -jwt-key-file string: path to the private key file in PEM format used to sign the JWT so that you can say something like -jwt-key-file=/etc/ssl/private/jwt_signing_key.pem: required by login.gov | ||||||
|   -login-url string: Authentication endpoint |   -login-url string: Authentication endpoint | ||||||
|   -oidc-issuer-url: the OpenID Connect issuer URL. ie: "https://accounts.google.com" |   -oidc-issuer-url: the OpenID Connect issuer URL. ie: "https://accounts.google.com" | ||||||
|   -oidc-jwks-url string: OIDC JWKS URI for token verification; required if OIDC discovery is disabled |   -oidc-jwks-url string: OIDC JWKS URI for token verification; required if OIDC discovery is disabled | ||||||
|  | @ -302,6 +312,7 @@ Usage of oauth2_proxy: | ||||||
|   -provider string: OAuth provider (default "google") |   -provider string: OAuth provider (default "google") | ||||||
|   -proxy-prefix string: the url root path that this proxy should be nested under (e.g. /<oauth2>/sign_in) (default "/oauth2") |   -proxy-prefix string: the url root path that this proxy should be nested under (e.g. /<oauth2>/sign_in) (default "/oauth2") | ||||||
|   -proxy-websockets: enables WebSocket proxying (default true) |   -proxy-websockets: enables WebSocket proxying (default true) | ||||||
|  |   -pubjwk-url string: JWK pubkey access endpoint: required by login.gov | ||||||
|   -redeem-url string: Token redemption endpoint |   -redeem-url string: Token redemption endpoint | ||||||
|   -redirect-url string: the OAuth Redirect URL. ie: "https://internalapp.yourcompany.com/oauth2/callback" |   -redirect-url string: the OAuth Redirect URL. ie: "https://internalapp.yourcompany.com/oauth2/callback" | ||||||
|   -request-logging: Log requests to stdout (default true) |   -request-logging: Log requests to stdout (default true) | ||||||
|  |  | ||||||
							
								
								
									
										3
									
								
								main.go
								
								
								
								
							
							
						
						
									
										3
									
								
								main.go
								
								
								
								
							|  | @ -92,7 +92,8 @@ func main() { | ||||||
| 
 | 
 | ||||||
| 	flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)") | 	flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)") | ||||||
| 	flagSet.String("acr-values", "http://idmanagement.gov/ns/assurance/loa/1", "acr values string:  optional, used by login.gov") | 	flagSet.String("acr-values", "http://idmanagement.gov/ns/assurance/loa/1", "acr values string:  optional, used by login.gov") | ||||||
| 	flagSet.String("jwt-key", "", "private key used to sign JWT: required by login.gov") | 	flagSet.String("jwt-key", "", "private key in PEM format used to sign JWT, so that you can say something like -jwt-key=\"${OAUTH2_PROXY_JWT_KEY}\": required by login.gov") | ||||||
|  | 	flagSet.String("jwt-key-file", "", "path to the private key file in PEM format used to sign the JWT so that you can say something like -jwt-key-file=/etc/ssl/private/jwt_signing_key.pem: required by login.gov") | ||||||
| 	flagSet.String("pubjwk-url", "", "JWK pubkey access endpoint: required by login.gov") | 	flagSet.String("pubjwk-url", "", "JWK pubkey access endpoint: required by login.gov") | ||||||
| 	flagSet.Bool("gcp-healthchecks", false, "Enable GCP/GKE healthcheck endpoints") | 	flagSet.Bool("gcp-healthchecks", false, "Enable GCP/GKE healthcheck endpoints") | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										24
									
								
								options.go
								
								
								
								
							
							
						
						
									
										24
									
								
								options.go
								
								
								
								
							|  | @ -6,6 +6,7 @@ import ( | ||||||
| 	"crypto/tls" | 	"crypto/tls" | ||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"os" | 	"os" | ||||||
|  | @ -90,6 +91,7 @@ type Options struct { | ||||||
| 	SignatureKey    string `flag:"signature-key" cfg:"signature_key" env:"OAUTH2_PROXY_SIGNATURE_KEY"` | 	SignatureKey    string `flag:"signature-key" cfg:"signature_key" env:"OAUTH2_PROXY_SIGNATURE_KEY"` | ||||||
| 	AcrValues       string `flag:"acr-values" cfg:"acr_values" env:"OAUTH2_PROXY_ACR_VALUES"` | 	AcrValues       string `flag:"acr-values" cfg:"acr_values" env:"OAUTH2_PROXY_ACR_VALUES"` | ||||||
| 	JWTKey          string `flag:"jwt-key" cfg:"jwt_key" env:"OAUTH2_PROXY_JWT_KEY"` | 	JWTKey          string `flag:"jwt-key" cfg:"jwt_key" env:"OAUTH2_PROXY_JWT_KEY"` | ||||||
|  | 	JWTKeyFile      string `flag:"jwt-key-file" cfg:"jwt_key_file" env:"OAUTH2_PROXY_JWT_KEY_FILE"` | ||||||
| 	PubJWKURL       string `flag:"pubjwk-url" cfg:"pubjwk_url" env:"OAUTH2_PROXY_PUBJWK_URL"` | 	PubJWKURL       string `flag:"pubjwk-url" cfg:"pubjwk_url" env:"OAUTH2_PROXY_PUBJWK_URL"` | ||||||
| 	GCPHealthChecks bool   `flag:"gcp-healthchecks" cfg:"gcp_healthchecks" env:"OAUTH2_PROXY_GCP_HEALTHCHECKS"` | 	GCPHealthChecks bool   `flag:"gcp-healthchecks" cfg:"gcp_healthchecks" env:"OAUTH2_PROXY_GCP_HEALTHCHECKS"` | ||||||
| 
 | 
 | ||||||
|  | @ -328,15 +330,33 @@ func parseProviderInfo(o *Options, msgs []string) []string { | ||||||
| 	case *providers.LoginGovProvider: | 	case *providers.LoginGovProvider: | ||||||
| 		p.AcrValues = o.AcrValues | 		p.AcrValues = o.AcrValues | ||||||
| 		p.PubJWKURL, msgs = parseURL(o.PubJWKURL, "pubjwk", msgs) | 		p.PubJWKURL, msgs = parseURL(o.PubJWKURL, "pubjwk", msgs) | ||||||
| 		if o.JWTKey == "" { | 
 | ||||||
|  | 		// JWT key can be supplied via env variable or file in the filesystem, but not both.
 | ||||||
|  | 		switch { | ||||||
|  | 		case o.JWTKey != "" && o.JWTKeyFile != "": | ||||||
|  | 			msgs = append(msgs, "cannot set both jwt-key and jwt-key-file options") | ||||||
|  | 		case o.JWTKey == "" && o.JWTKeyFile == "": | ||||||
| 			msgs = append(msgs, "login.gov provider requires a private key for signing JWTs") | 			msgs = append(msgs, "login.gov provider requires a private key for signing JWTs") | ||||||
| 		} else { | 		case o.JWTKey != "": | ||||||
|  | 			// The JWT Key is in the commandline argument
 | ||||||
| 			signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(o.JWTKey)) | 			signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(o.JWTKey)) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				msgs = append(msgs, "could not parse RSA Private Key PEM") | 				msgs = append(msgs, "could not parse RSA Private Key PEM") | ||||||
| 			} else { | 			} else { | ||||||
| 				p.JWTKey = signKey | 				p.JWTKey = signKey | ||||||
| 			} | 			} | ||||||
|  | 		case o.JWTKeyFile != "": | ||||||
|  | 			// The JWT key is in the filesystem
 | ||||||
|  | 			keyData, err := ioutil.ReadFile(o.JWTKeyFile) | ||||||
|  | 			if err != nil { | ||||||
|  | 				msgs = append(msgs, "could not read key file: "+o.JWTKeyFile) | ||||||
|  | 			} | ||||||
|  | 			signKey, err := jwt.ParseRSAPrivateKeyFromPEM(keyData) | ||||||
|  | 			if err != nil { | ||||||
|  | 				msgs = append(msgs, "could not parse private key from PEM file:"+o.JWTKeyFile) | ||||||
|  | 			} else { | ||||||
|  | 				p.JWTKey = signKey | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return msgs | 	return msgs | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue