Skip to content

Commit

Permalink
feat: extended TLS configuration (#259)
Browse files Browse the repository at this point in the history
Add ability to configure TLS parameters: cipher suites, curve preferences, HSTS.
  • Loading branch information
trutx authored and rojer committed Sep 23, 2019
1 parent d078771 commit 3fb13f1
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 13 deletions.
44 changes: 44 additions & 0 deletions auth_server/main.go
Expand Up @@ -25,6 +25,7 @@ import (
"net/http"
"os"
"os/signal"
"strconv"
"syscall"
"time"

Expand All @@ -43,6 +44,14 @@ type RestartableServer struct {
hs httpdown.Server
}

func stringToUint16(s string) uint16 {
v, err := strconv.ParseUint(s, 0, 16)
if err != nil {
glog.Exitf("Failed to convert %s to uint16", s)
}
return uint16(v)
}

func ServeOnce(c *server.Config, cf string, hd *httpdown.HTTP) (*server.AuthServer, httpdown.Server) {
glog.Infof("Config from %s (%d users, %d ACL static entries)", cf, len(c.Users), len(c.ACL))
as, err := server.NewAuthServer(c)
Expand All @@ -53,6 +62,41 @@ func ServeOnce(c *server.Config, cf string, hd *httpdown.HTTP) (*server.AuthServ
tlsConfig := &tls.Config{
PreferServerCipherSuites: true,
}
if c.Server.HSTS {
glog.Info("HTTP Strict Transport Security enabled")
}
if c.Server.TLSMinVersion != "" {
value, found := server.TLSVersionValues[c.Server.TLSMinVersion]
if !found {
value = stringToUint16(c.Server.TLSMinVersion)
}
tlsConfig.MinVersion = value
glog.Infof("TLS MinVersion: %s", c.Server.TLSMinVersion)
}
if c.Server.TLSCurvePreferences != nil {
var values []tls.CurveID
for _, s := range c.Server.TLSCurvePreferences {
value, found := server.TLSCurveIDValues[s]
if !found {
value = tls.CurveID(stringToUint16(s))
}
values = append(values, value)
}
tlsConfig.CurvePreferences = values
glog.Infof("TLS CurvePreferences: %s", c.Server.TLSCurvePreferences)
}
if c.Server.TLSCipherSuites != nil {
var values []uint16
for _, s := range c.Server.TLSCipherSuites {
value, found := server.TLSCipherSuitesValues[s]
if !found {
value = stringToUint16(s)
}
values = append(values, value)
}
tlsConfig.CipherSuites = values
glog.Infof("TLS CipherSuites: %s", c.Server.TLSCipherSuites)
}
if c.Server.CertFile != "" || c.Server.KeyFile != "" {
// Check for partial configuration.
if c.Server.CertFile == "" || c.Server.KeyFile == "" {
Expand Down
81 changes: 73 additions & 8 deletions auth_server/server/config.go
Expand Up @@ -50,13 +50,17 @@ type Config struct {
}

type ServerConfig struct {
ListenAddress string `yaml:"addr,omitempty"`
PathPrefix string `yaml:"path_prefix,omitempty"`
RealIPHeader string `yaml:"real_ip_header,omitempty"`
RealIPPos int `yaml:"real_ip_pos,omitempty"`
CertFile string `yaml:"certificate,omitempty"`
KeyFile string `yaml:"key,omitempty"`
LetsEncrypt LetsEncryptConfig `yaml:"letsencrypt,omitempty"`
ListenAddress string `yaml:"addr,omitempty"`
PathPrefix string `yaml:"path_prefix,omitempty"`
RealIPHeader string `yaml:"real_ip_header,omitempty"`
RealIPPos int `yaml:"real_ip_pos,omitempty"`
CertFile string `yaml:"certificate,omitempty"`
KeyFile string `yaml:"key,omitempty"`
HSTS bool `yaml:"hsts,omitempty"`
TLSMinVersion string `yaml:"tls_min_version,omitempty"`
TLSCurvePreferences []string `yaml:"tls_curve_preferences,omitempty"`
TLSCipherSuites []string `yaml:"tls_cipher_suites,omitempty"`
LetsEncrypt LetsEncryptConfig `yaml:"letsencrypt,omitempty"`

publicKey libtrust.PublicKey
privateKey libtrust.PrivateKey
Expand All @@ -78,14 +82,75 @@ type TokenConfig struct {
privateKey libtrust.PrivateKey
}

// TLSCipherSuitesValues maps CipherSuite names as strings to the actual values
// in the crypto/tls package
// Taken from https://golang.org/pkg/crypto/tls/#pkg-constants
var TLSCipherSuitesValues = map[string]uint16{
// TLS 1.0 - 1.2 cipher suites.
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
// TLS 1.3 cipher suites.
"TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256,
"TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384,
"TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256,
// TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator
// that the client is doing version fallback. See RFC 7507.
"TLS_FALLBACK_SCSV": tls.TLS_FALLBACK_SCSV,
}

// TLSVersionValues maps Version names as strings to the actual values in the
// crypto/tls package
// Taken from https://golang.org/pkg/crypto/tls/#pkg-constants
var TLSVersionValues = map[string]uint16{
"TLS10": tls.VersionTLS10,
"TLS11": tls.VersionTLS11,
"TLS12": tls.VersionTLS12,
"TLS13": tls.VersionTLS13,
// Deprecated: SSLv3 is cryptographically broken, and will be
// removed in Go 1.14. See golang.org/issue/32716.
"SSL30": tls.VersionSSL30,
}

// TLSCurveIDValues maps CurveID names as strings to the actual values in the
// crypto/tls package
// Taken from https://golang.org/pkg/crypto/tls/#CurveID
var TLSCurveIDValues = map[string]tls.CurveID{
"P256": tls.CurveP256,
"P384": tls.CurveP384,
"P521": tls.CurveP521,
"X25519": tls.X25519,
}

func validate(c *Config) error {
if c.Server.ListenAddress == "" {
return errors.New("server.addr is required")
}
if c.Server.PathPrefix != "" && !strings.HasPrefix(c.Server.PathPrefix, "/") {
return errors.New("server.path_prefix must be an absolute path")
}

if (c.Server.TLSMinVersion == "0x0304" || c.Server.TLSMinVersion == "TLS13") && c.Server.TLSCipherSuites != nil {
return errors.New("TLS 1.3 ciphersuites are not configurable")
}
if c.Token.Issuer == "" {
return errors.New("token.issuer is required")
}
Expand Down
3 changes: 3 additions & 0 deletions auth_server/server/server.go
Expand Up @@ -349,6 +349,9 @@ func (as *AuthServer) CreateToken(ar *authRequest, ares []authzResult) (string,
func (as *AuthServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
glog.V(3).Infof("Request: %+v", req)
path_prefix := as.config.Server.PathPrefix
if as.config.Server.HSTS {
rw.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
}
switch {
case req.URL.Path == path_prefix+"/":
as.doIndex(rw, req)
Expand Down
39 changes: 34 additions & 5 deletions examples/reference.yml
Expand Up @@ -22,6 +22,35 @@ server: # Server settings.
# Use specific certificate and key.
certificate: "/path/to/server.pem"
key: "/path/to/server.key"
#
# The following optional settings will fine tune TLS configuration to improve security.
# Leaving them unset should be just fine for most installations.
#
# Enable HTTP Strict Transport Security.
# hsts: true
#
# Set minimum TLS version.
# Values can be found at https://golang.org/pkg/crypto/tls/#pkg-constants
# Either the version name (i.e. TLS11) or its uint16 value can be specified.
# tls_min_version: TLS12
#
# List of TLS curve preferences.
# Values can be found at https://golang.org/pkg/crypto/tls/#CurveID
# Either CurveID names (i.e. P384) or uint16 values can be specified.
# tls_curve_preferences:
# - P521
# - 24
# - P256
#
# List of enabled TLS cipher suites.
# Values can be found at https://golang.org/pkg/crypto/tls/#pkg-constants
# Either CipherSuite names (i.e. TLS_RSA_WITH_RC4_128_SHA) or uint16 values can be specified.
# tls_cipher_suites:
# - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
# - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
# - 0xc014
# - 0xc00a

# Use LetsEncrypt (https://letsencrypt.org/) to automatically obtain and maintain a certificate.
# Note that this only applies to server TLS certificate, this certificate will not be used for tokens
letsencrypt:
Expand Down Expand Up @@ -102,10 +131,10 @@ github_auth:
# want to have sensitive information checked in.
# client_secret: "verysecret"
client_secret_file: "/path/to/client_secret.txt"
# Either token_db file for storing of server tokens.
# Either token_db file for storing of server tokens.
token_db: "/somewhere/to/put/github_tokens.ldb"
# or google cloud storage for storing of the sensitive information.
gcs_token_db:
# or google cloud storage for storing of the sensitive information.
gcs_token_db:
bucket: "tokenBucket"
client_secret_file: "/path/to/client_secret.json"
# How long to wait when talking to GitHub servers. Optional.
Expand All @@ -117,7 +146,7 @@ github_auth:
github_web_uri: "https://github.acme.com"
# The Github API URI in case you are using Github Enterprise.
# Includes the protocol, without trailing slash. - defaults to: https://api.github.com
github_api_uri: "https://github.acme.com/api/v3"
github_api_uri: "https://github.acme.com/api/v3"
# Set an URL to display in the `docker login` command when succesfully authenticated. Optional.
registry_url: localhost:5000

Expand All @@ -140,7 +169,7 @@ ldap_auth:
# specify them here. Plain text password is read from the file.
bind_dn:
bind_password_file:
# User query settings. ${account} is expanded from auth request
# User query settings. ${account} is expanded from auth request
base: o=example.com
filter: (&(uid=${account})(objectClass=person))
# Labels can be mapped from LDAP attributes
Expand Down

0 comments on commit 3fb13f1

Please sign in to comment.