Skip to content

Commit

Permalink
Merge pull request #6 from nais/sa-subject-config
Browse files Browse the repository at this point in the history
Make service account and subject email configurable
  • Loading branch information
thokra-nav authored Mar 7, 2024
2 parents f5c1563 + 69b4d13 commit db054e0
Show file tree
Hide file tree
Showing 20 changed files with 158 additions and 166 deletions.
3 changes: 2 additions & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
golang 1.22.1
golang 1.22.1
helm 3.13.1
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: all

all: generate test check fmt build
all: generate test check fmt build helm-lint

generate: generate-mocks

Expand Down Expand Up @@ -32,3 +32,6 @@ deadcode:

fmt:
go run mvdan.cc/gofumpt@latest -w ./

helm-lint:
helm lint --strict ./charts
17 changes: 15 additions & 2 deletions charts/Feature.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,25 @@ values:
template: |
{{ .Env.hookd_provision_key | quote }}
google.serviceAccountEmail:
displayName: Google service account email
serviceAccount.email:
displayName: Email address of the GCP service account
description: The email address of the service account that is used when using the Google APIs
computed:
template: |
{{ .Env.teams_google_service_account_email | quote }}
google.adminServiceAccountEmail:
displayName: Email address of the Google Workspace Admin service account
description: The email address of the service account that is used when using the Google Workspace Admin APIs
config:
type: string

google.adminUserEmail:
displayName: Email address of the admin user for Google Workspace
description: The email address of the user that is impersonated when using the Google Workspace Admin APIs
config:
type: string

gcp.billingAccount:
displayName: Billing account
computed:
Expand Down
7 changes: 7 additions & 0 deletions charts/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,18 @@ spec:
value: {{ .Values.gcp.cnrmRole | quote }}
- name: GCP_WORKLOAD_IDENTITY_POOL_NAME
value: {{ .Values.gcp.workloadIdentityPoolName | quote }}
- name: GCP_SERVICE_ACCOUNT_EMAIL
value: {{ .Values.serviceAccount.email | quote }}
# DependencyTrack
- name: DEPENDENCYTRACK_ENDPOINT
value: {{ .Values.dependencyTrack.endpoint | quote }}
- name: DEPENDENCYTRACK_USERNAME
value: {{ .Values.dependencyTrack.username | quote }}
# Google
- name: GOOGLE_ADMIN_SERVICE_ACCOUNT_EMAIL
value: {{ .Values.google.adminServiceAccountEmail | default (printf "nais-admin@%s.iam.gserviceaccount.com" .Values.googleManagementProjectID) | quote }}
- name: GOOGLE_ADMIN_USER_EMAIL
value: {{ .Values.google.adminUserEmail | default (printf "nais-admin@%s" .Values.tenantDomain) | quote }}

ports:
- name: http
Expand Down
2 changes: 1 addition & 1 deletion charts/templates/sa.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ metadata:
labels:
{{- include "api-reconcilers.labels" . | nindent 4 }}
annotations:
iam.gke.io/gcp-service-account: {{ .Values.google.serviceAccountEmail }}
iam.gke.io/gcp-service-account: {{ .Values.serviceAccount.email }}
6 changes: 5 additions & 1 deletion charts/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@ naisDeploy:
endpoint: http://hookd/internal/api/v1/provision
provisionKey: # mapped in fasit

serviceAccount:
email: null

google:
serviceAccountEmail: # mapped in fasit
adminServiceAccountEmail: null
adminUserEmail: null

gcp:
billingAccount: # mapped in fasit
Expand Down
11 changes: 11 additions & 0 deletions internal/cmd/reconciler/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ type Config struct {
// Example: `organizations/<org_id>/roles/CustomCNRMRole`, where `<org_id>` is a numeric ID.
CnrmRole string `env:"GCP_CNRM_ROLE"`

// ServiceAccountEmail The email address to impersonate when using Google APIs
ServiceAccountEmail string `env:"GCP_SERVICE_ACCOUNT_EMAIL"`

// WorkloadIdentityPoolName The name of the workload identity pool used in the management project.
//
// Example: projects/{project_number}/locations/global/workloadIdentityPools/{workload_identity_pool_id}
Expand Down Expand Up @@ -77,6 +80,14 @@ type Config struct {
ProjectID string `env:"PUBSUB_PROJECT_ID,default=$GOOGLE_MANAGEMENT_PROJECT_ID"`
}

Google struct {
// AdminServiceAccountEmail The email address of the service account to impersonate when using Google Workspace Admin APIs
AdminServiceAccountEmail string `env:"GOOGLE_ADMIN_SERVICE_ACCOUNT_EMAIL"`

// AdminUserEmail The email address to impersonate during Google Admin Workspace operations.
AdminUserEmail string `env:"GOOGLE_ADMIN_USER_EMAIL"`
}

// GoogleManagementProjectID The ID of the NAIS management project in the tenant organization in GCP.
GoogleManagementProjectID string `env:"GOOGLE_MANAGEMENT_PROJECT_ID"`

Expand Down
13 changes: 7 additions & 6 deletions internal/cmd/reconciler/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,12 @@ func run(ctx context.Context, cfg *Config, log logrus.FieldLogger) error {

azureGroupReconciler := azure_group_reconciler.New(ctx, cfg.TenantDomain, cfg.Azure.GroupNamePrefix)

githubReconciler, err := github_team_reconciler.New(ctx, cfg.GitHub.Organization, cfg.GitHub.AuthEndpoint, cfg.GoogleManagementProjectID)
githubReconciler, err := github_team_reconciler.New(ctx, cfg.GitHub.Organization, cfg.GitHub.AuthEndpoint, cfg.GCP.ServiceAccountEmail)
if err != nil {
return err
}

googleWorkspaceAdminReconciler, err := google_workspace_admin_reconciler.New(ctx, cfg.GoogleManagementProjectID, cfg.TenantDomain)
googleWorkspaceAdminReconciler, err := google_workspace_admin_reconciler.New(ctx, cfg.Google.AdminServiceAccountEmail, cfg.Google.AdminUserEmail, cfg.TenantDomain)
if err != nil {
return err
}
Expand All @@ -114,17 +114,17 @@ func run(ctx context.Context, cfg *Config, log logrus.FieldLogger) error {
return err
}

googleGcpReconciler, err := google_gcp_reconciler.New(ctx, cfg.GCP.Clusters, cfg.GoogleManagementProjectID, cfg.TenantDomain, cfg.TenantName, cfg.GCP.CnrmRole, cfg.GCP.BillingAccount)
googleGcpReconciler, err := google_gcp_reconciler.New(ctx, cfg.GCP.Clusters, cfg.GCP.ServiceAccountEmail, cfg.TenantDomain, cfg.TenantName, cfg.GCP.CnrmRole, cfg.GCP.BillingAccount)
if err != nil {
return err
}

garReconciler, err := google_gar_reconciler.New(ctx, cfg.GoogleManagementProjectID, cfg.TenantDomain, cfg.GCP.WorkloadIdentityPoolName)
garReconciler, err := google_gar_reconciler.New(ctx, cfg.GCP.ServiceAccountEmail, cfg.GoogleManagementProjectID, cfg.GCP.WorkloadIdentityPoolName)
if err != nil {
return err
}

namespaceReconciler, err := nais_namespace_reconciler.New(ctx, cfg.TenantDomain, cfg.GoogleManagementProjectID)
namespaceReconciler, err := nais_namespace_reconciler.New(ctx, cfg.GCP.ServiceAccountEmail, cfg.TenantDomain, cfg.GoogleManagementProjectID)
if err != nil {
return err
}
Expand All @@ -133,7 +133,8 @@ func run(ctx context.Context, cfg *Config, log logrus.FieldLogger) error {
if err != nil {
log.WithField("reconciler", "dependencytrack").WithError(err).Errorf("error when creating reconciler")
}
cdnReconciler, err := google_cdn_reconciler.New(ctx, cfg.GoogleManagementProjectID, cfg.TenantDomain, cfg.TenantName, cfg.GCP.WorkloadIdentityPoolName)

cdnReconciler, err := google_cdn_reconciler.New(ctx, cfg.GCP.ServiceAccountEmail, cfg.GoogleManagementProjectID, cfg.TenantName, cfg.GCP.WorkloadIdentityPoolName)
if err != nil {
log.WithField("reconciler", "cdn").WithError(err).Errorf("error when creating reconciler")
}
Expand Down
54 changes: 14 additions & 40 deletions internal/google_token_source/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,29 @@ package google_token_source

import (
"context"
"fmt"

"golang.org/x/oauth2"
admin_directory "google.golang.org/api/admin/directory/v1"
"google.golang.org/api/cloudresourcemanager/v3"
"google.golang.org/api/impersonate"
)

type Builder struct {
serviceAccountEmail string
subjectEmail string
}

func New(googleManagementProjectID, tenantDomain string) (*Builder, error) {
if googleManagementProjectID == "" {
return nil, fmt.Errorf("googleManagementProjectID can not be empty")
}

if tenantDomain == "" {
return nil, fmt.Errorf("tenantDomain can not be empty")
}

return &Builder{
serviceAccountEmail: "console@" + googleManagementProjectID + ".iam.gserviceaccount.com",
subjectEmail: "nais-console@" + tenantDomain,
}, nil
}

func (g Builder) impersonateTokenSource(ctx context.Context, delegate bool, scopes []string) (oauth2.TokenSource, error) {
impersonateConfig := impersonate.CredentialsConfig{
TargetPrincipal: g.serviceAccountEmail,
Scopes: scopes,
}
if delegate {
impersonateConfig.Subject = g.subjectEmail
}

return impersonate.CredentialsTokenSource(ctx, impersonateConfig)
}

func (g Builder) Admin(ctx context.Context) (oauth2.TokenSource, error) {
return g.impersonateTokenSource(ctx, true, []string{
admin_directory.AdminDirectoryUserReadonlyScope,
admin_directory.AdminDirectoryGroupScope,
func WorkspaceAdminTokenSource(ctx context.Context, adminServiceAccountEmail, adminUserEmail string) (oauth2.TokenSource, error) {
return impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{
Scopes: []string{
admin_directory.AdminDirectoryUserReadonlyScope,
admin_directory.AdminDirectoryGroupScope,
},
Subject: adminUserEmail,
TargetPrincipal: adminServiceAccountEmail,
})
}

func (g Builder) GCP(ctx context.Context) (oauth2.TokenSource, error) {
return g.impersonateTokenSource(ctx, false, []string{
cloudresourcemanager.CloudPlatformScope,
func GcpTokenSource(ctx context.Context, serviceAccountEmail string) (oauth2.TokenSource, error) {
return impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{
Scopes: []string{
cloudresourcemanager.CloudPlatformScope,
},
TargetPrincipal: serviceAccountEmail,
})
}
4 changes: 2 additions & 2 deletions internal/reconcilers/github/team/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func WithGraphClient(graphClient GraphClient) OptFunc {
}
}

func New(ctx context.Context, org, authEndpoint, googleManagementProjectID string, opts ...OptFunc) (reconcilers.Reconciler, error) {
func New(ctx context.Context, org, authEndpoint, serviceAccountEmail string, opts ...OptFunc) (reconcilers.Reconciler, error) {
r := &githubTeamReconciler{
org: org,
}
Expand All @@ -58,7 +58,7 @@ func New(ctx context.Context, org, authEndpoint, googleManagementProjectID strin
if r.teamsService == nil || r.graphClient == nil {
ts, err := impersonate.IDTokenSource(ctx, impersonate.IDTokenConfig{
Audience: authEndpoint,
TargetPrincipal: fmt.Sprintf("console@%s.iam.gserviceaccount.com", googleManagementProjectID),
TargetPrincipal: serviceAccountEmail,
})
if err != nil {
return nil, err
Expand Down
Loading

0 comments on commit db054e0

Please sign in to comment.