From 62c33a15f68895d6a388f68d2827e9fd5705c5df Mon Sep 17 00:00:00 2001 From: Tom Clegg Date: Fri, 8 May 2020 13:46:44 -0400 Subject: [PATCH] 15881: Move Google, SSO, and PAM configs into their own sections. Arvados-DCO-1.1-Signed-off-by: Tom Clegg --- doc/install/setup-login.html.textile.liquid | 10 +- lib/config/config.default.yml | 107 ++++++++++-------- lib/config/deprecated.go | 35 ++++++ lib/config/deprecated_test.go | 35 ++++++ lib/config/export.go | 23 ++-- lib/config/generated_config.go | 107 ++++++++++-------- lib/controller/localdb/login.go | 8 +- lib/controller/localdb/login_google.go | 6 +- lib/controller/localdb/login_google_test.go | 10 +- .../localdb/login_ldap_docker_test.sh | 11 +- lib/controller/localdb/login_pam.go | 4 +- lib/controller/localdb/login_pam_test.go | 6 +- sdk/go/arvados/config.go | 28 +++-- 13 files changed, 249 insertions(+), 141 deletions(-) diff --git a/doc/install/setup-login.html.textile.liquid b/doc/install/setup-login.html.textile.liquid index 753ba82ba6..a9fa4fd4e0 100644 --- a/doc/install/setup-login.html.textile.liquid +++ b/doc/install/setup-login.html.textile.liquid @@ -22,12 +22,13 @@ With this configuration, users will sign in with their Google accounts. First, visit "Setting up Google auth.":google-auth.html -Next, copy the values of *Client ID* and *Client secret* from the Google Developers Console into @Login.GoogleClientID@ and @Login.GoogleClientSecret@ of @config.yml@: +Next, copy the values of *Client ID* and *Client secret* from the Google Developers Console into @Login.Google.ClientID@ and @Login.Google.ClientSecret@ in @config.yml@:
     Login:
-      GoogleClientID: "0000000000000-zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.apps.googleusercontent.com"
-      GoogleClientSecret: "zzzzzzzzzzzzzzzzzzzzzzzz"
+      Google:
+        ClientID: "0000000000000-zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.apps.googleusercontent.com"
+        ClientSecret: "zzzzzzzzzzzzzzzzzzzzzzzz"
 
h2(#ldap). LDAP @@ -64,7 +65,8 @@ Enable PAM authentication in @config.yml@:
     Login:
-      PAM: true
+      PAM:
+        Enable: true
 
Check the "default config file":{{site.baseurl}}/admin/config.html for more PAM configuration options. diff --git a/lib/config/config.default.yml b/lib/config/config.default.yml index 2aa58ac31e..e248458899 100644 --- a/lib/config/config.default.yml +++ b/lib/config/config.default.yml @@ -524,54 +524,55 @@ Clusters: MaxUUIDEntries: 1000 Login: - # These settings are provided by your OAuth2 provider (eg - # Google) used to perform upstream authentication. - ProviderAppID: "" - ProviderAppSecret: "" - - # (Experimental) Authenticate with Google, bypassing the - # SSO-provider gateway service. Use the Google Cloud console to - # enable the People API (APIs and Services > Enable APIs and - # services > Google People API > Enable), generate a Client ID - # and secret (APIs and Services > Credentials > Create - # credentials > OAuth client ID > Web application) and add your - # controller's /login URL (e.g., - # "https://zzzzz.example.com/login") as an authorized redirect - # URL. - # - # Incompatible with ForceLegacyAPI14. ProviderAppID must be - # blank. - GoogleClientID: "" - GoogleClientSecret: "" - - # Allow users to log in to existing accounts using any verified - # email address listed by their Google account. If true, the - # Google People API must be enabled in order for Google login to - # work. If false, only the primary email address will be used. - GoogleAlternateEmailAddresses: true - - # (Experimental) Use PAM to authenticate logins, using the - # specified PAM service name. - # - # Cannot be used in combination with OAuth2 (ProviderAppID) or - # Google (GoogleClientID). Cannot be used on a cluster acting as - # a LoginCluster. - PAM: false - PAMService: arvados - - # Domain name (e.g., "example.com") to use to construct the - # user's email address if PAM authentication returns a username - # with no "@". If empty, use the PAM username as the user's - # email address, whether or not it contains "@". - # - # Note that the email address is used as the primary key for - # user records when logging in. Therefore, if you change - # PAMDefaultEmailDomain after the initial installation, you - # should also update existing user records to reflect the new - # domain. Otherwise, next time those users log in, they will be - # given new accounts instead of accessing their existing - # accounts. - PAMDefaultEmailDomain: "" + # One of the following mechanisms (SSO, Google, PAM, LDAP, or + # LoginCluster) should be enabled; see + # https://doc.arvados.org/install/setup-login.html + + Google: + # Authenticate with Google. + Enable: false + + # Use the Google Cloud console to enable the People API (APIs + # and Services > Enable APIs and services > Google People API + # > Enable), generate a Client ID and secret (APIs and + # Services > Credentials > Create credentials > OAuth client + # ID > Web application) and add your controller's /login URL + # (e.g., "https://zzzzz.example.com/login") as an authorized + # redirect URL. + # + # Incompatible with ForceLegacyAPI14. ProviderAppID must be + # blank. + ClientID: "" + ClientSecret: "" + + # Allow users to log in to existing accounts using any verified + # email address listed by their Google account. If true, the + # Google People API must be enabled in order for Google login to + # work. If false, only the primary email address will be used. + AlternateEmailAddresses: true + + PAM: + # (Experimental) Use PAM to authenticate users. + Enable: false + + # PAM service name. PAM will apply the policy in the + # corresponding config file (e.g., /etc/pam.d/arvados) or, if + # there is none, the default "other" config. + Service: arvados + + # Domain name (e.g., "example.com") to use to construct the + # user's email address if PAM authentication returns a + # username with no "@". If empty, use the PAM username as the + # user's email address, whether or not it contains "@". + # + # Note that the email address is used as the primary key for + # user records when logging in. Therefore, if you change + # PAMDefaultEmailDomain after the initial installation, you + # should also update existing user records to reflect the new + # domain. Otherwise, next time those users log in, they will + # be given new accounts instead of accessing their existing + # accounts. + DefaultEmailDomain: "" LDAP: # Use an LDAP service to authenticate users. @@ -631,6 +632,16 @@ Clusters: # originally supplied by the user will be used. UsernameAttribute: uid + SSO: + # Authenticate with a separate SSO server. + Enable: false + + # ProviderAppID and ProviderAppSecret are generated during SSO + # setup; see + # https://doc.arvados.org/install/install-sso.html#update-config + ProviderAppID: "" + ProviderAppSecret: "" + # The cluster ID to delegate the user database. When set, # logins on this cluster will be redirected to the login cluster # (login cluster must appear in RemoteClusters with Proxy: true) diff --git a/lib/config/deprecated.go b/lib/config/deprecated.go index 0689efa440..3d62e7cc56 100644 --- a/lib/config/deprecated.go +++ b/lib/config/deprecated.go @@ -23,6 +23,13 @@ type deprRequestLimits struct { type deprCluster struct { RequestLimits deprRequestLimits NodeProfiles map[string]nodeProfile + Login struct { + GoogleClientID *string + GoogleClientSecret *string + GoogleAlternateEmailAddresses *bool + ProviderAppID *string + ProviderAppSecret *string + } } type deprecatedConfig struct { @@ -80,6 +87,34 @@ func (ldr *Loader) applyDeprecatedConfig(cfg *arvados.Config) error { if dst, n := &cluster.API.MaxRequestAmplification, dcluster.RequestLimits.MultiClusterRequestConcurrency; n != nil && *n != *dst { *dst = *n } + + // Google* moved to Google.* + if dst, n := &cluster.Login.Google.ClientID, dcluster.Login.GoogleClientID; n != nil && *n != *dst { + *dst = *n + if *n != "" { + // In old config, non-empty ClientID meant enable + cluster.Login.Google.Enable = true + } + } + if dst, n := &cluster.Login.Google.ClientSecret, dcluster.Login.GoogleClientSecret; n != nil && *n != *dst { + *dst = *n + } + if dst, n := &cluster.Login.Google.AlternateEmailAddresses, dcluster.Login.GoogleAlternateEmailAddresses; n != nil && *n != *dst { + *dst = *n + } + + // Provider* moved to SSO.Provider* + if dst, n := &cluster.Login.SSO.ProviderAppID, dcluster.Login.ProviderAppID; n != nil && *n != *dst { + *dst = *n + if *n != "" { + // In old config, non-empty ID meant enable + cluster.Login.SSO.Enable = true + } + } + if dst, n := &cluster.Login.SSO.ProviderAppSecret, dcluster.Login.ProviderAppSecret; n != nil && *n != *dst { + *dst = *n + } + cfg.Clusters[id] = cluster } return nil diff --git a/lib/config/deprecated_test.go b/lib/config/deprecated_test.go index 58c27e984a..96eea4264c 100644 --- a/lib/config/deprecated_test.go +++ b/lib/config/deprecated_test.go @@ -89,6 +89,41 @@ Clusters: `) } +func (s *LoadSuite) TestDeprecatedLoginBackend(c *check.C) { + checkEquivalent(c, ` +Clusters: + z1111: + Login: + GoogleClientID: aaaa + GoogleClientSecret: bbbb + GoogleAlternateEmailAddresses: true +`, ` +Clusters: + z1111: + Login: + Google: + Enable: true + ClientID: aaaa + ClientSecret: bbbb + AlternateEmailAddresses: true +`) + checkEquivalent(c, ` +Clusters: + z1111: + Login: + ProviderAppID: aaaa + ProviderAppSecret: bbbb +`, ` +Clusters: + z1111: + Login: + SSO: + Enable: true + ProviderAppID: aaaa + ProviderAppSecret: bbbb +`) +} + func (s *LoadSuite) TestLegacyKeepWebConfig(c *check.C) { content := []byte(` { diff --git a/lib/config/export.go b/lib/config/export.go index 323043fbe7..26782c8ba6 100644 --- a/lib/config/export.go +++ b/lib/config/export.go @@ -131,14 +131,11 @@ var whitelist = map[string]bool{ "InstanceTypes.*": true, "InstanceTypes.*.*": true, "Login": true, - "Login.GoogleClientID": false, - "Login.GoogleClientSecret": false, - "Login.GoogleAlternateEmailAddresses": false, - "Login.PAM": true, - "Login.PAMService": false, - "Login.PAMDefaultEmailDomain": false, - "Login.ProviderAppID": false, - "Login.ProviderAppSecret": false, + "Login.Google": true, + "Login.Google.AlternateEmailAddresses": false, + "Login.Google.ClientID": false, + "Login.Google.ClientSecret": false, + "Login.Google.Enable": true, "Login.LDAP": true, "Login.LDAP.AppendDomain": false, "Login.LDAP.EmailAttribute": false, @@ -146,14 +143,22 @@ var whitelist = map[string]bool{ "Login.LDAP.InsecureTLS": false, "Login.LDAP.SearchAttribute": false, "Login.LDAP.SearchBase": false, - "Login.LDAP.SearchBindUser": false, "Login.LDAP.SearchBindPassword": false, + "Login.LDAP.SearchBindUser": false, "Login.LDAP.SearchFilters": false, "Login.LDAP.StartTLS": false, "Login.LDAP.StripDomain": false, "Login.LDAP.URL": false, "Login.LDAP.UsernameAttribute": false, "Login.LoginCluster": true, + "Login.PAM": true, + "Login.PAM.DefaultEmailDomain": false, + "Login.PAM.Enable": true, + "Login.PAM.Service": false, + "Login.SSO": true, + "Login.SSO.Enable": true, + "Login.SSO.ProviderAppID": false, + "Login.SSO.ProviderAppSecret": false, "Login.RemoteTokenRefresh": true, "Mail": true, "Mail.MailchimpAPIKey": false, diff --git a/lib/config/generated_config.go b/lib/config/generated_config.go index 4677fe5444..df08dd00e2 100644 --- a/lib/config/generated_config.go +++ b/lib/config/generated_config.go @@ -530,54 +530,55 @@ Clusters: MaxUUIDEntries: 1000 Login: - # These settings are provided by your OAuth2 provider (eg - # Google) used to perform upstream authentication. - ProviderAppID: "" - ProviderAppSecret: "" - - # (Experimental) Authenticate with Google, bypassing the - # SSO-provider gateway service. Use the Google Cloud console to - # enable the People API (APIs and Services > Enable APIs and - # services > Google People API > Enable), generate a Client ID - # and secret (APIs and Services > Credentials > Create - # credentials > OAuth client ID > Web application) and add your - # controller's /login URL (e.g., - # "https://zzzzz.example.com/login") as an authorized redirect - # URL. - # - # Incompatible with ForceLegacyAPI14. ProviderAppID must be - # blank. - GoogleClientID: "" - GoogleClientSecret: "" - - # Allow users to log in to existing accounts using any verified - # email address listed by their Google account. If true, the - # Google People API must be enabled in order for Google login to - # work. If false, only the primary email address will be used. - GoogleAlternateEmailAddresses: true - - # (Experimental) Use PAM to authenticate logins, using the - # specified PAM service name. - # - # Cannot be used in combination with OAuth2 (ProviderAppID) or - # Google (GoogleClientID). Cannot be used on a cluster acting as - # a LoginCluster. - PAM: false - PAMService: arvados - - # Domain name (e.g., "example.com") to use to construct the - # user's email address if PAM authentication returns a username - # with no "@". If empty, use the PAM username as the user's - # email address, whether or not it contains "@". - # - # Note that the email address is used as the primary key for - # user records when logging in. Therefore, if you change - # PAMDefaultEmailDomain after the initial installation, you - # should also update existing user records to reflect the new - # domain. Otherwise, next time those users log in, they will be - # given new accounts instead of accessing their existing - # accounts. - PAMDefaultEmailDomain: "" + # One of the following mechanisms (SSO, Google, PAM, LDAP, or + # LoginCluster) should be enabled; see + # https://doc.arvados.org/install/setup-login.html + + Google: + # Authenticate with Google. + Enable: false + + # Use the Google Cloud console to enable the People API (APIs + # and Services > Enable APIs and services > Google People API + # > Enable), generate a Client ID and secret (APIs and + # Services > Credentials > Create credentials > OAuth client + # ID > Web application) and add your controller's /login URL + # (e.g., "https://zzzzz.example.com/login") as an authorized + # redirect URL. + # + # Incompatible with ForceLegacyAPI14. ProviderAppID must be + # blank. + ClientID: "" + ClientSecret: "" + + # Allow users to log in to existing accounts using any verified + # email address listed by their Google account. If true, the + # Google People API must be enabled in order for Google login to + # work. If false, only the primary email address will be used. + AlternateEmailAddresses: true + + PAM: + # (Experimental) Use PAM to authenticate users. + Enable: false + + # PAM service name. PAM will apply the policy in the + # corresponding config file (e.g., /etc/pam.d/arvados) or, if + # there is none, the default "other" config. + Service: arvados + + # Domain name (e.g., "example.com") to use to construct the + # user's email address if PAM authentication returns a + # username with no "@". If empty, use the PAM username as the + # user's email address, whether or not it contains "@". + # + # Note that the email address is used as the primary key for + # user records when logging in. Therefore, if you change + # PAMDefaultEmailDomain after the initial installation, you + # should also update existing user records to reflect the new + # domain. Otherwise, next time those users log in, they will + # be given new accounts instead of accessing their existing + # accounts. + DefaultEmailDomain: "" LDAP: # Use an LDAP service to authenticate users. @@ -637,6 +638,16 @@ Clusters: # originally supplied by the user will be used. UsernameAttribute: uid + SSO: + # Authenticate with a separate SSO server. + Enable: false + + # ProviderAppID and ProviderAppSecret are generated during SSO + # setup; see + # https://doc.arvados.org/install/install-sso.html#update-config + ProviderAppID: "" + ProviderAppSecret: "" + # The cluster ID to delegate the user database. When set, # logins on this cluster will be redirected to the login cluster # (login cluster must appear in RemoteClusters with Proxy: true) diff --git a/lib/controller/localdb/login.go b/lib/controller/localdb/login.go index 8cba3b6fa1..0fd0a9ad23 100644 --- a/lib/controller/localdb/login.go +++ b/lib/controller/localdb/login.go @@ -23,9 +23,9 @@ type loginController interface { } func chooseLoginController(cluster *arvados.Cluster, railsProxy *railsProxy) loginController { - wantGoogle := cluster.Login.GoogleClientID != "" - wantSSO := cluster.Login.ProviderAppID != "" - wantPAM := cluster.Login.PAM + wantGoogle := cluster.Login.Google.Enable + wantSSO := cluster.Login.SSO.Enable + wantPAM := cluster.Login.PAM.Enable wantLDAP := cluster.Login.LDAP.Enable switch { case wantGoogle && !wantSSO && !wantPAM && !wantLDAP: @@ -38,7 +38,7 @@ func chooseLoginController(cluster *arvados.Cluster, railsProxy *railsProxy) log return &ldapLoginController{Cluster: cluster, RailsProxy: railsProxy} default: return errorLoginController{ - error: errors.New("configuration problem: exactly one of Login.GoogleClientID, Login.ProviderAppID, Login.PAM, or Login.LDAP.Enable must be configured"), + error: errors.New("configuration problem: exactly one of Login.Google, Login.SSO, Login.PAM, and Login.LDAP must be enabled"), } } } diff --git a/lib/controller/localdb/login_google.go b/lib/controller/localdb/login_google.go index bf1754c158..144b04c46d 100644 --- a/lib/controller/localdb/login_google.go +++ b/lib/controller/localdb/login_google.go @@ -71,8 +71,8 @@ func (ctrl *googleLoginController) Login(ctx context.Context, opts arvados.Login return loginError(fmt.Errorf("error making redirect URL: %s", err)) } conf := &oauth2.Config{ - ClientID: ctrl.Cluster.Login.GoogleClientID, - ClientSecret: ctrl.Cluster.Login.GoogleClientSecret, + ClientID: ctrl.Cluster.Login.Google.ClientID, + ClientSecret: ctrl.Cluster.Login.Google.ClientSecret, Endpoint: provider.Endpoint(), Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, RedirectURL: redirURL.String(), @@ -162,7 +162,7 @@ func (ctrl *googleLoginController) getAuthInfo(ctx context.Context, cluster *arv ret.Email = claims.Email } - if !ctrl.Cluster.Login.GoogleAlternateEmailAddresses { + if !ctrl.Cluster.Login.Google.AlternateEmailAddresses { if ret.Email == "" { return nil, fmt.Errorf("cannot log in with unverified email address %q", claims.Email) } diff --git a/lib/controller/localdb/login_google_test.go b/lib/controller/localdb/login_google_test.go index 9e16e2e904..495fbb69b3 100644 --- a/lib/controller/localdb/login_google_test.go +++ b/lib/controller/localdb/login_google_test.go @@ -146,10 +146,10 @@ func (s *LoginSuite) SetUpTest(c *check.C) { cfg, err := config.NewLoader(nil, ctxlog.TestLogger(c)).Load() s.cluster, err = cfg.GetCluster("") - s.cluster.Login.ProviderAppID = "" - s.cluster.Login.ProviderAppSecret = "" - s.cluster.Login.GoogleClientID = "test%client$id" - s.cluster.Login.GoogleClientSecret = "test#client/secret" + s.cluster.Login.SSO.Enable = false + s.cluster.Login.Google.Enable = true + s.cluster.Login.Google.ClientID = "test%client$id" + s.cluster.Login.Google.ClientSecret = "test#client/secret" s.cluster.Users.PreferDomainForUsername = "PreferDomainForUsername.example.com" c.Assert(err, check.IsNil) @@ -227,7 +227,7 @@ func (s *LoginSuite) setupPeopleAPIError(c *check.C) { } func (s *LoginSuite) TestGoogleLogin_PeopleAPIDisabled(c *check.C) { - s.cluster.Login.GoogleAlternateEmailAddresses = false + s.cluster.Login.Google.AlternateEmailAddresses = false s.authEmail = "joe.smith@primary.example.com" s.setupPeopleAPIError(c) state := s.startLogin(c) diff --git a/lib/controller/localdb/login_ldap_docker_test.sh b/lib/controller/localdb/login_ldap_docker_test.sh index 61b1e0e884..4e0679f620 100755 --- a/lib/controller/localdb/login_ldap_docker_test.sh +++ b/lib/controller/localdb/login_ldap_docker_test.sh @@ -103,11 +103,12 @@ case "${config_method}" in setup_pam_ldap="apt update && DEBIAN_FRONTEND=noninteractive apt install -y ldap-utils libpam-ldap && pam-auth-update --package /usr/share/pam-configs/ldap" cat >>"${tmpdir}/zzzzz.yml" <