Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (

"k8s.io/apiserver/pkg/server/dynamiccertificates"

oscrypto "github.com/openshift/library-go/pkg/crypto"

"github.com/openshift/oauth-proxy/util"
)

Expand Down Expand Up @@ -72,14 +70,23 @@ func (s *Server) ServeHTTP() {
log.Printf("HTTP: closing %s", listener.Addr())
}

func (s *Server) ServeHTTPS(ctx context.Context) {
addr := s.Opts.HttpsAddress
func (s *Server) buildTLSConfig() *tls.Config {
config := &tls.Config{
MinVersion: s.Opts.tlsMinVersionValue,
NextProtos: []string{"http/1.1"},
}

config := oscrypto.SecureTLSConfig(&tls.Config{})
if config.NextProtos == nil {
config.NextProtos = []string{"http/1.1"}
if len(s.Opts.tlsCipherSuiteIDs) > 0 {
config.CipherSuites = s.Opts.tlsCipherSuiteIDs
}

return config
}

func (s *Server) ServeHTTPS(ctx context.Context) {
addr := s.Opts.HttpsAddress
config := s.buildTLSConfig()

var err error
servingCertProvider, err := dynamiccertificates.NewDynamicServingContentFromFiles("serving", s.Opts.TLSCertFile, s.Opts.TLSKeyFile)
if err != nil {
Expand Down
75 changes: 75 additions & 0 deletions http_test.go
Comment thread
coleenquadros marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package main

import (
"crypto/tls"
"testing"
)

func TestBuildTLSConfig_Defaults(t *testing.T) {
s := &Server{Opts: &Options{tlsMinVersionValue: tls.VersionTLS13}}
config := s.buildTLSConfig()

if config.MinVersion != tls.VersionTLS13 {
t.Errorf("Expected default MinVersion TLS 1.3 (%d), got %d", tls.VersionTLS13, config.MinVersion)
}
}

func TestBuildTLSConfig_MinVersion(t *testing.T) {
tests := []struct {
name string
version uint16
expected uint16
}{
{"TLS 1.2", tls.VersionTLS12, tls.VersionTLS12},
{"TLS 1.3", tls.VersionTLS13, tls.VersionTLS13},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &Server{Opts: &Options{tlsMinVersionValue: tt.version}}
config := s.buildTLSConfig()

if config.MinVersion != tt.expected {
t.Errorf("Expected MinVersion %d, got %d", tt.expected, config.MinVersion)
}
})
}
}

func TestBuildTLSConfig_CipherSuites(t *testing.T) {
tests := []struct {
name string
suiteIDs []uint16
expectCount int
}{
{
name: "single cipher",
suiteIDs: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
expectCount: 1,
},
{
name: "multiple ciphers",
suiteIDs: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
expectCount: 2,
},
{
name: "no ciphers uses default",
suiteIDs: nil,
expectCount: 0,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &Server{Opts: &Options{
tlsMinVersionValue: tls.VersionTLS13,
tlsCipherSuiteIDs: tt.suiteIDs,
}}
config := s.buildTLSConfig()

if len(config.CipherSuites) != tt.expectCount {
t.Errorf("Expected %d cipher suites, got %d", tt.expectCount, len(config.CipherSuites))
}
})
}
}
15 changes: 15 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func main() {
openshiftCAs := NewStringArray()
clientCA := ""
upstreamCAs := NewStringArray()
tlsCipherSuitesStr := ""

config := flagSet.String("config", "", "path to config file")
showVersion := flagSet.Bool("version", false, "print version string")
Expand All @@ -42,6 +43,8 @@ func main() {
flagSet.String("tls-cert", "", "path to certificate file")
flagSet.String("tls-key", "", "path to private key file")
flagSet.StringVar(&clientCA, "tls-client-ca", clientCA, "path to a CA file for admitting client certificates.")
flagSet.String("tls-min-version", "", "minimum TLS version (e.g., VersionTLS12, VersionTLS13). Defaults to TLS 1.3")
flagSet.StringVar(&tlsCipherSuitesStr, "tls-cipher-suites", "", "comma-separated list of TLS cipher suites")
flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth/callback\"")
flagSet.Bool("set-xauthrequest", false, "set X-Auth-Request-User and X-Auth-Request-Email response headers (useful in Nginx auth_request mode)")
flagSet.Var(upstreams, "upstream", "the http url(s) of the upstream endpoint or file:// paths for static files. Routing is based on the path")
Expand Down Expand Up @@ -131,6 +134,18 @@ func main() {
cfg.LoadEnvForStruct(opts)
options.Resolve(opts, flagSet, cfg)

// Parse comma-separated TLS cipher suites from CLI (overrides config file)
if tlsCipherSuitesStr != "" {
cipherSuites := strings.Split(tlsCipherSuitesStr, ",")
opts.TLSCipherSuites = make([]string, 0, len(cipherSuites))
for _, cipher := range cipherSuites {
trimmed := strings.TrimSpace(cipher)
if trimmed != "" {
opts.TLSCipherSuites = append(opts.TLSCipherSuites, trimmed)
}
}
}

var p providers.Provider
switch opts.Provider {
case "openshift":
Expand Down
63 changes: 57 additions & 6 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/http"
"net/url"
"regexp"
"sort"
"strings"
"time"

Expand Down Expand Up @@ -40,6 +41,8 @@ type Options struct {
TLSCertFile string `flag:"tls-cert" cfg:"tls_cert_file"`
TLSKeyFile string `flag:"tls-key" cfg:"tls_key_file"`
TLSClientCAFile string `flag:"tls-client-ca" cfg:"tls_client_ca"`
TLSMinVersion string `flag:"tls-min-version" cfg:"tls_min_version"`
TLSCipherSuites []string `cfg:"tls_cipher_suites"` // No flag tag - manually parsed after Resolve()

AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"`
EmailDomains []string `flag:"email-domain" cfg:"email_domains"`
Expand Down Expand Up @@ -111,12 +114,14 @@ type Options struct {
Timeout time.Duration `flag:"upstream-timeout" cfg:"upstream_timeout"`

// internal values that are set after config validation
redirectURL *url.URL
proxyURLs []*url.URL
CompiledAuthRegex []*regexp.Regexp
CompiledSkipRegex []*regexp.Regexp
provider providers.Provider
signatureData *SignatureData
redirectURL *url.URL
proxyURLs []*url.URL
CompiledAuthRegex []*regexp.Regexp
CompiledSkipRegex []*regexp.Regexp
provider providers.Provider
signatureData *SignatureData
tlsMinVersionValue uint16
tlsCipherSuiteIDs []uint16
}

type SignatureData struct {
Expand Down Expand Up @@ -310,6 +315,30 @@ func (o *Options) Validate(p providers.Provider) error {
msgs = append(msgs, "tls-client-ca requires tls-key-file or tls-cert-file to be set to listen on tls")
}

o.tlsMinVersionValue = tls.VersionTLS13
if o.TLSMinVersion != "" {
if v, ok := tlsVersionMap[o.TLSMinVersion]; ok {
o.tlsMinVersionValue = v
} else {
msgs = append(msgs, fmt.Sprintf("unrecognized tls-min-version %q; valid values: %s", o.TLSMinVersion, validTLSVersions()))
}
}

if len(o.TLSCipherSuites) > 0 {
var unrecognized []string
o.tlsCipherSuiteIDs = make([]uint16, 0, len(o.TLSCipherSuites))
for _, name := range o.TLSCipherSuites {
if id, ok := tlsCipherSuiteMap[name]; ok {
o.tlsCipherSuiteIDs = append(o.tlsCipherSuiteIDs, id)
} else {
unrecognized = append(unrecognized, name)
}
}
if len(unrecognized) > 0 {
msgs = append(msgs, fmt.Sprintf("unrecognized tls-cipher-suites: %s", strings.Join(unrecognized, ", ")))
}
}

switch o.CookieSameSite {
case "", "none", "lax", "strict":
default:
Expand Down Expand Up @@ -427,3 +456,25 @@ func secretBytes(secret string) []byte {
}
return []byte(secret)
}

var tlsVersionMap = map[string]uint16{
"VersionTLS12": tls.VersionTLS12,
"VersionTLS13": tls.VersionTLS13,
}

var tlsCipherSuiteMap = func() map[string]uint16 {
m := make(map[string]uint16)
for _, cs := range tls.CipherSuites() {
m[cs.Name] = cs.ID
}
return m
}()

func validTLSVersions() string {
versions := make([]string, 0, len(tlsVersionMap))
for k := range tlsVersionMap {
versions = append(versions, k)
}
sort.Strings(versions)
return strings.Join(versions, ", ")
}
38 changes: 38 additions & 0 deletions options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"crypto"
"crypto/tls"
"fmt"
"net/url"
"strings"
Expand Down Expand Up @@ -229,3 +230,40 @@ func TestValidateCookieSameSite(t *testing.T) {
})
}
}

func TestValidateTLSMinVersionDefault(t *testing.T) {
o := testOptions()
assert.Equal(t, nil, o.Validate(&testProvider{}))
assert.Equal(t, uint16(tls.VersionTLS13), o.tlsMinVersionValue)
}

func TestValidateTLSMinVersion(t *testing.T) {
o := testOptions()
o.TLSMinVersion = "VersionTLS12"
assert.Equal(t, nil, o.Validate(&testProvider{}))
assert.Equal(t, uint16(tls.VersionTLS12), o.tlsMinVersionValue)
}

func TestValidateTLSMinVersionInvalid(t *testing.T) {
o := testOptions()
o.TLSMinVersion = "VersionTLS99"
err := o.Validate(&testProvider{})
assert.NotEqual(t, nil, err)
assert.Equal(t, true, strings.Contains(err.Error(), "unrecognized tls-min-version"))
}

func TestValidateTLSCipherSuites(t *testing.T) {
o := testOptions()
o.TLSCipherSuites = []string{"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"}
assert.Equal(t, nil, o.Validate(&testProvider{}))
assert.Equal(t, 1, len(o.tlsCipherSuiteIDs))
assert.Equal(t, uint16(tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256), o.tlsCipherSuiteIDs[0])
}

func TestValidateTLSCipherSuitesInvalid(t *testing.T) {
o := testOptions()
o.TLSCipherSuites = []string{"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "BOGUS_CIPHER"}
err := o.Validate(&testProvider{})
assert.NotEqual(t, nil, err)
assert.Equal(t, true, strings.Contains(err.Error(), "BOGUS_CIPHER"))
}