From 004e966269b15183d97efeea46596d10c2ef315b Mon Sep 17 00:00:00 2001 From: Rohith Jayawardene Date: Fri, 18 Nov 2016 10:49:34 +0000 Subject: [PATCH] Debug Handler (#156) - adding the --enable-profiling to switch on profiling --- README.md | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++- cli.go | 13 +++++++-- doc.go | 2 ++ handlers.go | 38 ++++++++++++++++++++++++ middleware.go | 3 +- server.go | 8 ++++- 6 files changed, 138 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c68a244b..05796a4f 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Keycloak-proxy is a proxy service which at the risk of stating the obvious integrates with the [Keycloak](https://github.com/keycloak/keycloak) authentication service. Although technically the service has no dependency on Keycloak itself and would quite happily work with any OpenID provider. The service supports both access tokens in browser cookie or bearer tokens. ```shell +[jest@starfury keycloak-proxy]$ bin/keycloak-proxy help NAME: keycloak-proxy - is a proxy using the keycloak service for auth and authorization @@ -30,7 +31,7 @@ USAGE: keycloak-proxy [options] VERSION: - v1.2.7 (git+sha: fe9654c) + v1.2.8 (git+sha: d98a7fe-dirty) AUTHOR(S): Rohith @@ -50,6 +51,7 @@ GLOBAL OPTIONS: --revocation-url value the url for the revocation endpoint to revoke refresh token [$PROXY_REVOCATION_URL] --store-url value url for the storage subsystem, e.g redis://127.0.0.1:6379, file:///etc/tokens.file [$PROXY_STORE_URL] --upstream-url value the url for the upstream endpoint you wish to proxy to [$PROXY_UPSTREAM_URL] + --enable-login-handler this enables the login hanlder /oauth/login, by default this is disabled --upstream-keepalives enables or disables the keepalive connections for upstream endpoint --upstream-timeout value is the maximum amount of time a dial will wait for a connect to complete (default: 10s) --upstream-keepalive-timeout value specifies the keep-alive period for an active network connection (default: 10s) @@ -90,6 +92,7 @@ GLOBAL OPTIONS: --cors-exposes-headers value set the expose cors headers access control (Access-Control-Expose-Headers) --cors-max-age value the max age applied to cors headers (Access-Control-Max-Age) (default: 0s) --cors-credentials the credentials access control header (Access-Control-Allow-Credentials) + --enable-profiling switching on the golang profiling via pprof on /debug/pprof, /debug/pprof/heap etc --enable-security-filter enables the security filter handler --skip-token-verification TESTING ONLY; bypass token verification, only expiration and roles enforced --json-logging switch on json logging rather than text (defaults true) @@ -98,6 +101,82 @@ GLOBAL OPTIONS: --help, -h show help --version, -v print the version + + +NAME: + keycloak-proxy - is a proxy using the keycloak service for auth and authorization + +USAGE: + keycloak-proxy [options] + +VERSION: + v1.2.7 (git+sha: fe9654c) + +AUTHOR(S): + Rohith + +COMMANDS: + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --config value the path to the configuration file for the keycloak proxy [$PROXY_CONFIG_FILE] + --listen value the interface the service should be listening on [$PROXY_LISTEN] + --client-secret value the client secret used to authenticate to the oauth server (access_type: confidential) [$PROXY_CLIENT_SECRET] + --client-id value the client id used to authenticate to the oauth service [$PROXY_CLIENT_ID] + --discovery-url value the discovery url to retrieve the openid configuration [$PROXY_DISCOVERY_URL] + --scope value a variable list of scopes requested when authenticating the user + --token-validate-only validate the token and roles only, no required implement oauth + --redirection-url value redirection url for the oauth callback url (/oauth is added) [$PROXY_REDIRECTION_URL] + --revocation-url value the url for the revocation endpoint to revoke refresh token [$PROXY_REVOCATION_URL] + --store-url value url for the storage subsystem, e.g redis://127.0.0.1:6379, file:///etc/tokens.file [$PROXY_STORE_URL] + --upstream-url value the url for the upstream endpoint you wish to proxy to [$PROXY_UPSTREAM_URL] + --upstream-keepalives enables or disables the keepalive connections for upstream endpoint + --upstream-timeout value is the maximum amount of time a dial will wait for a connect to complete (default: 10s) + --upstream-keepalive-timeout value specifies the keep-alive period for an active network connection (default: 10s) + --enable-authorization-header adds the authorization header to the proxy request + --enable-refresh-tokens enables the handling of the refresh tokens + --secure-cookie enforces the cookie to be secure, default to true + --http-only-cookie enforces the cookie is in http only mode, default to false + --cookie-domain value a domain the access cookie is available to, defaults host header + --cookie-access-name value the name of the cookie use to hold the access token (default: "kc-access") + --cookie-refresh-name value the name of the cookie used to hold the encrypted refresh token (default: "kc-state") + --encryption-key value the encryption key used to encrpytion the session state + --no-redirects do not have back redirects when no authentication is present, 401 them + --hostname value a list of hostnames the service will respond to, defaults to all + --enable-metrics enable the prometheus metrics collector on /oauth/metrics + --localhost-only-metrics enforces the metrics page can only been requested from 127.0.0.1 + --enable-proxy-protocol whether to enable proxy protocol + --enable-forwarding enables the forwarding proxy mode, signing outbound request + --forwarding-username value the username to use when logging into the openid provider + --forwarding-password value the password to use when logging into the openid provider + --forwarding-domains value a list of domains which should be signed; everything else is relayed unsigned + --tls-cert value the path to a certificate file used for TLS + --tls-private-key value the path to the private key for TLS support + --tls-ca-certificate value the path to the ca certificate used for mutual TLS + --tls-ca-key value the path the ca private key, used by the forward signing proxy + --tls-client-certificate value the path to the client certificate, used to outbound connections in reverse and forwarding proxy modes + --skip-upstream-tls-verify whether to skip the verification of any upstream TLS (defaults to true) + --skip-openid-provider-tls-verify whether to skip the verification of any TLS communication with the openid provider (defaults to false) + --match-claims value keypair values for matching access token claims e.g. aud=myapp, iss=http://example.* + --add-claims value retrieve extra claims from the token and inject into headers, e.g given_name -> X-Auth-Given-Name + --resource value a list of resources 'uri=/admin|methods=GET,PUT|roles=role1,role2' + --headers value Add custom headers to the upstream request, key=value + --signin-page value a custom template displayed for signin + --forbidden-page value a custom template used for access forbidden + --tag value keypair's passed to the templates at render,e.g title='My Page' + --cors-origins value list of origins to add to the CORE origins control (Access-Control-Allow-Origin) + --cors-methods value the method permitted in the access control (Access-Control-Allow-Methods) + --cors-headers value a set of headers to add to the CORS access control (Access-Control-Allow-Headers) + --cors-exposes-headers value set the expose cors headers access control (Access-Control-Expose-Headers) + --cors-max-age value the max age applied to cors headers (Access-Control-Max-Age) (default: 0s) + --cors-credentials the credentials access control header (Access-Control-Allow-Credentials) + --enable-security-filter enables the security filter handler + --skip-token-verification TESTING ONLY; bypass token verification, only expiration and roles enforced + --json-logging switch on json logging rather than text (defaults true) + --log-requests switch on logging of all incoming requests (defaults true) + --verbose switch on debug / verbose logging + --help, -h show help + --version, -v print the version ``` #### **Building** diff --git a/cli.go b/cli.go index 1b10fa15..fc37123f 100644 --- a/cli.go +++ b/cli.go @@ -313,11 +313,15 @@ func getCLIOptions() []cli.Flag { Name: "cors-max-age", Usage: "the max age applied to cors headers (Access-Control-Max-Age)", }, - cli.BoolFlag{ + cli.BoolTFlag{ Name: "cors-credentials", Usage: "the credentials access control header (Access-Control-Allow-Credentials)", }, - cli.BoolFlag{ + cli.BoolTFlag{ + Name: "enable-profiling", + Usage: "switching on the golang profiling via pprof on /debug/pprof, /debug/pprof/heap etc", + }, + cli.BoolTFlag{ Name: "enable-security-filter", Usage: "enables the security filter handler", }, @@ -333,7 +337,7 @@ func getCLIOptions() []cli.Flag { Name: "log-requests", Usage: "switch on logging of all incoming requests (defaults true)", }, - cli.BoolFlag{ + cli.BoolTFlag{ Name: "verbose", Usage: "switch on debug / verbose logging", }, @@ -429,6 +433,9 @@ func parseCLIOptions(cx *cli.Context, config *Config) (err error) { if cx.IsSet("enable-login-handler") { config.EnableLoginHandler = cx.Bool("enable-login-handler") } + if cx.IsSet("enable-profiling") { + config.EnableProfiling = cx.Bool("enable-profiling") + } if cx.IsSet("enable-metrics") { config.EnableMetrics = cx.Bool("enable-metrics") } diff --git a/doc.go b/doc.go index 597ad706..ed1b904f 100644 --- a/doc.go +++ b/doc.go @@ -126,6 +126,8 @@ type Config struct { // Headers permits adding customs headers across the board Headers map[string]string `json:"headers" yaml:"headers"` + // EnableProfiling indicates if profiles is switched on + EnableProfiling bool `json:"enable-profiling" yaml:"enable-profiling"` // EnableMetrics indicates if the metrics is enabled EnableMetrics bool `json:"enable-metrics" yaml:"enable-metrics"` // EnableURIMetrics indicates we want to keep metrics on uri request times diff --git a/handlers.go b/handlers.go index b686af36..a9fd920f 100644 --- a/handlers.go +++ b/handlers.go @@ -23,6 +23,7 @@ import ( "io/ioutil" "net" "net/http" + "net/http/pprof" "net/url" "path" "strings" @@ -394,6 +395,43 @@ func (r *oauthProxy) healthHandler(cx *gin.Context) { cx.String(http.StatusOK, "OK\n") } +// debugHandler is responsible for providing the pprof +func (r *oauthProxy) debugHandler(cx *gin.Context) { + name := cx.Param("name") + switch cx.Request.Method { + case http.MethodGet: + switch name { + case "heap": + fallthrough + case "goroutine": + fallthrough + case "block": + fallthrough + case "threadcreate": + pprof.Handler(name).ServeHTTP(cx.Writer, cx.Request) + case "cmdline": + pprof.Cmdline(cx.Writer, cx.Request) + case "profile": + pprof.Profile(cx.Writer, cx.Request) + case "trace": + pprof.Trace(cx.Writer, cx.Request) + case "symbol": + pprof.Symbol(cx.Writer, cx.Request) + default: + cx.AbortWithStatus(http.StatusNotFound) + } + case http.MethodPost: + switch name { + case "symbol": + pprof.Symbol(cx.Writer, cx.Request) + default: + cx.AbortWithStatus(http.StatusNotFound) + } + } + + cx.Abort() +} + // metricsHandler forwards the request into the prometheus handler func (r *oauthProxy) metricsHandler(cx *gin.Context) { if r.config.LocalhostMetrics { diff --git a/middleware.go b/middleware.go index 82a68352..010f0e5f 100644 --- a/middleware.go +++ b/middleware.go @@ -77,6 +77,7 @@ func (r *oauthProxy) metricsMiddleware() gin.HandlerFunc { // entrypointMiddleware checks to see if the request requires authentication func (r *oauthProxy) entrypointMiddleware() gin.HandlerFunc { return func(cx *gin.Context) { + // step: we can skip the if strings.HasPrefix(cx.Request.URL.Path, oauthURL) { return } @@ -255,10 +256,8 @@ func (r *oauthProxy) authenticationMiddleware() gin.HandlerFunc { } }(user.token, rToken) } - // step: update the with the new access token user.token = token - // step: inject the user into the context cx.Set(userContextName, user) } diff --git a/server.go b/server.go index b23f278b..eb5d26bf 100644 --- a/server.go +++ b/server.go @@ -150,10 +150,16 @@ func (r *oauthProxy) createReverseProxy() error { return err } - //step: create the gin router + // step: create the gin router engine := gin.New() engine.Use(gin.Recovery()) + // step: is profiling enabled? + if r.config.EnableProfiling { + log.Warn("Enabling the debug profiling on /debug/pprof") + engine.Any("/debug/pprof/:name", r.debugHandler) + } + // step: are we logging the traffic? if r.config.LogRequests { engine.Use(r.loggingMiddleware())