Skip to content

Commit

Permalink
[KEYCLOAK-9379] Health, metrics and profiling on separate port (#65)
Browse files Browse the repository at this point in the history
* [KEYCLOAK-9379] Health, metrics and profiling on separate port

Added option to serve health, metrics and profiling endpoints on separate port (with https or http)

    Added support for http scheme on health & metrics endpoint
    Added support for specific TLS config for health & metrics endpoint(including client cert for mutual TLS)
    Updated go-chi router dependency to actual repo (not link)
    Updated dependencies: replaced outdated repo location for go-chi/chi router
    Removed logging from admin handler

Signed-off-by: Frederic BIDON frederic@oneconcern.com

* Add basic tests for admin listener

* Add listener admin HTTPS test

* Add another Admin listener https test, fix copying of config during table tests

Co-authored-by: Frederic BIDON <frederic@oneconcern.com>
  • Loading branch information
p53 and fredbi authored Mar 19, 2021
1 parent 2e2dadd commit 981de5b
Show file tree
Hide file tree
Showing 11 changed files with 473 additions and 264 deletions.
29 changes: 28 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func newDefaultConfig() *Config {
if name, err := os.Hostname(); err == nil {
hostnames = append(hostnames, name)
}
hostnames = append(hostnames, []string{"localhost", "127.0.0.1"}...)
hostnames = append(hostnames, []string{"localhost", "127.0.0.1", "::1"}...)

return &Config{
AccessTokenDuration: time.Duration(720) * time.Hour,
Expand Down Expand Up @@ -89,6 +89,15 @@ func (r *Config) isValid() error {
if r.Listen == "" {
return errors.New("you have not specified the listening interface")
}
if r.ListenAdmin == r.Listen {
r.ListenAdmin = ""
}
if r.ListenAdminScheme == "" {
r.ListenAdminScheme = secureScheme
}
if r.ListenAdminScheme != secureScheme && r.ListenAdminScheme != unsecureScheme {
return errors.New("scheme for admin listener must be one of [http, https]")
}
if r.MaxIdleConns <= 0 {
return errors.New("max-idle-connections must be a number > 0")
}
Expand All @@ -101,21 +110,39 @@ func (r *Config) isValid() error {
if r.TLSCertificate != "" && r.TLSPrivateKey == "" {
return errors.New("you have not provided a private key")
}
if r.TLSAdminCertificate != "" && r.TLSAdminPrivateKey == "" {
return errors.New("you have not provided a private key for admin endpoint")
}
if r.TLSPrivateKey != "" && r.TLSCertificate == "" {
return errors.New("you have not provided a certificate file")
}
if r.TLSAdminPrivateKey != "" && r.TLSAdminCertificate == "" {
return errors.New("you have not provided a certificate file for admin endpoint")
}
if r.TLSCertificate != "" && !fileExists(r.TLSCertificate) {
return fmt.Errorf("the tls certificate %s does not exist", r.TLSCertificate)
}
if r.TLSAdminCertificate != "" && !fileExists(r.TLSAdminCertificate) {
return fmt.Errorf("the tls certificate %s does not exist for admin endpoint", r.TLSAdminCertificate)
}
if r.TLSPrivateKey != "" && !fileExists(r.TLSPrivateKey) {
return fmt.Errorf("the tls private key %s does not exist", r.TLSPrivateKey)
}
if r.TLSAdminPrivateKey != "" && !fileExists(r.TLSAdminPrivateKey) {
return fmt.Errorf("the tls private key %s does not exist for admin endpoint", r.TLSAdminPrivateKey)
}
if r.TLSCaCertificate != "" && !fileExists(r.TLSCaCertificate) {
return fmt.Errorf("the tls ca certificate file %s does not exist", r.TLSCaCertificate)
}
if r.TLSAdminCaCertificate != "" && !fileExists(r.TLSAdminCaCertificate) {
return fmt.Errorf("the tls ca certificate file %s does not exist for admin endpoint", r.TLSAdminCaCertificate)
}
if r.TLSClientCertificate != "" && !fileExists(r.TLSClientCertificate) {
return fmt.Errorf("the tls client certificate %s does not exist", r.TLSClientCertificate)
}
if r.TLSAdminClientCertificate != "" && !fileExists(r.TLSAdminClientCertificate) {
return fmt.Errorf("the tls client certificate %s does not exist for admin endpoint", r.TLSAdminClientCertificate)
}
if r.UseLetsEncrypt && r.LetsEncryptCacheDir == "" {
return fmt.Errorf("the letsencrypt cache dir has not been set")
}
Expand Down
23 changes: 18 additions & 5 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,14 @@ type Resource struct {
type Config struct {
// ConfigFile is the binding interface
ConfigFile string `json:"config" yaml:"config" usage:"path the a configuration file" env:"CONFIG_FILE"`
// Listen is the binding interface
Listen string `json:"listen" yaml:"listen" usage:"the interface the service should be listening on" env:"LISTEN"`
// Listen defines the binding interface for main listener, e.g. {address}:{port}. This is required and there is no default value.
Listen string `json:"listen" yaml:"listen" usage:"Defines the binding interface for main listener, e.g. {address}:{port}. This is required and there is no default value" env:"LISTEN"`
// ListenHTTP is the interface to bind the http only service on
ListenHTTP string `json:"listen-http" yaml:"listen-http" usage:"interface we should be listening" env:"LISTEN_HTTP"`
ListenHTTP string `json:"listen-http" yaml:"listen-http" usage:"interface we should be listening to for HTTP traffic" env:"LISTEN_HTTP"`
// ListenAdmin defines the interface to bind admin-only endpoint (live-status, debug, prometheus...). If not defined, this defaults to the main listener defined by Listen.
ListenAdmin string `json:"listen-admin" yaml:"listen-admin" usage:"defines the interface to bind admin-only endpoint (live-status, debug, prometheus...). If not defined, this defaults to the main listener defined by Listen" env:"LISTEN_ADMIN"`
// ListenAdminScheme defines the scheme admin endpoints are served with. If not defined, same as main listener.
ListenAdminScheme string `json:"listen-admin-scheme" yaml:"listen-admin-scheme" usage:"scheme to serve admin-only endpoint (http or https)." env:"LISTEN_ADMIN_SCHEME"`
// DiscoveryURL is the url for the keycloak server
DiscoveryURL string `json:"discovery-url" yaml:"discovery-url" usage:"discovery url to retrieve the openid configuration" env:"DISCOVERY_URL"`
// ClientID is the client id
Expand Down Expand Up @@ -236,7 +240,7 @@ type Config struct {
EnableFrameDeny bool `json:"filter-frame-deny" yaml:"filter-frame-deny" usage:"enable to the frame deny header"`
// ContentSecurityPolicy allows the Content-Security-Policy header value to be set with a custom value
ContentSecurityPolicy string `json:"content-security-policy" yaml:"content-security-policy" usage:"specify the content security policy"`
// LocalhostMetrics indicated the metrics can only be consume via localhost
// LocalhostMetrics indicates that metrics can only be consumed from localhost
LocalhostMetrics bool `json:"localhost-metrics" yaml:"localhost-metrics" usage:"enforces the metrics page can only been requested from 127.0.0.1"`
// EnableCompression enables gzip compression for response
EnableCompression bool `json:"enable-compression" yaml:"enable-compression" usage:"enable gzip compression for response"`
Expand Down Expand Up @@ -274,6 +278,15 @@ type Config struct {
// SkipUpstreamTLSVerify skips the verification of any upstream tls
SkipUpstreamTLSVerify bool `json:"skip-upstream-tls-verify" yaml:"skip-upstream-tls-verify" usage:"skip the verification of any upstream TLS"`

// TLSAdminCertificate is the location for a tls certificate for admin https endpoint. Defaults to TLSCertificate.
TLSAdminCertificate string `json:"tls-admin-cert" yaml:"tls-admin-cert" usage:"path to ths TLS certificate" env:"TLS_ADMIN_CERTIFICATE"`
// TLSAdminPrivateKey is the location of a tls private key for admin https endpoint. Default to TLSPrivateKey
TLSAdminPrivateKey string `json:"tls-admin-private-key" yaml:"tls-admin-private-key" usage:"path to the private key for TLS" env:"TLS_ADMIN_PRIVATE_KEY"`
// TLSCaCertificate is the CA certificate which the client cert must be signed
TLSAdminCaCertificate string `json:"tls-admin-ca-certificate" yaml:"tls-admin-ca-certificate" usage:"path to the ca certificate used for signing requests" env:"TLS_ADMIN_CA_CERTIFICATE"`
// TLSAdinClientCertificate is path to a client certificate to use for outbound connections
TLSAdminClientCertificate string `json:"tls-admin-client-certificate" yaml:"tls-admin-client-certificate" usage:"path to the client certificate for outbound connections in reverse and forwarding proxy modes" env:"TLS_ADMIN_CLIENT_CERTIFICATE"`

// CorsOrigins is a list of origins permitted
CorsOrigins []string `json:"cors-origins" yaml:"cors-origins" usage:"origins to add to the CORE origins control (Access-Control-Allow-Origin)"`
// CorsMethods is a set of access control methods
Expand All @@ -296,7 +309,7 @@ type Config struct {

// NoRedirects informs we should hand back a 401 not a redirect
NoRedirects bool `json:"no-redirects" yaml:"no-redirects" usage:"do not have back redirects when no authentication is present, 401 them"`
// SkipTokenVerification tells the service to skipp verifying the access token - for testing purposes
// SkipTokenVerification tells the service to skip verifying the access token - for testing purposes
SkipTokenVerification bool `json:"skip-token-verification" yaml:"skip-token-verification" usage:"TESTING ONLY; bypass token verification, only expiration and roles enforced"`
// according RFC issuer should not be checked on access token, this will be default true in future
SkipAccessTokenIssuerCheck bool `json:"skip-access-token-issuer-check" yaml:"skip-access-token-issuer-check" usage:"according RFC issuer should not be checked on access token, this will be default true in future"`
Expand Down
167 changes: 0 additions & 167 deletions e2e_test.go

This file was deleted.

1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/fsnotify/fsnotify v1.4.10-0.20200417215612-7f4cf4dd2b52
github.com/garyburd/redigo v1.6.0 // indirect
github.com/go-chi/chi v3.3.3+incompatible
github.com/go-resty/resty/v2 v2.5.0
github.com/gofrs/uuid v3.3.0+incompatible
github.com/oleiade/reflections v1.0.1
github.com/onsi/ginkgo v1.8.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-resty/resty v1.12.0 h1:L1P5qymrXL5H/doXe2pKUr1wxovAI5ilm2LdVLbwThc=
github.com/go-resty/resty/v2 v2.5.0 h1:WFb5bD49/85PO7WgAjZ+/TJQ+Ty1XOcWEfD1zIFCM1c=
github.com/go-resty/resty/v2 v2.5.0/go.mod h1:B88+xCTEwvfD94NOuE6GS1wMlnoKNY8eEiNizfNwOwA=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84=
Expand Down Expand Up @@ -355,6 +358,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
Expand Down
4 changes: 2 additions & 2 deletions middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ func (r *oauthProxy) checkClaim(user *userContext, claimName string, match *rege
return false
}

// admissionMiddleware is responsible checking the access token against the protected resource
// admissionMiddleware is responsible for checking the access token against the protected resource
func (r *oauthProxy) admissionMiddleware(resource *Resource) func(http.Handler) http.Handler {
claimMatches := make(map[string]*regexp.Regexp)
for k, v := range r.config.MatchClaims {
Expand Down Expand Up @@ -463,7 +463,7 @@ func (r *oauthProxy) responseHeaderMiddleware(headers map[string]string) func(ht
}
}

// identityHeadersMiddleware is responsible for add the authentication headers for the upstream
// identityHeadersMiddleware is responsible for adding the authentication headers to upstream
func (r *oauthProxy) identityHeadersMiddleware(custom []string) func(http.Handler) http.Handler {
customClaims := make(map[string]string)

Expand Down
Loading

0 comments on commit 981de5b

Please sign in to comment.