Workload identity support (#2126)

* WIP: support for workload identity

* WIP: bugfixes to support WI

* Added support for Workload Identity

* Added missing flag

* Refactoring and typo

* Updated CHANGELOG.md

* Updated docs

* Updated changelog

* Improved readability and fixed codeclimate issues

* Update CHANGELOG.md

Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk>

* Fixed if statement

* Apply suggestions from code review

Co-authored-by: Jan Larwig <jan@larwig.com>

* Cleanup

* Removed target principal

* Removed references to target principal

* Added docs

* Fixed header anchor linking

* Update auth.md

* Updated generated code

* Improved code

* Fixed tests

---------

Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk>
Co-authored-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
Koen van Zuijlen
2023-09-04 10:34:54 +01:00
committed by GitHub
co-authored by Joel Speed Jan Larwig
parent 738c09095b
commit a6e8ec81e8
9 changed files with 114 additions and 60 deletions
+32 -21
View File
@@ -70,6 +70,7 @@ func NewLegacyFlagSet() *pflag.FlagSet {
flagSet.AddFlagSet(legacyHeadersFlagSet())
flagSet.AddFlagSet(legacyServerFlagset())
flagSet.AddFlagSet(legacyProviderFlagSet())
flagSet.AddFlagSet(legacyGoogleFlagSet())
return flagSet
}
@@ -481,21 +482,22 @@ type LegacyProvider struct {
ClientSecret string `flag:"client-secret" cfg:"client_secret"`
ClientSecretFile string `flag:"client-secret-file" cfg:"client_secret_file"`
KeycloakGroups []string `flag:"keycloak-group" cfg:"keycloak_groups"`
AzureTenant string `flag:"azure-tenant" cfg:"azure_tenant"`
AzureGraphGroupField string `flag:"azure-graph-group-field" cfg:"azure_graph_group_field"`
BitbucketTeam string `flag:"bitbucket-team" cfg:"bitbucket_team"`
BitbucketRepository string `flag:"bitbucket-repository" cfg:"bitbucket_repository"`
GitHubOrg string `flag:"github-org" cfg:"github_org"`
GitHubTeam string `flag:"github-team" cfg:"github_team"`
GitHubRepo string `flag:"github-repo" cfg:"github_repo"`
GitHubToken string `flag:"github-token" cfg:"github_token"`
GitHubUsers []string `flag:"github-user" cfg:"github_users"`
GitLabGroup []string `flag:"gitlab-group" cfg:"gitlab_groups"`
GitLabProjects []string `flag:"gitlab-project" cfg:"gitlab_projects"`
GoogleGroups []string `flag:"google-group" cfg:"google_group"`
GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email"`
GoogleServiceAccountJSON string `flag:"google-service-account-json" cfg:"google_service_account_json"`
KeycloakGroups []string `flag:"keycloak-group" cfg:"keycloak_groups"`
AzureTenant string `flag:"azure-tenant" cfg:"azure_tenant"`
AzureGraphGroupField string `flag:"azure-graph-group-field" cfg:"azure_graph_group_field"`
BitbucketTeam string `flag:"bitbucket-team" cfg:"bitbucket_team"`
BitbucketRepository string `flag:"bitbucket-repository" cfg:"bitbucket_repository"`
GitHubOrg string `flag:"github-org" cfg:"github_org"`
GitHubTeam string `flag:"github-team" cfg:"github_team"`
GitHubRepo string `flag:"github-repo" cfg:"github_repo"`
GitHubToken string `flag:"github-token" cfg:"github_token"`
GitHubUsers []string `flag:"github-user" cfg:"github_users"`
GitLabGroup []string `flag:"gitlab-group" cfg:"gitlab_groups"`
GitLabProjects []string `flag:"gitlab-project" cfg:"gitlab_projects"`
GoogleGroups []string `flag:"google-group" cfg:"google_group"`
GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email"`
GoogleServiceAccountJSON string `flag:"google-service-account-json" cfg:"google_service_account_json"`
GoogleUseApplicationDefaultCredentials bool `flag:"google-use-application-default-credentials" cfg:"google_use_application_default_credentials"`
// These options allow for other providers besides Google, with
// potential overrides.
@@ -549,9 +551,6 @@ func legacyProviderFlagSet() *pflag.FlagSet {
flagSet.StringSlice("github-user", []string{}, "allow users with these usernames to login even if they do not belong to the specified org and team or collaborators (may be given multiple times)")
flagSet.StringSlice("gitlab-group", []string{}, "restrict logins to members of this group (may be given multiple times)")
flagSet.StringSlice("gitlab-project", []string{}, "restrict logins to members of this project (may be given multiple times) (eg `group/project=accesslevel`). Access level should be a value matching Gitlab access levels (see https://docs.gitlab.com/ee/api/members.html#valid-access-levels), defaulted to 20 if absent")
flagSet.StringSlice("google-group", []string{}, "restrict logins to members of this google group (may be given multiple times).")
flagSet.String("google-admin-email", "", "the google admin to impersonate for api calls")
flagSet.String("google-service-account-json", "", "the path to the service account json credentials")
flagSet.String("client-id", "", "the OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"")
flagSet.String("client-secret", "", "the OAuth Client Secret")
flagSet.String("client-secret-file", "", "the file with OAuth Client Secret")
@@ -592,6 +591,17 @@ func legacyProviderFlagSet() *pflag.FlagSet {
return flagSet
}
func legacyGoogleFlagSet() *pflag.FlagSet {
flagSet := pflag.NewFlagSet("google", pflag.ExitOnError)
flagSet.StringSlice("google-group", []string{}, "restrict logins to members of this google group (may be given multiple times).")
flagSet.String("google-admin-email", "", "the google admin to impersonate for api calls")
flagSet.String("google-service-account-json", "", "the path to the service account json credentials")
flagSet.String("google-use-application-default-credentials", "", "use application default credentials instead of service account json (i.e. GKE Workload Identity)")
return flagSet
}
func (l LegacyServer) convert() (Server, Server) {
appServer := Server{
BindAddress: l.HTTPAddress,
@@ -718,9 +728,10 @@ func (l *LegacyProvider) convert() (Providers, error) {
}
case "google":
provider.GoogleConfig = GoogleOptions{
Groups: l.GoogleGroups,
AdminEmail: l.GoogleAdminEmail,
ServiceAccountJSON: l.GoogleServiceAccountJSON,
Groups: l.GoogleGroups,
AdminEmail: l.GoogleAdminEmail,
ServiceAccountJSON: l.GoogleServiceAccountJSON,
UseApplicationDefaultCredentials: l.GoogleUseApplicationDefaultCredentials,
}
}
+2
View File
@@ -189,6 +189,8 @@ type GoogleOptions struct {
AdminEmail string `json:"adminEmail,omitempty"`
// ServiceAccountJSON is the path to the service account json credentials
ServiceAccountJSON string `json:"serviceAccountJson,omitempty"`
// UseApplicationDefaultCredentials is a boolean whether to use Application Default Credentials instead of a ServiceAccountJSON
UseApplicationDefaultCredentials bool `json:"useApplicationDefaultCredentials,omitempty"`
}
type OIDCOptions struct {
+2 -2
View File
@@ -63,7 +63,7 @@ func TestGoogleGroupOptions(t *testing.T) {
expected := errorMsg([]string{
"missing setting: google-admin-email",
"missing setting: google-service-account-json"})
"missing setting: google-service-account-json or google-use-application-default-credentials"})
assert.Equal(t, expected, err.Error())
}
@@ -76,7 +76,7 @@ func TestGoogleGroupInvalidFile(t *testing.T) {
assert.NotEqual(t, nil, err)
expected := errorMsg([]string{
"invalid Google credentials file: file_doesnt_exist.json",
"Google credentials file not found: file_doesnt_exist.json",
})
assert.Equal(t, expected, err.Error())
}
+25 -13
View File
@@ -66,20 +66,32 @@ func validateProvider(provider options.Provider, providerIDs map[string]struct{}
func validateGoogleConfig(provider options.Provider) []string {
msgs := []string{}
if len(provider.GoogleConfig.Groups) > 0 ||
provider.GoogleConfig.AdminEmail != "" ||
provider.GoogleConfig.ServiceAccountJSON != "" {
if len(provider.GoogleConfig.Groups) < 1 {
msgs = append(msgs, "missing setting: google-group")
}
if provider.GoogleConfig.AdminEmail == "" {
msgs = append(msgs, "missing setting: google-admin-email")
}
if provider.GoogleConfig.ServiceAccountJSON == "" {
msgs = append(msgs, "missing setting: google-service-account-json")
} else if _, err := os.Stat(provider.GoogleConfig.ServiceAccountJSON); err != nil {
msgs = append(msgs, fmt.Sprintf("invalid Google credentials file: %s", provider.GoogleConfig.ServiceAccountJSON))
hasGoogleGroups := len(provider.GoogleConfig.Groups) >= 1
hasAdminEmail := provider.GoogleConfig.AdminEmail != ""
hasSAJSON := provider.GoogleConfig.ServiceAccountJSON != ""
useADC := provider.GoogleConfig.UseApplicationDefaultCredentials
if !hasGoogleGroups && !hasAdminEmail && !hasSAJSON && !useADC {
return msgs
}
if !hasGoogleGroups {
msgs = append(msgs, "missing setting: google-group")
}
if !hasAdminEmail {
msgs = append(msgs, "missing setting: google-admin-email")
}
_, err := os.Stat(provider.GoogleConfig.ServiceAccountJSON)
if !useADC {
if !hasSAJSON {
msgs = append(msgs, "missing setting: google-service-account-json or google-use-application-default-credentials")
} else if err != nil {
msgs = append(msgs, fmt.Sprintf("Google credentials file not found: %s", provider.GoogleConfig.ServiceAccountJSON))
}
} else if hasSAJSON {
msgs = append(msgs, "invalid setting: can't use both google-service-account-json and google-use-application-default-credentials")
}
return msgs