From cff199abfc1f9a6bc71a3bfb722b79c8650833d7 Mon Sep 17 00:00:00 2001 From: Steven Martin Date: Thu, 11 Mar 2021 21:12:26 -0500 Subject: [PATCH 1/4] Add files via upload --- api/types/oidc.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/api/types/oidc.go b/api/types/oidc.go index 23c8439ebf773..7a587906eb9e8 100644 --- a/api/types/oidc.go +++ b/api/types/oidc.go @@ -85,9 +85,13 @@ type OIDCConnector interface { SetDisplay(string) // GetGoogleServiceAccountURI returns path to google service account URI GetGoogleServiceAccountURI() string + // GetGoogleServiceAccount returns google service account json for Google + GetGoogleServiceAccount() string + // SetGoogleServiceAccount sets the google service account json contents + SetGoogleServiceAccount(string) // GetGoogleAdminEmail returns a google admin user email // https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority - // "Note: Although you can use service accounts in applications that run from a G Suite domain, service accounts are not members of your G Suite account and aren’t subject to domain policies set by G Suite administrators. For example, a policy set in the G Suite admin console to restrict the ability of G Suite end users to share documents outside of the domain would not apply to service accounts." + // "Note: Although you can use service accounts in applications that run from a Google Workspace (formerly G Suite) domain, service accounts are not members of your Google Workspace account and aren’t subject to domain policies set by administrators. For example, a policy set in the Google Workspace admin console to restrict the ability of end users to share documents outside of the domain would not apply to service accounts." GetGoogleAdminEmail() string } @@ -139,6 +143,16 @@ func (o *OIDCConnectorV2) GetGoogleServiceAccountURI() string { return o.Spec.GoogleServiceAccountURI } +// GetGoogleServiceAccount returns a string representing a Google service account +func (o *OIDCConnectorV2) GetGoogleServiceAccount() string { + return o.Spec.GoogleServiceAccount +} + +// SetGoogleServiceAccount sets a string representing a Google service account +func (o *OIDCConnectorV2) SetGoogleServiceAccount(s string) { + o.Spec.GoogleServiceAccount = s +} + // GetGoogleAdminEmail returns a google admin user email func (o *OIDCConnectorV2) GetGoogleAdminEmail() string { return o.Spec.GoogleAdminEmail @@ -176,11 +190,13 @@ func (o *OIDCConnectorV2) SetResourceID(id int64) { // WithoutSecrets returns an instance of resource without secrets. func (o *OIDCConnectorV2) WithoutSecrets() Resource { - if o.GetClientSecret() == "" { + if o.GetClientSecret() == "" && o.GetGoogleServiceAccount() == "" { return o } o2 := *o + o2.SetClientSecret("") + o2.SetGoogleServiceAccount("") return &o2 } @@ -410,6 +426,8 @@ type OIDCConnectorSpecV2 struct { ClaimsToRoles []ClaimMapping `json:"claims_to_roles,omitempty"` // GoogleServiceAccountURI is a path to google service account uri GoogleServiceAccountURI string `json:"google_service_account_uri,omitempty"` + // GoogleServiceAccount is a string containing the google service account credentials + GoogleServiceAccount string `json:"google_service_account,omitempty"` // GoogleAdminEmail is email of google admin to impersonate GoogleAdminEmail string `json:"google_admin_email,omitempty"` } From 07e86cc2fdad3c47f42eec483c47acd7a95a6e2a Mon Sep 17 00:00:00 2001 From: Steven Martin Date: Thu, 11 Mar 2021 21:13:45 -0500 Subject: [PATCH 2/4] Add files via upload --- lib/auth/oidc.go | 49 +++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/lib/auth/oidc.go b/lib/auth/oidc.go index 806c8ccc8a0d9..f245ac1e8fa94 100644 --- a/lib/auth/oidc.go +++ b/lib/auth/oidc.go @@ -668,7 +668,7 @@ collect: for { if count > MaxPages { warningMessage := "Truncating list of teams used to populate claims: " + - "hit maximum number pages that can be fetched from GSuite." + "hit maximum number pages that can be fetched from Google Workspace." // Print warning to Teleport logs as well as the Audit Log. log.Warnf(warningMessage) @@ -714,7 +714,7 @@ func (g *gsuiteClient) fetchGroupsPage(pageToken string) (*gsuiteGroups, error) u.RawQuery = q.Encode() endpoint := u.String() - log.Debugf("Fetching OIDC claims from GSuite groups endpoint: %q.", endpoint) + log.Debugf("Fetching OIDC claims from Google Workspace groups endpoint: %q.", endpoint) req, err := http.NewRequest("GET", endpoint, nil) if err != nil { @@ -835,32 +835,39 @@ func (a *Server) getClaims(oidcClient *oidc.Client, connector services.OIDCConne return nil, trace.Wrap(err) } - serviceAccountURI := connector.GetGoogleServiceAccountURI() - if serviceAccountURI == "" { - return nil, trace.NotFound( - "the gsuite connector requires google_service_account_uri parameter to be specified and pointing to a valid google service account file with credentials, read this article for more details https://developers.google.com/admin-sdk/directory/v1/guides/delegation") + var jsonCredentials []byte + var credentialLoadingMethod string + if connector.GetGoogleServiceAccountURI() != "" { + // load the google service account from URI + credentialLoadingMethod = "google_service_account_uri" + + uri, err := utils.ParseSessionsURI(connector.GetGoogleServiceAccountURI()) + if err != nil { + return nil, trace.BadParameter("failed to parse google_service_account_uri: %v", err) + } + jsonCredentials, err = ioutil.ReadFile(uri.Path) + if err != nil { + return nil, trace.Wrap(err) + } + } else if connector.GetGoogleServiceAccount() != "" { + // load the google service account from string + credentialLoadingMethod = "google_service_account" + jsonCredentials = []byte(connector.GetGoogleServiceAccount()) + } else { + return nil, trace.NotFound("the google workspace connector requires google_service_account parameter with JSON-formatted credentials or google_service_account_uri parameter pointing to a valid google service account file with credentials to be specified, read this article for more details https://developers.google.com/admin-sdk/directory/v1/guides/delegation") } - uri, err := utils.ParseSessionsURI(serviceAccountURI) + config, err := google.JWTConfigFromJSON(jsonCredentials, teleport.GSuiteGroupsScope) if err != nil { - return nil, trace.BadParameter("failed to parse google_service_account_uri: %v", err) + return nil, trace.BadParameter("unable to parse google service account from %v: %v", credentialLoadingMethod, err) } impersonateAdmin := connector.GetGoogleAdminEmail() if impersonateAdmin == "" { return nil, trace.NotFound( - "the gsuite connector requires google_admin_email user to impersonate, as service accounts can not be used directly https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority") - } - - jsonCredentials, err := ioutil.ReadFile(uri.Path) - if err != nil { - return nil, trace.Wrap(err) + "the google workspace connector requires google_admin_email user to impersonate, as service accounts can not be used directly https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority") } - config, err := google.JWTConfigFromJSON(jsonCredentials, teleport.GSuiteGroupsScope) - if err != nil { - return nil, trace.BadParameter("unable to parse client secret file to config: %v", err) - } // User should impersonate admin user, otherwise it won't work: // // https://developers.google.com/admin-sdk/directory/v1/guides/delegation @@ -869,7 +876,7 @@ func (a *Server) getClaims(oidcClient *oidc.Client, connector services.OIDCConne // domain, exists, err := userInfoClaims.StringClaim(teleport.GSuiteDomainClaim) if err != nil || !exists { - return nil, trace.BadParameter("hd is the required claim for GSuite") + return nil, trace.BadParameter("hd is the required claim for Google Workspace") } config.Subject = impersonateAdmin @@ -878,10 +885,10 @@ func (a *Server) getClaims(oidcClient *oidc.Client, connector services.OIDCConne if !trace.IsNotFound(err) { return nil, trace.Wrap(err) } - log.Debugf("Found no GSuite claims.") + log.Debugf("Found no Google Workspace claims.") } else { if gsuiteClaims != nil { - log.Debugf("Got GSuiteclaims: %v.", gsuiteClaims) + log.Debugf("Got gsuiteClaims claims from Google Workspace: %v.", gsuiteClaims) } claims, err = mergeClaims(claims, gsuiteClaims) if err != nil { From 8d504ee5aba8c5ec6258f94dca1f8365e6d6fc88 Mon Sep 17 00:00:00 2001 From: Steven Martin Date: Thu, 11 Mar 2021 21:14:46 -0500 Subject: [PATCH 3/4] Add files via upload --- lib/services/local/users.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/services/local/users.go b/lib/services/local/users.go index c7f8b3c549a03..2724bdcad0861 100644 --- a/lib/services/local/users.go +++ b/lib/services/local/users.go @@ -718,6 +718,7 @@ func (s *IdentityService) GetOIDCConnector(name string, withSecrets bool) (servi } if !withSecrets { conn.SetClientSecret("") + conn.SetGoogleServiceAccount("") } return conn, nil } @@ -738,6 +739,7 @@ func (s *IdentityService) GetOIDCConnectors(withSecrets bool) ([]services.OIDCCo } if !withSecrets { conn.SetClientSecret("") + conn.SetGoogleServiceAccount("") } connectors[i] = conn } From 678380c64bc6ac215852e8e61707713312530b6c Mon Sep 17 00:00:00 2001 From: Steven Martin Date: Thu, 11 Mar 2021 21:15:17 -0500 Subject: [PATCH 4/4] Add files via upload --- lib/services/oidc.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/services/oidc.go b/lib/services/oidc.go index ea1dd5de058c8..1316ae0bd3328 100644 --- a/lib/services/oidc.go +++ b/lib/services/oidc.go @@ -38,6 +38,11 @@ func ValidateOIDCConnector(oc types.OIDCConnector) error { if _, err := url.Parse(oc.GetRedirectURL()); err != nil { return trace.BadParameter("RedirectURL: bad url: '%v'", oc.GetRedirectURL()) } + + if oc.GetGoogleServiceAccountURI() != "" && oc.GetGoogleServiceAccount() != "" { + return trace.BadParameter("one of either google_service_account_uri or google_service_account is supported, not both") + } + if oc.GetGoogleServiceAccountURI() != "" { uri, err := utils.ParseSessionsURI(oc.GetGoogleServiceAccountURI()) if err != nil { @@ -50,6 +55,11 @@ func ValidateOIDCConnector(oc types.OIDCConnector) error { return trace.BadParameter("whenever google_service_account_uri is specified, google_admin_email should be set as well, read https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority for more details") } } + if oc.GetGoogleServiceAccount() != "" { + if oc.GetGoogleAdminEmail() == "" { + return trace.BadParameter("whenever google_service_account is specified, google_admin_email should be set as well, read https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority for more details") + } + } return nil } @@ -95,6 +105,7 @@ var OIDCConnectorSpecV2Schema = fmt.Sprintf(`{ "display": {"type": "string"}, "prompt": {"type": "string"}, "google_service_account_uri": {"type": "string"}, + "google_service_account": {"type": "string"}, "google_admin_email": {"type": "string"}, "scope": { "type": "array",