From 4f5605bb6f14a062808e146bd4ac4c9fd86ff1c7 Mon Sep 17 00:00:00 2001 From: Daniel Beneyto Date: Tue, 17 Nov 2020 09:07:14 +0100 Subject: [PATCH 1/3] Add TLS suport to http server --- .defaults.yml | 5 +++ config/config.yml_example | 5 +++ main.go | 5 +++ pkg/cfg/cfg.go | 8 ++++- pkg/cfg/tls.go | 69 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 pkg/cfg/tls.go diff --git a/.defaults.yml b/.defaults.yml index 5355712d..468c1c07 100644 --- a/.defaults.yml +++ b/.defaults.yml @@ -15,6 +15,11 @@ vouch: # whiteList: # teamWhitelist: + tls: + # cert: + # key: + profile: intermediate + jwt: # secret: issuer: Vouch diff --git a/config/config.yml_example b/config/config.yml_example index 95d7e5fb..71282569 100644 --- a/config/config.yml_example +++ b/config/config.yml_example @@ -55,6 +55,11 @@ vouch: # - myOrg # - myOrg/myTeam + tls: + # cert: /path/to/signed_cert_plus_intermediates # Path to certificate file + # key: /path/to/private_key # Path to key file + profile: intermediate # TLS configuration profile (modern, intermediate, old, default) + jwt: # secret - VOUCH_JWT_SECRET # a random string used to cryptographically sign the jwt diff --git a/main.go b/main.go index 043f3c0c..d6430062 100644 --- a/main.go +++ b/main.go @@ -166,6 +166,11 @@ func main() { ErrorLog: log.New(&fwdToZapWriter{fastlog}, "", 0), } + if cfg.Cfg.TLS.Cert != "" || cfg.Cfg.TLS.Key != "" { + srv.TLSConfig = cfg.TLSConfig(cfg.Cfg.TLS.Profile) + logger.Fatal(srv.ListenAndServeTLS(cfg.Cfg.TLS.Cert, cfg.Cfg.TLS.Key)) + } + logger.Fatal(srv.ListenAndServe()) } diff --git a/pkg/cfg/cfg.go b/pkg/cfg/cfg.go index 3e8def28..43cef43f 100644 --- a/pkg/cfg/cfg.go +++ b/pkg/cfg/cfg.go @@ -43,7 +43,13 @@ type Config struct { TeamWhiteList []string `mapstructure:"teamWhitelist"` AllowAllUsers bool `mapstructure:"allowAllUsers"` PublicAccess bool `mapstructure:"publicAccess"` - JWT struct { + + TLS struct { + Cert string `mapstructure:"cert"` + Key string `mapstructure:"key"` + Profile string `mapstructure:"profile"` + } + JWT struct { MaxAge int `mapstructure:"maxAge"` // in minutes Issuer string `mapstructure:"issuer"` Secret string `mapstructure:"secret"` diff --git a/pkg/cfg/tls.go b/pkg/cfg/tls.go new file mode 100644 index 00000000..020bcfca --- /dev/null +++ b/pkg/cfg/tls.go @@ -0,0 +1,69 @@ +/* + +Copyright 2020 The Vouch Proxy Authors. +Use of this source code is governed by The MIT License (MIT) that +can be found in the LICENSE file. Software distributed under The +MIT License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES +OR CONDITIONS OF ANY KIND, either express or implied. + +*/ + +package cfg + +import ( + "crypto/tls" +) + +// TLSConfig config returns a *tls.Config with the specified profile (modern, intermediate, old, default) configuration. +func TLSConfig(profile string) *tls.Config { + c := &tls.Config{} + + // Source: https://ssl-config.mozilla.org/#server=go&version=1.14&config=modern&hsts=false&guideline=5.6 + switch profile { + case "modern": + c = &tls.Config{ + MinVersion: tls.VersionTLS13, + } + case "intermediate": + c = &tls.Config{ + MinVersion: tls.VersionTLS12, + CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, + PreferServerCipherSuites: true, + CipherSuites: []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + }, + } + case "old": + c = &tls.Config{ + MinVersion: tls.VersionTLS10, + PreferServerCipherSuites: true, + CipherSuites: []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + tls.TLS_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_RSA_WITH_AES_128_CBC_SHA256, + tls.TLS_RSA_WITH_AES_128_CBC_SHA, + tls.TLS_RSA_WITH_AES_256_CBC_SHA, + tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, + }, + } + } + + return c +} From fb89cfc6afc68b4ae5cc5b50b41d36bfe43da621 Mon Sep 17 00:00:00 2001 From: Daniel Beneyto Date: Wed, 18 Nov 2020 20:09:33 +0100 Subject: [PATCH 2/3] Implement suggestions --- README.md | 1 + config/config.yml_example | 7 ++++--- main.go | 14 ++++++++++---- pkg/cfg/cfg.go | 9 +++++++++ pkg/cfg/cfg_test.go | 25 +++++++++++++++++++++++++ pkg/cfg/tls_test.go | 37 +++++++++++++++++++++++++++++++++++++ 6 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 pkg/cfg/tls_test.go diff --git a/README.md b/README.md index 372291d1..244cd3f6 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,7 @@ The variable `VOUCH_CONFIG` can be used to set an alternate location for the con - [Kubernetes architecture post ingress](https://github.com/vouch/vouch-proxy/pull/263#issuecomment-628297832) - [set `HTTP_PROXY` to relay Vouch Proxy IdP requests through an outbound proxy server](https://github.com/vouch/vouch-proxy/issues/291) - [Reverse Proxy for Google Cloud Run Services](https://github.com/karthikv2k/oauth_reverse_proxy) +- [Enable native TLS in Vouch Proxy](https://github.com/vouch/vouch-proxy/pull/332#issue-522612010) Please do help us to expand this list. diff --git a/config/config.yml_example b/config/config.yml_example index 71282569..c714e674 100644 --- a/config/config.yml_example +++ b/config/config.yml_example @@ -56,9 +56,10 @@ vouch: # - myOrg/myTeam tls: - # cert: /path/to/signed_cert_plus_intermediates # Path to certificate file - # key: /path/to/private_key # Path to key file - profile: intermediate # TLS configuration profile (modern, intermediate, old, default) + # cert: /path/to/signed_cert_plus_intermediates # VOUCH_TLS_CERT + # key: /path/to/private_key # VOUCH_TLS_KEY + # profile - defines the TLS configuration profile (modern, intermediate, old, default) + profile: intermediate # VOUCH_TLS_PROFILE jwt: # secret - VOUCH_JWT_SECRET diff --git a/main.go b/main.go index d6430062..d6face4b 100644 --- a/main.go +++ b/main.go @@ -60,6 +60,10 @@ var ( logger *zap.SugaredLogger fastlog *zap.Logger help = flag.Bool("help", false, "show usage") + scheme = map[bool]string{ + false: "http", + true: "https", + } // doProfile = flag.Bool("profile", false, "run profiler at /debug/pprof") ) @@ -112,6 +116,7 @@ func main() { configure() var listen = cfg.Cfg.Listen + ":" + strconv.Itoa(cfg.Cfg.Port) checkTCPPortAvailable(listen) + tls := (cfg.Cfg.TLS.Cert != "" && cfg.Cfg.TLS.Key != "") logger.Infow("starting "+cfg.Branding.FullName, // "semver": semver, @@ -120,7 +125,8 @@ func main() { "buildhost", host, "branch", branch, "semver", semver, - "listen", listen, + "listen", scheme[tls]+"://"+listen, + "tls", tls, "oauth.provider", cfg.GenOAuth.Provider) muxR := mux.NewRouter() @@ -166,13 +172,13 @@ func main() { ErrorLog: log.New(&fwdToZapWriter{fastlog}, "", 0), } - if cfg.Cfg.TLS.Cert != "" || cfg.Cfg.TLS.Key != "" { + if tls { srv.TLSConfig = cfg.TLSConfig(cfg.Cfg.TLS.Profile) logger.Fatal(srv.ListenAndServeTLS(cfg.Cfg.TLS.Cert, cfg.Cfg.TLS.Key)) + } else { + logger.Fatal(srv.ListenAndServe()) } - logger.Fatal(srv.ListenAndServe()) - } func checkTCPPortAvailable(listen string) { diff --git a/pkg/cfg/cfg.go b/pkg/cfg/cfg.go index 43cef43f..fae2bf90 100644 --- a/pkg/cfg/cfg.go +++ b/pkg/cfg/cfg.go @@ -403,6 +403,15 @@ func basicTest() error { if Cfg.Cookie.MaxAge > Cfg.JWT.MaxAge { return fmt.Errorf("configuration error: Cookie maxAge (%d) cannot be larger than the JWT maxAge (%d)", Cfg.Cookie.MaxAge, Cfg.JWT.MaxAge) } + + // check tls config + if Cfg.TLS.Key != "" && Cfg.TLS.Cert == "" { + return fmt.Errorf("configuration error: TLS certificate file not provided but TLS key is set (%s)", Cfg.TLS.Key) + } + if Cfg.TLS.Cert != "" && Cfg.TLS.Key == "" { + return fmt.Errorf("configuration error: TLS key file not provided but TLS certificate is set (%s)", Cfg.TLS.Cert) + } + return nil } diff --git a/pkg/cfg/cfg_test.go b/pkg/cfg/cfg_test.go index d6dfb660..7cfe6cf0 100644 --- a/pkg/cfg/cfg_test.go +++ b/pkg/cfg/cfg_test.go @@ -54,6 +54,31 @@ func TestConfigEnvPrecedence(t *testing.T) { } +func TestConfigWithTLS(t *testing.T) { + tests := []struct { + name string + tlsKeyFile string + tlsCertFile string + wantErr bool + }{ + {"TLSConfigOK", "/path/to/key", "/path/to/cert", false}, + {"TLSConfigKONoCert", "/path/to/key", "", true}, + {"TLSConfigKONoKey", "", "/path/to/cert", true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Cleanup(cleanupEnv) + InitForTestPurposes() + Cfg.TLS.Cert = tt.tlsCertFile + Cfg.TLS.Key = tt.tlsKeyFile + err := ValidateConfiguration() + + if (err != nil) != tt.wantErr { + t.Errorf("error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} func TestSetGitHubDefaults(t *testing.T) { InitForTestPurposesWithProvider("github") assert.Equal(t, []string{"read:user"}, GenOAuth.Scopes) diff --git a/pkg/cfg/tls_test.go b/pkg/cfg/tls_test.go new file mode 100644 index 00000000..f2bf2ca7 --- /dev/null +++ b/pkg/cfg/tls_test.go @@ -0,0 +1,37 @@ +/* + +Copyright 2020 The Vouch Proxy Authors. +Use of this source code is governed by The MIT License (MIT) that +can be found in the LICENSE file. Software distributed under The +MIT License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES +OR CONDITIONS OF ANY KIND, either express or implied. + +*/ + +package cfg + +import ( + "crypto/tls" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTLSConfig(t *testing.T) { + tests := []struct { + name string + profile string + wantTLSMinVersion uint16 + }{ + {"TLSDefaultProfile", "", 0}, + {"TLSModernProfile", "modern", tls.VersionTLS13}, + {"TLSIntermediateProfile", "intermediate", tls.VersionTLS12}, + {"TLSOldProfile", "old", tls.VersionTLS10}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tlsConfig := TLSConfig(tt.profile) + assert.Equal(t, tt.wantTLSMinVersion, tlsConfig.MinVersion) + }) + } +} From 81f50c0f29a99ecba42ec3ca5abaf3c7c404f7fa Mon Sep 17 00:00:00 2001 From: Benjamin Foote Date: Mon, 7 Dec 2020 19:31:52 -0800 Subject: [PATCH 3/3] #297 create self signed certs --- .gitignore | 1 + do.sh | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/.gitignore b/.gitignore index 5ba43587..69943ee8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ config/secret !config/testing/* pkg/model/storage-test.db .vscode/* +certs/* coverage.out coverage.html.env_google .env* diff --git a/do.sh b/do.sh index 2137db2c..3159000d 100755 --- a/do.sh +++ b/do.sh @@ -318,6 +318,17 @@ gosec() { # segfault's without exec since it would just call this function infinitely :) exec gosec ./... } + +selfcert() { + # https://stackoverflow.com/questions/63588254/how-to-set-up-an-https-server-with-a-self-signed-certificate-in-golang + set -e + mkdir -p $SDIR/certs + # openssl genrsa -out $SDIR/certs/server.key 2048 + openssl ecparam -genkey -name secp384r1 -out $SDIR/certs/server.key + openssl req -new -x509 -sha256 -key $SDIR/certs/server.key -out $SDIR/certs/server.crt -days 3650 + echo -e "created self signed certs in '$SDIR/certs'\n" +} + usage() { cat <