diff --git a/cmd/gangway/handlers.go b/cmd/gangway/handlers.go index 17e2aa7a4..a49bb7e01 100644 --- a/cmd/gangway/handlers.go +++ b/cmd/gangway/handlers.go @@ -40,17 +40,18 @@ const ( // userInfo stores information about an authenticated user type userInfo struct { - ClusterName string - Username string - KubeCfgUser string - IDToken string - RefreshToken string - ClientID string - ClientSecret string - IssuerURL string - APIServerURL string - ClusterCA string - HTTPPath string + ClusterName string + Username string + KubeCfgUser string + IDToken string + RefreshToken string + ClientID string + ClientSecret string + IssuerURL string + APIServerURL string + ClusterCA string + IdentityProviderCA string + HTTPPath string } // homeInfo is used to store dynamic properties on @@ -122,11 +123,12 @@ func generateKubeConfig(cfg *userInfo) clientcmdapi.Config { AuthProvider: &clientcmdapi.AuthProviderConfig{ Name: "oidc", Config: map[string]string{ - "client-id": cfg.ClientID, - "client-secret": cfg.ClientSecret, - "id-token": cfg.IDToken, - "idp-issuer-url": cfg.IssuerURL, - "refresh-token": cfg.RefreshToken, + "client-id": cfg.ClientID, + "client-secret": cfg.ClientSecret, + "id-token": cfg.IDToken, + "idp-issuer-url": cfg.IssuerURL, + "refresh-token": cfg.RefreshToken, + "idp-certificate-authority-data": cfg.IdentityProviderCA, }, }, }, @@ -300,6 +302,20 @@ func generateInfo(w http.ResponseWriter, r *http.Request) *userInfo { log.Warningf("Could not read CA file: %s", err) } + identityProviderCA := "" + if cfg.IdentityProviderCAPath != "" { + caFile, err := os.Open(cfg.IdentityProviderCAPath) + if err != nil { + log.Errorf("Failed to open CA file. %s", err.Error()) + } + defer caFile.Close() + idpCA, err := ioutil.ReadAll(caFile) + if err != nil { + log.Errorf("Could not read CA file: %s", err.Error()) + } + identityProviderCA = base64.StdEncoding.EncodeToString(idpCA) + } + // load the session cookies sessionIDToken, err := gangwayUserSession.Session.Get(r, "gangway_id_token") if err != nil { @@ -362,17 +378,18 @@ func generateInfo(w http.ResponseWriter, r *http.Request) *userInfo { } info := &userInfo{ - ClusterName: cfg.ClusterName, - Username: username, - KubeCfgUser: kubeCfgUser, - IDToken: idToken, - RefreshToken: refreshToken, - ClientID: cfg.ClientID, - ClientSecret: cfg.ClientSecret, - IssuerURL: issuerURL, - APIServerURL: cfg.APIServerURL, - ClusterCA: string(caBytes), - HTTPPath: cfg.HTTPPath, + ClusterName: cfg.ClusterName, + Username: username, + KubeCfgUser: kubeCfgUser, + IDToken: idToken, + RefreshToken: refreshToken, + ClientID: cfg.ClientID, + ClientSecret: cfg.ClientSecret, + IssuerURL: issuerURL, + APIServerURL: cfg.APIServerURL, + ClusterCA: string(caBytes), + HTTPPath: cfg.HTTPPath, + IdentityProviderCA: identityProviderCA, } return info } diff --git a/cmd/gangway/handlers_test.go b/cmd/gangway/handlers_test.go index 59b89b77e..2e1821f53 100644 --- a/cmd/gangway/handlers_test.go +++ b/cmd/gangway/handlers_test.go @@ -284,11 +284,12 @@ func TestKubeconfigHandler(t *testing.T) { usernameClaim: "sub", expectedAuthInfoName: "gangway@heptio.com@cluster1", expectedAuthInfoAuthProviderConfig: map[string]string{ - "client-id": "someClientID", - "client-secret": "someClientSecret", - "id-token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJHYW5nd2F5VGVzdCIsImlhdCI6MTU0MDA0NjM0NywiZXhwIjoxODg3MjAxNTQ3LCJhdWQiOiJnYW5nd2F5LmhlcHRpby5jb20iLCJzdWIiOiJnYW5nd2F5QGhlcHRpby5jb20iLCJHaXZlbk5hbWUiOiJHYW5nIiwiU3VybmFtZSI6IldheSIsIkVtYWlsIjoiZ2FuZ3dheUBoZXB0aW8uY29tIiwiR3JvdXBzIjoiZGV2LGFkbWluIn0.zNG4Dnxr76J0p4phfsAUYWunioct0krkMiunMynlQsU", - "refresh-token": "bar", - "idp-issuer-url": "GangwayTest", + "client-id": "someClientID", + "client-secret": "someClientSecret", + "id-token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJHYW5nd2F5VGVzdCIsImlhdCI6MTU0MDA0NjM0NywiZXhwIjoxODg3MjAxNTQ3LCJhdWQiOiJnYW5nd2F5LmhlcHRpby5jb20iLCJzdWIiOiJnYW5nd2F5QGhlcHRpby5jb20iLCJHaXZlbk5hbWUiOiJHYW5nIiwiU3VybmFtZSI6IldheSIsIkVtYWlsIjoiZ2FuZ3dheUBoZXB0aW8uY29tIiwiR3JvdXBzIjoiZGV2LGFkbWluIn0.zNG4Dnxr76J0p4phfsAUYWunioct0krkMiunMynlQsU", + "refresh-token": "bar", + "idp-issuer-url": "GangwayTest", + "idp-certificate-authority-data": "", }, }, } diff --git a/docs/yaml/02-config.yaml b/docs/yaml/02-config.yaml index a162183a0..aed8d39d4 100644 --- a/docs/yaml/02-config.yaml +++ b/docs/yaml/02-config.yaml @@ -93,3 +93,7 @@ data: # The path to find custom HTML templates # Env var: GANGWAY_CUSTOM_HTTP_TEMPLATES_DIR #customHTMLTemplatesDir: /custom-templates + + # The path to find the CA bundle for the IdP, optional. Used to configure kubectl. + # Env var: GANGWAY_IDP_CA_PATH + # idpCAPath: "/cacerts/idpca.cert" diff --git a/internal/config/config.go b/internal/config/config.go index e113dbab0..c2e516340 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -45,6 +45,7 @@ type Config struct { APIServerURL string `yaml:"apiServerURL" envconfig:"apiserver_url"` ClusterCAPath string `yaml:"clusterCAPath" envconfig:"cluster_ca_path"` TrustedCAPath string `yaml:"trustedCAPath" envconfig:"trusted_ca_path"` + IdentityProviderCAPath string `yaml:"idpCAPath" envconfig:"idp_ca_path"` HTTPPath string `yaml:"httpPath" envconfig:"http_path"` SessionSecurityKey string `yaml:"sessionSecurityKey" envconfig:"SESSION_SECURITY_KEY"` diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 5393ed7ff..48620950e 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -38,6 +38,7 @@ func TestEnvionmentOverrides(t *testing.T) { os.Setenv("GANGWAY_TOKEN_URL", "https://foo.bar/token") os.Setenv("GANGWAY_AUDIENCE", "foo") os.Setenv("GANGWAY_SCOPES", "groups,sub") + os.Setenv("GANGWAY_IDP_CA_PATH", "/cacerts/idp-ca.cert") cfg, err := NewConfig("") if err != nil { t.Errorf("Failed to test config overrides with error: %s", err) @@ -54,6 +55,9 @@ func TestEnvionmentOverrides(t *testing.T) { if cfg.Scopes[0] != "groups" || cfg.Scopes[1] != "sub" { t.Errorf("Failed to set scopes via environment variable. Expected %s but got %s", "[groups, sub]", cfg.Scopes) } + if cfg.IdentityProviderCAPath != "/cacerts/idp-ca.cert" { + t.Errorf("Failed to set idp ca via environment variable. Expected \"/cacerts/idp-ca.cert\" but got %s", cfg.IdentityProviderCAPath) + } } func TestGetRootPathPrefix(t *testing.T) { diff --git a/templates/commandline.tmpl b/templates/commandline.tmpl index f071c02e9..b0c090241 100644 --- a/templates/commandline.tmpl +++ b/templates/commandline.tmpl @@ -63,7 +63,8 @@ kubectl config set-credentials "{{ .KubeCfgUser }}" \ --auth-provider-arg='client-id={{ .ClientID }}' \ --auth-provider-arg='client-secret={{ .ClientSecret }}' \ --auth-provider-arg='refresh-token={{ .RefreshToken }}' \ - --auth-provider-arg='id-token={{ .IDToken }}' + --auth-provider-arg='id-token={{ .IDToken }}' \ + --auth-provider-arg='idp-certificate-authority-data={{ .IdentityProviderCA }}' \ kubectl config set-context "{{ .ClusterName }}" --cluster="{{ .ClusterName }}" --user="{{ .KubeCfgUser }}" kubectl config use-context "{{ .ClusterName }}" rm "ca-{{ .ClusterName }}.pem"