Skip to content

Commit

Permalink
[library usage] Implement library token provider (#380)
Browse files Browse the repository at this point in the history
* feat: define types

* refactor: move environment variables to `internal/env`

* feat: implement OptionsWithEnv

* feat: convert library options to internal options

* feat: implement library token provider

* fix: drop ROPCLogin from library usage for now

* doc: typo
  • Loading branch information
bcho committed Dec 20, 2023
1 parent 7564350 commit 30ead78
Show file tree
Hide file tree
Showing 10 changed files with 473 additions and 84 deletions.
30 changes: 30 additions & 0 deletions pkg/internal/env/variables.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package env

const (
// env vars
LoginMethod = "AAD_LOGIN_METHOD"
KubeloginROPCUsername = "AAD_USER_PRINCIPAL_NAME"
KubeloginROPCPassword = "AAD_USER_PRINCIPAL_PASSWORD"
KubeloginClientID = "AAD_SERVICE_PRINCIPAL_CLIENT_ID"
KubeloginClientSecret = "AAD_SERVICE_PRINCIPAL_CLIENT_SECRET"
KubeloginClientCertificatePath = "AAD_SERVICE_PRINCIPAL_CLIENT_CERTIFICATE"
KubeloginClientCertificatePassword = "AAD_SERVICE_PRINCIPAL_CLIENT_CERTIFICATE_PASSWORD"

// env vars used by Terraform
TerraformClientID = "ARM_CLIENT_ID"
TerraformClientSecret = "ARM_CLIENT_SECRET"
TerraformClientCertificatePath = "ARM_CLIENT_CERTIFICATE_PATH"
TerraformClientCertificatePassword = "ARM_CLIENT_CERTIFICATE_PASSWORD"
TerraformTenantID = "ARM_TENANT_ID"

// env vars following azure sdk naming convention
AzureAuthorityHost = "AZURE_AUTHORITY_HOST"
AzureClientCertificatePassword = "AZURE_CLIENT_CERTIFICATE_PASSWORD"
AzureClientCertificatePath = "AZURE_CLIENT_CERTIFICATE_PATH"
AzureClientID = "AZURE_CLIENT_ID"
AzureClientSecret = "AZURE_CLIENT_SECRET"
AzureFederatedTokenFile = "AZURE_FEDERATED_TOKEN_FILE"
AzureTenantID = "AZURE_TENANT_ID"
AzureUsername = "AZURE_USERNAME"
AzurePassword = "AZURE_PASSWORD"
)
92 changes: 33 additions & 59 deletions pkg/internal/token/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"
"time"

"github.com/Azure/kubelogin/pkg/internal/env"
"github.com/spf13/pflag"
"k8s.io/client-go/util/homedir"
)
Expand Down Expand Up @@ -45,33 +46,6 @@ const (
AzureCLILogin = "azurecli"
WorkloadIdentityLogin = "workloadidentity"
manualTokenLogin = "manual_token"

// env vars
loginMethod = "AAD_LOGIN_METHOD"
kubeloginROPCUsername = "AAD_USER_PRINCIPAL_NAME"
kubeloginROPCPassword = "AAD_USER_PRINCIPAL_PASSWORD"
kubeloginClientID = "AAD_SERVICE_PRINCIPAL_CLIENT_ID"
kubeloginClientSecret = "AAD_SERVICE_PRINCIPAL_CLIENT_SECRET"
kubeloginClientCertificatePath = "AAD_SERVICE_PRINCIPAL_CLIENT_CERTIFICATE"
kubeloginClientCertificatePassword = "AAD_SERVICE_PRINCIPAL_CLIENT_CERTIFICATE_PASSWORD"

// env vars used by Terraform
terraformClientID = "ARM_CLIENT_ID"
terraformClientSecret = "ARM_CLIENT_SECRET"
terraformClientCertificatePath = "ARM_CLIENT_CERTIFICATE_PATH"
terraformClientCertificatePassword = "ARM_CLIENT_CERTIFICATE_PASSWORD"
terraformTenantID = "ARM_TENANT_ID"

// env vars following azure sdk naming convention
azureAuthorityHost = "AZURE_AUTHORITY_HOST"
azureClientCertificatePassword = "AZURE_CLIENT_CERTIFICATE_PASSWORD"
azureClientCertificatePath = "AZURE_CLIENT_CERTIFICATE_PATH"
azureClientID = "AZURE_CLIENT_ID"
azureClientSecret = "AZURE_CLIENT_SECRET"
azureFederatedTokenFile = "AZURE_FEDERATED_TOKEN_FILE"
azureTenantID = "AZURE_TENANT_ID"
azureUsername = "AZURE_USERNAME"
azurePassword = "AZURE_PASSWORD"
)

var (
Expand All @@ -97,27 +71,27 @@ func NewOptions() Options {

func (o *Options) AddFlags(fs *pflag.FlagSet) {
fs.StringVarP(&o.LoginMethod, "login", "l", o.LoginMethod,
fmt.Sprintf("Login method. Supported methods: %s. It may be specified in %s environment variable", GetSupportedLogins(), loginMethod))
fmt.Sprintf("Login method. Supported methods: %s. It may be specified in %s environment variable", GetSupportedLogins(), env.LoginMethod))
fs.StringVar(&o.ClientID, "client-id", o.ClientID,
fmt.Sprintf("AAD client application ID. It may be specified in %s or %s environment variable", kubeloginClientID, azureClientID))
fmt.Sprintf("AAD client application ID. It may be specified in %s or %s environment variable", env.KubeloginClientID, env.AzureClientID))
fs.StringVar(&o.ClientSecret, "client-secret", o.ClientSecret,
fmt.Sprintf("AAD client application secret. Used in spn login. It may be specified in %s or %s environment variable", kubeloginClientSecret, azureClientSecret))
fmt.Sprintf("AAD client application secret. Used in spn login. It may be specified in %s or %s environment variable", env.KubeloginClientSecret, env.AzureClientSecret))
fs.StringVar(&o.ClientCert, "client-certificate", o.ClientCert,
fmt.Sprintf("AAD client cert in pfx. Used in spn login. It may be specified in %s or %s environment variable", kubeloginClientCertificatePath, azureClientCertificatePath))
fmt.Sprintf("AAD client cert in pfx. Used in spn login. It may be specified in %s or %s environment variable", env.KubeloginClientCertificatePath, env.AzureClientCertificatePath))
fs.StringVar(&o.ClientCertPassword, "client-certificate-password", o.ClientCertPassword,
fmt.Sprintf("Password for AAD client cert. Used in spn login. It may be specified in %s or %s environment variable", kubeloginClientCertificatePassword, azureClientCertificatePassword))
fmt.Sprintf("Password for AAD client cert. Used in spn login. It may be specified in %s or %s environment variable", env.KubeloginClientCertificatePassword, env.AzureClientCertificatePassword))
fs.StringVar(&o.Username, "username", o.Username,
fmt.Sprintf("user name for ropc login flow. It may be specified in %s or %s environment variable", kubeloginROPCUsername, azureUsername))
fmt.Sprintf("user name for ropc login flow. It may be specified in %s or %s environment variable", env.KubeloginROPCUsername, env.AzureUsername))
fs.StringVar(&o.Password, "password", o.Password,
fmt.Sprintf("password for ropc login flow. It may be specified in %s or %s environment variable", kubeloginROPCPassword, azurePassword))
fmt.Sprintf("password for ropc login flow. It may be specified in %s or %s environment variable", env.KubeloginROPCPassword, env.AzurePassword))
fs.StringVar(&o.IdentityResourceID, "identity-resource-id", o.IdentityResourceID, "Managed Identity resource id.")
fs.StringVar(&o.ServerID, "server-id", o.ServerID, "AAD server application ID")
fs.StringVar(&o.FederatedTokenFile, "federated-token-file", o.FederatedTokenFile,
fmt.Sprintf("Workload Identity federated token file. It may be specified in %s environment variable", azureFederatedTokenFile))
fmt.Sprintf("Workload Identity federated token file. It may be specified in %s environment variable", env.AzureFederatedTokenFile))
fs.StringVar(&o.AuthorityHost, "authority-host", o.AuthorityHost,
fmt.Sprintf("Workload Identity authority host. It may be specified in %s environment variable", azureAuthorityHost))
fmt.Sprintf("Workload Identity authority host. It may be specified in %s environment variable", env.AzureAuthorityHost))
fs.StringVar(&o.TokenCacheDir, "token-cache-dir", o.TokenCacheDir, "directory to cache token")
fs.StringVarP(&o.TenantID, "tenant-id", "t", o.TenantID, fmt.Sprintf("AAD tenant ID. It may be specified in %s environment variable", azureTenantID))
fs.StringVarP(&o.TenantID, "tenant-id", "t", o.TenantID, fmt.Sprintf("AAD tenant ID. It may be specified in %s environment variable", env.AzureTenantID))
fs.StringVarP(&o.Environment, "environment", "e", o.Environment, "Azure environment name")
fs.BoolVar(&o.IsLegacy, "legacy", o.IsLegacy, "set to true to get token with 'spn:' prefix in audience claim")
fs.BoolVar(&o.UseAzureRMTerraformEnv, "use-azurerm-env-vars", o.UseAzureRMTerraformEnv,
Expand Down Expand Up @@ -160,75 +134,75 @@ func (o *Options) UpdateFromEnv() {
o.tokenCacheFile = getCacheFileName(o)

if o.UseAzureRMTerraformEnv {
if v, ok := os.LookupEnv(terraformClientID); ok {
if v, ok := os.LookupEnv(env.TerraformClientID); ok {
o.ClientID = v
}
if v, ok := os.LookupEnv(terraformClientSecret); ok {
if v, ok := os.LookupEnv(env.TerraformClientSecret); ok {
o.ClientSecret = v
}
if v, ok := os.LookupEnv(terraformClientCertificatePath); ok {
if v, ok := os.LookupEnv(env.TerraformClientCertificatePath); ok {
o.ClientCert = v
}
if v, ok := os.LookupEnv(terraformClientCertificatePassword); ok {
if v, ok := os.LookupEnv(env.TerraformClientCertificatePassword); ok {
o.ClientCertPassword = v
}
if v, ok := os.LookupEnv(terraformTenantID); ok {
if v, ok := os.LookupEnv(env.TerraformTenantID); ok {
o.TenantID = v
}
} else {
if v, ok := os.LookupEnv(kubeloginClientID); ok {
if v, ok := os.LookupEnv(env.KubeloginClientID); ok {
o.ClientID = v
}
if v, ok := os.LookupEnv(azureClientID); ok {
if v, ok := os.LookupEnv(env.AzureClientID); ok {
o.ClientID = v
}
if v, ok := os.LookupEnv(kubeloginClientSecret); ok {
if v, ok := os.LookupEnv(env.KubeloginClientSecret); ok {
o.ClientSecret = v
}
if v, ok := os.LookupEnv(azureClientSecret); ok {
if v, ok := os.LookupEnv(env.AzureClientSecret); ok {
o.ClientSecret = v
}
if v, ok := os.LookupEnv(kubeloginClientCertificatePath); ok {
if v, ok := os.LookupEnv(env.KubeloginClientCertificatePath); ok {
o.ClientCert = v
}
if v, ok := os.LookupEnv(azureClientCertificatePath); ok {
if v, ok := os.LookupEnv(env.AzureClientCertificatePath); ok {
o.ClientCert = v
}
if v, ok := os.LookupEnv(kubeloginClientCertificatePassword); ok {
if v, ok := os.LookupEnv(env.KubeloginClientCertificatePassword); ok {
o.ClientCertPassword = v
}
if v, ok := os.LookupEnv(azureClientCertificatePassword); ok {
if v, ok := os.LookupEnv(env.AzureClientCertificatePassword); ok {
o.ClientCertPassword = v
}
if v, ok := os.LookupEnv(azureTenantID); ok {
if v, ok := os.LookupEnv(env.AzureTenantID); ok {
o.TenantID = v
}
}

if v, ok := os.LookupEnv(kubeloginROPCUsername); ok {
if v, ok := os.LookupEnv(env.KubeloginROPCUsername); ok {
o.Username = v
}
if v, ok := os.LookupEnv(azureUsername); ok {
if v, ok := os.LookupEnv(env.AzureUsername); ok {
o.Username = v
}
if v, ok := os.LookupEnv(kubeloginROPCPassword); ok {
if v, ok := os.LookupEnv(env.KubeloginROPCPassword); ok {
o.Password = v
}
if v, ok := os.LookupEnv(azurePassword); ok {
if v, ok := os.LookupEnv(env.AzurePassword); ok {
o.Password = v
}
if v, ok := os.LookupEnv(loginMethod); ok {
if v, ok := os.LookupEnv(env.LoginMethod); ok {
o.LoginMethod = v
}

if o.LoginMethod == WorkloadIdentityLogin {
if v, ok := os.LookupEnv(azureClientID); ok {
if v, ok := os.LookupEnv(env.AzureClientID); ok {
o.ClientID = v
}
if v, ok := os.LookupEnv(azureFederatedTokenFile); ok {
if v, ok := os.LookupEnv(env.AzureFederatedTokenFile); ok {
o.FederatedTokenFile = v
}
if v, ok := os.LookupEnv(azureAuthorityHost); ok {
if v, ok := os.LookupEnv(env.AzureAuthorityHost); ok {
o.AuthorityHost = v
}
}
Expand Down
49 changes: 25 additions & 24 deletions pkg/internal/token/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"
"time"

"github.com/Azure/kubelogin/pkg/internal/env"
"github.com/Azure/kubelogin/pkg/internal/testutils"
"github.com/google/go-cmp/cmp"
"github.com/spf13/pflag"
Expand Down Expand Up @@ -86,14 +87,14 @@ func TestOptionsWithEnvVars(t *testing.T) {
{
name: "setting env var using legacy env var format",
envVarMap: map[string]string{
kubeloginClientID: clientID,
kubeloginClientSecret: clientSecret,
kubeloginClientCertificatePath: certPath,
kubeloginClientCertificatePassword: certPassword,
kubeloginROPCUsername: username,
kubeloginROPCPassword: password,
azureTenantID: tenantID,
loginMethod: DeviceCodeLogin,
env.KubeloginClientID: clientID,
env.KubeloginClientSecret: clientSecret,
env.KubeloginClientCertificatePath: certPath,
env.KubeloginClientCertificatePassword: certPassword,
env.KubeloginROPCUsername: username,
env.KubeloginROPCPassword: password,
env.AzureTenantID: tenantID,
env.LoginMethod: DeviceCodeLogin,
},
expected: Options{
ClientID: clientID,
Expand All @@ -112,12 +113,12 @@ func TestOptionsWithEnvVars(t *testing.T) {
name: "setting env var using terraform env var format",
isTerraform: true,
envVarMap: map[string]string{
terraformClientID: clientID,
terraformClientSecret: clientSecret,
terraformClientCertificatePath: certPath,
terraformClientCertificatePassword: certPassword,
terraformTenantID: tenantID,
loginMethod: DeviceCodeLogin,
env.TerraformClientID: clientID,
env.TerraformClientSecret: clientSecret,
env.TerraformClientCertificatePath: certPath,
env.TerraformClientCertificatePassword: certPassword,
env.TerraformTenantID: tenantID,
env.LoginMethod: DeviceCodeLogin,
},
expected: Options{
UseAzureRMTerraformEnv: true,
Expand All @@ -134,16 +135,16 @@ func TestOptionsWithEnvVars(t *testing.T) {
{
name: "setting env var using azure sdk env var format",
envVarMap: map[string]string{
azureClientID: clientID,
azureClientSecret: clientSecret,
azureClientCertificatePath: certPath,
azureClientCertificatePassword: certPassword,
azureUsername: username,
azurePassword: password,
azureTenantID: tenantID,
loginMethod: WorkloadIdentityLogin,
azureFederatedTokenFile: tokenFile,
azureAuthorityHost: authorityHost,
env.AzureClientID: clientID,
env.AzureClientSecret: clientSecret,
env.AzureClientCertificatePath: certPath,
env.AzureClientCertificatePassword: certPassword,
env.AzureUsername: username,
env.AzurePassword: password,
env.AzureTenantID: tenantID,
env.LoginMethod: WorkloadIdentityLogin,
env.AzureFederatedTokenFile: tokenFile,
env.AzureAuthorityHost: authorityHost,
},
expected: Options{
ClientID: clientID,
Expand Down
42 changes: 42 additions & 0 deletions pkg/token/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package token

import "github.com/Azure/kubelogin/pkg/internal/token"

// list of supported login methods for library consumers

const (
ServicePrincipalLogin = token.ServicePrincipalLogin
MSILogin = token.MSILogin
WorkloadIdentityLogin = token.WorkloadIdentityLogin
)

// Options defines the options for getting token.
// This struct is a subset of internal/token.Options where its values are copied
// to internal type. See internal/token/options.go for details
type Options struct {
LoginMethod string

// shared login settings

Environment string
TenantID string
ServerID string
ClientID string

// for ServicePrincipalLogin

ClientSecret string
ClientCert string
ClientCertPassword string
IsPoPTokenEnabled bool
PoPTokenClaims string

// for MSILogin

IdentityResourceID string

// for WorkloadIdentityLogin

AuthorityHost string
FederatedTokenFile string
}
57 changes: 57 additions & 0 deletions pkg/token/options_ctor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package token

import (
"os"

"github.com/Azure/kubelogin/pkg/internal/env"
"github.com/Azure/kubelogin/pkg/internal/token"
)

// OptionsWithEnv loads options from environment variables.
func OptionsWithEnv() *Options {
// initial default values
rv := &Options{
LoginMethod: os.Getenv(env.LoginMethod),
TenantID: os.Getenv(env.AzureTenantID),
ClientID: os.Getenv(env.KubeloginClientID),
ClientSecret: os.Getenv(env.KubeloginClientSecret),
ClientCert: os.Getenv(env.KubeloginClientCertificatePath),
ClientCertPassword: os.Getenv(env.KubeloginClientCertificatePassword),
AuthorityHost: os.Getenv(env.AzureAuthorityHost),
FederatedTokenFile: os.Getenv(env.AzureFederatedTokenFile),
}

// azure variant overrides
if v, ok := os.LookupEnv(env.AzureClientID); ok {
rv.ClientID = v
}
if v, ok := os.LookupEnv(env.AzureClientSecret); ok {
rv.ClientSecret = v
}
if v, ok := os.LookupEnv(env.AzureClientCertificatePath); ok {
rv.ClientCert = v
}
if v, ok := os.LookupEnv(env.AzureClientCertificatePassword); ok {
rv.ClientCertPassword = v
}

return rv
}

func (opts *Options) toInternalOptions() *token.Options {
return &token.Options{
LoginMethod: opts.LoginMethod,
Environment: opts.Environment,
TenantID: opts.TenantID,
ServerID: opts.ServerID,
ClientID: opts.ClientID,
ClientSecret: opts.ClientSecret,
ClientCert: opts.ClientCert,
ClientCertPassword: opts.ClientCertPassword,
IsPoPTokenEnabled: opts.IsPoPTokenEnabled,
PoPTokenClaims: opts.PoPTokenClaims,
IdentityResourceID: opts.IdentityResourceID,
AuthorityHost: opts.AuthorityHost,
FederatedTokenFile: opts.FederatedTokenFile,
}
}
Loading

0 comments on commit 30ead78

Please sign in to comment.