Skip to content

Commit

Permalink
feat: add azure.client-assertion
Browse files Browse the repository at this point in the history
  • Loading branch information
FabianKramm committed Nov 20, 2023
1 parent c912079 commit e2ba92b
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 33 deletions.
2 changes: 1 addition & 1 deletion auth/providers/azure/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func New(ctx context.Context, opts Options) (auth.Interface, error) {

switch opts.AuthMode {
case ClientCredentialAuthMode:
c.graphClient, err = graph.New(c.ClientID, c.ClientSecret, c.TenantID, c.UseGroupUID, cachedAuthInfo.AADEndpoint, cachedAuthInfo.MSGraphHost)
c.graphClient, err = graph.New(c.ClientID, c.ClientSecret, c.ClientAssertion, c.TenantID, c.UseGroupUID, cachedAuthInfo.AADEndpoint, cachedAuthInfo.MSGraphHost)
case ARCAuthMode:
c.graphClient, err = graph.NewWithARC(c.ClientID, c.ResourceId, c.TenantID, c.AzureRegion)
case OBOAuthMode:
Expand Down
2 changes: 1 addition & 1 deletion auth/providers/azure/azure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func clientSetup(clientID, clientSecret, tenantID, serverUrl string, useGroupUID
ClientID: clientID,
})

c.graphClient, err = graph.TestUserInfo(clientID, clientSecret, serverUrl+"/login", serverUrl+"/api", useGroupUID)
c.graphClient, err = graph.TestUserInfo(clientID, clientSecret, "", serverUrl+"/login", serverUrl+"/api", useGroupUID)
if err != nil {
return nil, err
}
Expand Down
37 changes: 22 additions & 15 deletions auth/providers/azure/graph/clientcredential_tokenprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,26 @@ import (
)

type clientCredentialTokenProvider struct {
name string
client *http.Client
clientID string
clientSecret string
scope string
loginURL string
name string
client *http.Client
clientID string
clientSecret string
clientAssertion string
scope string
loginURL string
}

// NewClientCredentialTokenProvider returns a TokenProvider that implements OAuth client credential flow on Azure Active Directory
// https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#get-a-token
func NewClientCredentialTokenProvider(clientID, clientSecret, loginURL, scope string) TokenProvider {
func NewClientCredentialTokenProvider(clientID, clientSecret, clientAssertion, loginURL, scope string) TokenProvider {
return &clientCredentialTokenProvider{
name: "ClientCredentialTokenProvider",
client: httpclient.DefaultHTTPClient,
clientID: clientID,
clientSecret: clientSecret,
scope: scope,
loginURL: loginURL,
name: "ClientCredentialTokenProvider",
client: httpclient.DefaultHTTPClient,
clientID: clientID,
clientAssertion: clientAssertion,
clientSecret: clientSecret,
scope: scope,
loginURL: loginURL,
}
}

Expand All @@ -58,9 +60,14 @@ func (u *clientCredentialTokenProvider) Acquire(ctx context.Context, token strin
authResp := AuthResponse{}
form := url.Values{}
form.Set("client_id", u.clientID)
form.Set("client_secret", u.clientSecret)
form.Set("scope", u.scope)
if u.clientAssertion != "" {
form.Set("client_assertion", u.clientAssertion)
form.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
} else {
form.Set("client_secret", u.clientSecret)
}
form.Set("grant_type", "client_credentials")
form.Set("scope", u.scope)

req, err := http.NewRequest(http.MethodPost, u.loginURL, strings.NewReader(form.Encode()))
if err != nil {
Expand Down
8 changes: 4 additions & 4 deletions auth/providers/azure/graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,11 +407,11 @@ func newUserInfo(tokenProvider TokenProvider, graphURL *url.URL, useGroupUID boo
}

// New returns a new UserInfo object
func New(clientID, clientSecret, tenantID string, useGroupUID bool, aadEndpoint, msgraphHost string) (*UserInfo, error) {
func New(clientID, clientSecret, clientAssertion, tenantID string, useGroupUID bool, aadEndpoint, msgraphHost string) (*UserInfo, error) {
graphEndpoint := "https://" + msgraphHost + "/"
graphURL, _ := url.Parse(graphEndpoint + "v1.0")

tokenProvider := NewClientCredentialTokenProvider(clientID, clientSecret,
tokenProvider := NewClientCredentialTokenProvider(clientID, clientSecret, clientAssertion,
fmt.Sprintf("%s%s/oauth2/v2.0/token", aadEndpoint, tenantID),
fmt.Sprintf("https://%s/.default", msgraphHost))

Expand Down Expand Up @@ -456,7 +456,7 @@ func NewWithARC(msiAudience, resourceId, tenantId, region string) (*UserInfo, er
return userInfo, nil
}

func TestUserInfo(clientID, clientSecret, loginUrl, apiUrl string, useGroupUID bool) (*UserInfo, error) {
func TestUserInfo(clientID, clientSecret, clientAssertion, loginUrl, apiUrl string, useGroupUID bool) (*UserInfo, error) {
parsedApi, err := url.Parse(apiUrl)
if err != nil {
return nil, err
Expand All @@ -470,7 +470,7 @@ func TestUserInfo(clientID, clientSecret, loginUrl, apiUrl string, useGroupUID b
groupsPerCall: expandedGroupsPerCall,
useGroupUID: useGroupUID,
}
u.tokenProvider = NewClientCredentialTokenProvider(clientID, clientSecret, loginUrl, "")
u.tokenProvider = NewClientCredentialTokenProvider(clientID, clientSecret, clientAssertion, loginUrl, "")
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions auth/providers/azure/graph/graph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func getAuthServerAndUserInfo(returnCode int, body, clientID, clientSecret strin
headers: http.Header{},
groupsPerCall: expandedGroupsPerCall,
}
u.tokenProvider = NewClientCredentialTokenProvider(clientID, clientSecret, ts.URL, "")
u.tokenProvider = NewClientCredentialTokenProvider(clientID, clientSecret, "", ts.URL, "")
return ts, u
}

Expand Down Expand Up @@ -140,7 +140,7 @@ func TestLogin(t *testing.T) {
headers: http.Header{},
groupsPerCall: expandedGroupsPerCall,
}
u.tokenProvider = NewClientCredentialTokenProvider("CIA", "outcome", badURL, "")
u.tokenProvider = NewClientCredentialTokenProvider("CIA", "outcome", "", badURL, "")

err := u.RefreshToken(ctx, "")
if err == nil {
Expand Down
25 changes: 20 additions & 5 deletions auth/providers/azure/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type Options struct {
Environment string
ClientID string
ClientSecret string
ClientAssertion string
TenantID string
UseGroupUID bool
AuthMode string
Expand All @@ -60,15 +61,17 @@ type Options struct {

func NewOptions() Options {
return Options{
ClientSecret: os.Getenv("AZURE_CLIENT_SECRET"),
UseGroupUID: true,
ClientSecret: os.Getenv("AZURE_CLIENT_SECRET"),
ClientAssertion: os.Getenv("AZURE_CLIENT_ASSERTION"),
UseGroupUID: true,
}
}

func (o *Options) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&o.Environment, "azure.environment", o.Environment, "Azure cloud environment")
fs.StringVar(&o.ClientID, "azure.client-id", o.ClientID, "MS Graph application client ID to use")
fs.StringVar(&o.ClientSecret, "azure.client-secret", o.ClientSecret, "MS Graph application client secret to use")
fs.StringVar(&o.ClientAssertion, "azure.client-assertion", o.ClientAssertion, "MS Graph application client assertion (JWT) to use")
fs.StringVar(&o.TenantID, "azure.tenant-id", o.TenantID, "MS Graph application tenant id to use")
fs.BoolVar(&o.UseGroupUID, "azure.use-group-uid", o.UseGroupUID, "Use group UID for authentication instead of group display name")
fs.StringVar(&o.AuthMode, "azure.auth-mode", "client-credential", "auth mode to call graph api, valid value is either aks, arc, obo, client-credential or passthrough")
Expand Down Expand Up @@ -99,8 +102,8 @@ func (o *Options) Validate() []error {
}

if o.AuthMode != AKSAuthMode && o.AuthMode != PassthroughAuthMode && o.AuthMode != ARCAuthMode {
if o.ClientSecret == "" {
errs = append(errs, errors.New("azure.client-secret must be non-empty"))
if o.ClientSecret == "" && o.ClientAssertion == "" {
errs = append(errs, errors.New("azure.client-secret or azure.client-assertion must be non-empty"))
}
}
if o.AuthMode == AKSAuthMode && o.AKSTokenURL == "" {
Expand Down Expand Up @@ -156,7 +159,8 @@ func (o Options) Apply(d *apps.Deployment) (extraObjs []runtime.Object, err erro
Labels: d.Labels,
},
Data: map[string][]byte{
"client-secret": []byte(o.ClientSecret),
"client-secret": []byte(o.ClientSecret),
"client-assertion": []byte(o.ClientAssertion),
},
}
extraObjs = append(extraObjs, authSecret)
Expand Down Expand Up @@ -191,6 +195,17 @@ func (o Options) Apply(d *apps.Deployment) (extraObjs []runtime.Object, err erro
},
},
})
container.Env = append(container.Env, core.EnvVar{
Name: "AZURE_CLIENT_ASSERTION",
ValueFrom: &core.EnvVarSource{
SecretKeyRef: &core.SecretKeySelector{
LocalObjectReference: core.LocalObjectReference{
Name: authSecret.Name,
},
Key: "client-assertion",
},
},
})

args := container.Args
if o.Environment != "" {
Expand Down
4 changes: 2 additions & 2 deletions authz/providers/azure/rbac/rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ func New(opts authzOpts.Options, authopts auth.Options, authzInfo *AuthzInfo) (*
switch opts.AuthzMode {
case authzOpts.ARCAuthzMode:
// if client secret is there check use client credential provider
if authopts.ClientSecret != "" {
tokenProvider = graph.NewClientCredentialTokenProvider(authopts.ClientID, authopts.ClientSecret,
if authopts.ClientSecret != "" || authopts.ClientAssertion != "" {
tokenProvider = graph.NewClientCredentialTokenProvider(authopts.ClientID, authopts.ClientSecret, authopts.ClientAssertion,
fmt.Sprintf("%s%s/oauth2/v2.0/token", authzInfo.AADEndpoint, authopts.TenantID),
fmt.Sprintf("%s.default", authzInfo.ARMEndPoint))
} else {
Expand Down
2 changes: 1 addition & 1 deletion server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ func (s Server) ListenAndServe() {
klog.Fatalf("Authzmode %s is not supported for fetching list of resources", s.AuthzRecommendedOptions.Azure.AuthzMode)
}

err := azureutils.SetDiscoverResourcesSettings(clusterType, s.AuthRecommendedOptions.Azure.Environment, s.AuthzRecommendedOptions.Azure.AKSAuthzTokenURL, s.AuthzRecommendedOptions.Azure.KubeConfigFile, s.AuthRecommendedOptions.Azure.TenantID, s.AuthRecommendedOptions.Azure.ClientID, s.AuthRecommendedOptions.Azure.ClientSecret)
err := azureutils.SetDiscoverResourcesSettings(clusterType, s.AuthRecommendedOptions.Azure.Environment, s.AuthzRecommendedOptions.Azure.AKSAuthzTokenURL, s.AuthzRecommendedOptions.Azure.KubeConfigFile, s.AuthRecommendedOptions.Azure.TenantID, s.AuthRecommendedOptions.Azure.ClientID, s.AuthRecommendedOptions.Azure.ClientSecret, s.AuthRecommendedOptions.Azure.ClientAssertion)
if err != nil {
klog.Fatalf("Failed to create settings for discovering resources. Error:%s", err)
}
Expand Down
6 changes: 4 additions & 2 deletions util/azure/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ type DiscoverResourcesSettings struct {
tenantID string
clientID string
clientSecret string
clientAssertion string
}

type Display struct {
Expand Down Expand Up @@ -204,7 +205,7 @@ func ConvertIntToString(number int) string {
return strconv.Itoa(number)
}

func SetDiscoverResourcesSettings(clusterType string, environment string, loginURL string, kubeconfigFilePath string, tenantID string, clientID string, clientSecret string) error {
func SetDiscoverResourcesSettings(clusterType string, environment string, loginURL string, kubeconfigFilePath string, tenantID string, clientID string, clientSecret string, clientAssertion string) error {
if settings == nil {
settings = &DiscoverResourcesSettings{
clusterType: clusterType,
Expand All @@ -213,6 +214,7 @@ func SetDiscoverResourcesSettings(clusterType string, environment string, loginU
tenantID: tenantID,
clientID: clientID,
clientSecret: clientSecret,
clientAssertion: clientAssertion,
}

env := azure.PublicCloud
Expand Down Expand Up @@ -437,7 +439,7 @@ func fetchDataActionsList(ctx context.Context) ([]Operation, error) {

var token string
if settings.clusterType == ConnectedClusters {
tokenProvider := graph.NewClientCredentialTokenProvider(settings.clientID, settings.clientSecret,
tokenProvider := graph.NewClientCredentialTokenProvider(settings.clientID, settings.clientSecret, settings.clientAssertion,
fmt.Sprintf("%s%s/oauth2/v2.0/token", settings.environment.ActiveDirectoryEndpoint, settings.tenantID),
fmt.Sprintf("%s/.default", settings.environment.ResourceManagerEndpoint))

Expand Down

0 comments on commit e2ba92b

Please sign in to comment.