From 3d457a8cdf8b2ac08e9e2f184a5c5d8f9829fcfd Mon Sep 17 00:00:00 2001 From: Joel Speed Date: Sun, 14 Feb 2021 18:47:15 +0000 Subject: [PATCH] Create server options struct and move legacy server options to legacyOptions --- pkg/apis/options/alpha_options.go | 16 +++++ pkg/apis/options/legacy_options.go | 60 ++++++++++++++++ pkg/apis/options/legacy_options_test.go | 93 +++++++++++++++++++++++++ pkg/apis/options/options.go | 16 +---- pkg/apis/options/server.go | 27 +++++++ 5 files changed, 199 insertions(+), 13 deletions(-) create mode 100644 pkg/apis/options/server.go diff --git a/pkg/apis/options/alpha_options.go b/pkg/apis/options/alpha_options.go index 6016bac0..c9a86310 100644 --- a/pkg/apis/options/alpha_options.go +++ b/pkg/apis/options/alpha_options.go @@ -28,6 +28,18 @@ type AlphaOptions struct { // Headers may source values from either the authenticated user's session // or from a static secret value. InjectResponseHeaders []Header `json:"injectResponseHeaders,omitempty"` + + // Server is used to configure the HTTP(S) server for the proxy application. + // You may choose to run both HTTP and HTTPS servers simultaneously. + // This can be done by setting the BindAddress and the SecureBindAddress simultaneously. + // To use the secure server you must configure a TLS certificate and key. + Server Server `json:"server,omitempty"` + + // MetricsServer is used to configure the HTTP(S) server for metrics. + // You may choose to run both HTTP and HTTPS servers simultaneously. + // This can be done by setting the BindAddress and the SecureBindAddress simultaneously. + // To use the secure server you must configure a TLS certificate and key. + MetricsServer Server `json:"metricsServer,omitempty"` } // MergeInto replaces alpha options in the Options struct with the values @@ -36,6 +48,8 @@ func (a *AlphaOptions) MergeInto(opts *Options) { opts.UpstreamServers = a.Upstreams opts.InjectRequestHeaders = a.InjectRequestHeaders opts.InjectResponseHeaders = a.InjectResponseHeaders + opts.Server = a.Server + opts.MetricsServer = a.MetricsServer } // ExtractFrom populates the fields in the AlphaOptions with the values from @@ -44,4 +58,6 @@ func (a *AlphaOptions) ExtractFrom(opts *Options) { a.Upstreams = opts.UpstreamServers a.InjectRequestHeaders = opts.InjectRequestHeaders a.InjectResponseHeaders = opts.InjectResponseHeaders + a.Server = opts.Server + a.MetricsServer = opts.MetricsServer } diff --git a/pkg/apis/options/legacy_options.go b/pkg/apis/options/legacy_options.go index d3fabd58..dbeb722c 100644 --- a/pkg/apis/options/legacy_options.go +++ b/pkg/apis/options/legacy_options.go @@ -18,6 +18,9 @@ type LegacyOptions struct { // Legacy options for injecting request/response headers LegacyHeaders LegacyHeaders `cfg:",squash"` + // Legacy options for the server address and TLS + LegacyServer LegacyServer `cfg:",squash"` + Options Options `cfg:",squash"` } @@ -35,6 +38,11 @@ func NewLegacyOptions() *LegacyOptions { SkipAuthStripHeaders: true, }, + LegacyServer: LegacyServer{ + HTTPAddress: "127.0.0.1:4180", + HTTPSAddress: ":443", + }, + Options: *NewOptions(), } } @@ -44,6 +52,7 @@ func NewLegacyFlagSet() *pflag.FlagSet { flagSet.AddFlagSet(legacyUpstreamsFlagSet()) flagSet.AddFlagSet(legacyHeadersFlagSet()) + flagSet.AddFlagSet(legacyServerFlagset()) return flagSet } @@ -56,6 +65,8 @@ func (l *LegacyOptions) ToOptions() (*Options, error) { l.Options.UpstreamServers = upstreams l.Options.InjectRequestHeaders, l.Options.InjectResponseHeaders = l.LegacyHeaders.convert() + l.Options.Server, l.Options.MetricsServer = l.LegacyServer.convert() + return &l.Options, nil } @@ -403,3 +414,52 @@ func getXAuthRequestAccessTokenHeader() Header { }, } } + +type LegacyServer struct { + MetricsAddress string `flag:"metrics-address" cfg:"metrics_address"` + HTTPAddress string `flag:"http-address" cfg:"http_address"` + HTTPSAddress string `flag:"https-address" cfg:"https_address"` + TLSCertFile string `flag:"tls-cert-file" cfg:"tls_cert_file"` + TLSKeyFile string `flag:"tls-key-file" cfg:"tls_key_file"` +} + +func legacyServerFlagset() *pflag.FlagSet { + flagSet := pflag.NewFlagSet("server", pflag.ExitOnError) + + flagSet.String("metrics-address", "", "the address /metrics will be served on (e.g. \":9100\")") + flagSet.String("http-address", "127.0.0.1:4180", "[http://]: or unix:// to listen on for HTTP clients") + flagSet.String("https-address", ":443", ": to listen on for HTTPS clients") + flagSet.String("tls-cert-file", "", "path to certificate file") + flagSet.String("tls-key-file", "", "path to private key file") + + return flagSet +} + +func (l LegacyServer) convert() (Server, Server) { + appServer := Server{ + BindAddress: l.HTTPAddress, + SecureBindAddress: l.HTTPSAddress, + } + if l.TLSKeyFile != "" || l.TLSCertFile != "" { + appServer.TLS = &TLS{ + Key: &SecretSource{ + FromFile: l.TLSKeyFile, + }, + Cert: &SecretSource{ + FromFile: l.TLSCertFile, + }, + } + // Preserve backwards compatibility, only run one server + appServer.BindAddress = "" + } else { + // Disable the HTTPS server if there's no certificates. + // This preserves backwards compatibility. + appServer.SecureBindAddress = "" + } + + metricsServer := Server{ + BindAddress: l.MetricsAddress, + } + + return appServer, metricsServer +} diff --git a/pkg/apis/options/legacy_options_test.go b/pkg/apis/options/legacy_options_test.go index dbac5793..9f397f6f 100644 --- a/pkg/apis/options/legacy_options_test.go +++ b/pkg/apis/options/legacy_options_test.go @@ -106,6 +106,10 @@ var _ = Describe("Legacy Options", func() { opts.InjectResponseHeaders = []Header{} + opts.Server = Server{ + BindAddress: "127.0.0.1:4180", + } + converted, err := legacyOpts.ToOptions() Expect(err).ToNot(HaveOccurred()) Expect(converted).To(Equal(opts)) @@ -759,4 +763,93 @@ var _ = Describe("Legacy Options", func() { }), ) }) + + Context("Legacy Servers", func() { + type legacyServersTableInput struct { + legacyServer LegacyServer + expectedAppServer Server + expectedMetricsServer Server + } + + const ( + insecureAddr = "127.0.0.1:8080" + insecureMetricsAddr = ":9090" + secureAddr = ":443" + secureMetricsAddr = ":9443" + crtPath = "tls.crt" + keyPath = "tls.key" + ) + + var tlsConfig = &TLS{ + Cert: &SecretSource{ + FromFile: crtPath, + }, + Key: &SecretSource{ + FromFile: keyPath, + }, + } + + DescribeTable("should convert to app and metrics servers", + func(in legacyServersTableInput) { + appServer, metricsServer := in.legacyServer.convert() + Expect(appServer).To(Equal(in.expectedAppServer)) + Expect(metricsServer).To(Equal(in.expectedMetricsServer)) + }, + Entry("with default options only starts app HTTP server", legacyServersTableInput{ + legacyServer: LegacyServer{ + HTTPAddress: insecureAddr, + HTTPSAddress: secureAddr, + }, + expectedAppServer: Server{ + BindAddress: insecureAddr, + }, + }), + Entry("with TLS options specified only starts app HTTPS server", legacyServersTableInput{ + legacyServer: LegacyServer{ + HTTPAddress: insecureAddr, + HTTPSAddress: secureAddr, + TLSKeyFile: keyPath, + TLSCertFile: crtPath, + }, + expectedAppServer: Server{ + SecureBindAddress: secureAddr, + TLS: tlsConfig, + }, + }), + Entry("with metrics HTTP and HTTPS addresses", legacyServersTableInput{ + legacyServer: LegacyServer{ + HTTPAddress: insecureAddr, + HTTPSAddress: secureAddr, + MetricsAddress: insecureMetricsAddr, + MetricsSecureAddress: secureMetricsAddr, + }, + expectedAppServer: Server{ + BindAddress: insecureAddr, + }, + expectedMetricsServer: Server{ + BindAddress: insecureMetricsAddr, + SecureBindAddress: secureMetricsAddr, + }, + }), + Entry("with metrics HTTPS and tls cert/key", legacyServersTableInput{ + legacyServer: LegacyServer{ + HTTPAddress: insecureAddr, + HTTPSAddress: secureAddr, + MetricsAddress: insecureMetricsAddr, + MetricsSecureAddress: secureMetricsAddr, + MetricsTLSKeyFile: keyPath, + MetricsTLSCertFile: crtPath, + }, + expectedAppServer: Server{ + BindAddress: insecureAddr, + }, + expectedMetricsServer: Server{ + BindAddress: insecureMetricsAddr, + SecureBindAddress: secureMetricsAddr, + TLS: tlsConfig, + }, + }), + ) + + }) }) diff --git a/pkg/apis/options/options.go b/pkg/apis/options/options.go index ef0d090f..34cb75d5 100644 --- a/pkg/apis/options/options.go +++ b/pkg/apis/options/options.go @@ -22,9 +22,6 @@ type Options struct { ProxyPrefix string `flag:"proxy-prefix" cfg:"proxy_prefix"` PingPath string `flag:"ping-path" cfg:"ping_path"` PingUserAgent string `flag:"ping-user-agent" cfg:"ping_user_agent"` - MetricsAddress string `flag:"metrics-address" cfg:"metrics_address"` - HTTPAddress string `flag:"http-address" cfg:"http_address"` - HTTPSAddress string `flag:"https-address" cfg:"https_address"` ReverseProxy bool `flag:"reverse-proxy" cfg:"reverse_proxy"` RealClientIPHeader string `flag:"real-client-ip-header" cfg:"real_client_ip_header"` TrustedIPs []string `flag:"trusted-ip" cfg:"trusted_ips"` @@ -33,8 +30,6 @@ type Options struct { ClientID string `flag:"client-id" cfg:"client_id"` ClientSecret string `flag:"client-secret" cfg:"client_secret"` ClientSecretFile string `flag:"client-secret-file" cfg:"client_secret_file"` - TLSCertFile string `flag:"tls-cert-file" cfg:"tls_cert_file"` - TLSKeyFile string `flag:"tls-key-file" cfg:"tls_key_file"` AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"` KeycloakGroups []string `flag:"keycloak-group" cfg:"keycloak_groups"` @@ -68,6 +63,9 @@ type Options struct { InjectRequestHeaders []Header `cfg:",internal"` InjectResponseHeaders []Header `cfg:",internal"` + Server Server `cfg:",internal"` + MetricsServer Server `cfg:",internal"` + SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"` SkipAuthRoutes []string `flag:"skip-auth-route" cfg:"skip_auth_routes"` SkipJwtBearerTokens bool `flag:"skip-jwt-bearer-tokens" cfg:"skip_jwt_bearer_tokens"` @@ -136,10 +134,7 @@ func NewOptions() *Options { return &Options{ ProxyPrefix: "/oauth2", ProviderType: "google", - MetricsAddress: "", PingPath: "/ping", - HTTPAddress: "127.0.0.1:4180", - HTTPSAddress: ":443", RealClientIPHeader: "X-Real-IP", ForceHTTPS: false, Cookie: cookieDefaults(), @@ -162,14 +157,10 @@ func NewOptions() *Options { func NewFlagSet() *pflag.FlagSet { flagSet := pflag.NewFlagSet("oauth2-proxy", pflag.ExitOnError) - flagSet.String("http-address", "127.0.0.1:4180", "[http://]: or unix:// to listen on for HTTP clients") - flagSet.String("https-address", ":443", ": to listen on for HTTPS clients") flagSet.Bool("reverse-proxy", false, "are we running behind a reverse proxy, controls whether headers like X-Real-Ip are accepted") flagSet.String("real-client-ip-header", "X-Real-IP", "Header used to determine the real IP of the client (one of: X-Forwarded-For, X-Real-IP, or X-ProxyUser-IP)") flagSet.StringSlice("trusted-ip", []string{}, "list of IPs or CIDR ranges to allow to bypass authentication. WARNING: trusting by IP has inherent security flaws, read the configuration documentation for more information.") flagSet.Bool("force-https", false, "force HTTPS redirect for HTTP requests") - flagSet.String("tls-cert-file", "", "path to certificate file") - flagSet.String("tls-key-file", "", "path to private key file") flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"") flagSet.StringSlice("skip-auth-regex", []string{}, "(DEPRECATED for --skip-auth-route) bypass authentication for requests path's that match (may be given multiple times)") flagSet.StringSlice("skip-auth-route", []string{}, "bypass authentication for requests that match the method & path. Format: method=path_regex OR path_regex alone for all methods") @@ -204,7 +195,6 @@ func NewFlagSet() *pflag.FlagSet { flagSet.String("proxy-prefix", "/oauth2", "the url root path that this proxy should be nested under (e.g. //sign_in)") flagSet.String("ping-path", "/ping", "the ping endpoint that can be used for basic health checks") flagSet.String("ping-user-agent", "", "special User-Agent that will be used for basic health checks") - flagSet.String("metrics-address", "", "the address /metrics will be served on (e.g. \":9100\")") flagSet.String("session-store-type", "cookie", "the session storage provider to use") flagSet.Bool("session-cookie-minimal", false, "strip OAuth tokens from cookie session stores if they aren't needed (cookie session store only)") flagSet.String("redis-connection-url", "", "URL of redis server for redis session storage (eg: redis://HOST[:PORT])") diff --git a/pkg/apis/options/server.go b/pkg/apis/options/server.go new file mode 100644 index 00000000..a100d9c5 --- /dev/null +++ b/pkg/apis/options/server.go @@ -0,0 +1,27 @@ +package options + +// Server represents the configuration for an HTTP(S) server +type Server struct { + // BindAddress is the the address on which to serve traffic. + // Leave blank or set to "-" to disable. + BindAddress string + + // SecureBindAddress is the the address on which to serve secure traffic. + // Leave blank or set to "-" to disable. + SecureBindAddress string + + // TLS contains the information for loading the certificate and key for the + // secure traffic. + TLS *TLS +} + +// TLS contains the information for loading a TLS certifcate and key. +type TLS struct { + // Key is the the TLS key data to use. + // Typically this will come from a file. + Key *SecretSource + + // Cert is the TLS certificate data to use. + // Typically this will come from a file. + Cert *SecretSource +}