Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VAULT-14644 Add support for Azure WIF auth to auto-auth (for Agent and Proxy) #22264

Merged
merged 11 commits into from
Aug 10, 2023
3 changes: 3 additions & 0 deletions changelog/22264.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
auto-auth/azure: Added Azure Workload Identity Federation support to auto-auth (for Vault Agent and Vault Proxy).
```
116 changes: 93 additions & 23 deletions command/agentproxyshared/auth/azure/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import (
"context"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"

policy "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
az "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
cleanhttp "github.com/hashicorp/go-cleanhttp"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/api"
Expand All @@ -31,10 +33,12 @@ type azureMethod struct {
logger hclog.Logger
mountPath string

role string
resource string
objectID string
clientID string
authenticateFromEnvironment bool
role string
scope string
resource string
objectID string
clientID string
}

func NewAzureAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) {
Expand Down Expand Up @@ -84,6 +88,25 @@ func NewAzureAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) {
}
}

scopeRaw, ok := conf.Config["scope"]
if ok {
a.scope, ok = scopeRaw.(string)
if !ok {
return nil, errors.New("could not convert 'scope' config value to string")
}
}
if a.scope == "" {
a.scope = fmt.Sprintf("%s/.default", a.resource)
}

authenticateFromEnvironmentRaw, ok := conf.Config["authenticate_from_environment"]
if ok {
a.authenticateFromEnvironment, ok = authenticateFromEnvironmentRaw.(bool)
if !ok {
return nil, errors.New("could not convert 'authenticate_from_environment' config value to bool")
}
}

switch {
case a.role == "":
return nil, errors.New("'role' value is empty")
Expand All @@ -106,10 +129,11 @@ func (a *azureMethod) Authenticate(ctx context.Context, client *api.Client) (ret
ResourceGroupName string
SubscriptionID string
VMScaleSetName string
ResourceID string
}
}

body, err := getMetadataInfo(ctx, instanceEndpoint, "", "", "")
body, err := getInstanceMetadataInfo(ctx)
if err != nil {
retErr = err
return
Expand All @@ -121,21 +145,19 @@ func (a *azureMethod) Authenticate(ctx context.Context, client *api.Client) (ret
return
}

// Fetch JWT
var identity struct {
AccessToken string `json:"access_token"`
}

body, err = getMetadataInfo(ctx, identityEndpoint, a.resource, a.objectID, a.clientID)
if err != nil {
retErr = err
return
}

err = jsonutil.DecodeJSON(body, &identity)
if err != nil {
retErr = fmt.Errorf("error parsing identity metadata response: %w", err)
return
token := ""
if a.authenticateFromEnvironment {
token, err = getAzureTokenFromEnvironment(ctx, a.scope)
if err != nil {
retErr = err
return
}
} else {
token, err = getTokenFromIdentityEndpoint(ctx, a.resource, a.objectID, a.clientID)
if err != nil {
retErr = err
return
}
}

// Attempt login
Expand All @@ -145,7 +167,7 @@ func (a *azureMethod) Authenticate(ctx context.Context, client *api.Client) (ret
"vmss_name": instance.Compute.VMScaleSetName,
"resource_group_name": instance.Compute.ResourceGroupName,
"subscription_id": instance.Compute.SubscriptionID,
"jwt": identity.AccessToken,
"jwt": token,
}

return fmt.Sprintf("%s/login", a.mountPath), nil, data, nil
Expand All @@ -161,6 +183,54 @@ func (a *azureMethod) CredSuccess() {
func (a *azureMethod) Shutdown() {
}

// getAzureTokenFromEnvironment Is Azure's preferred way for authentication, and takes values
// from environment variables to form a credential.
// It uses a DefaultAzureCredential:
// https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#readme-defaultazurecredential
// Environment variables are taken into account in the following order:
// https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#readme-environment-variables
func getAzureTokenFromEnvironment(ctx context.Context, scope string) (string, error) {
cred, err := az.NewDefaultAzureCredential(nil)
if err != nil {
return "", err
}

tokenOpts := policy.TokenRequestOptions{Scopes: []string{scope}}
tk, err := cred.GetToken(ctx, tokenOpts)
if err != nil {
return "", err
}
return tk.Token, nil
}

// getInstanceMetadataInfo calls the Azure Instance Metadata endpoint to get
// information about the Azure environment it's running in.
func getInstanceMetadataInfo(ctx context.Context) ([]byte, error) {
VioletHynes marked this conversation as resolved.
Show resolved Hide resolved
return getMetadataInfo(ctx, instanceEndpoint, "", "", "")
}

// getTokenFromIdentityEndpoint is kept for backwards compatibility purposes. Using the
// newer APIs and the Azure SDK should be preferred over this mechanism.
Comment on lines +212 to +213
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// getTokenFromIdentityEndpoint is kept for backwards compatibility purposes. Using the
// newer APIs and the Azure SDK should be preferred over this mechanism.
// getTokenFromIdentityEndpoint is kept for backwards compatibility purposes. Using the
// newer APIs and the Azure SDK should be preferred over this mechanism.
// Deprecated: use getAzureTokenFromEnvironment.

Should we mark this with deprecated to give folks a warning in their IDE?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know that it's deprecated, per se - it's the 'old'/'legacy' way of doing things but I don't think it's deprecated from a Vault-product standpoint or an Azure product standpoint.

func getTokenFromIdentityEndpoint(ctx context.Context, resource, objectID, clientID string) (string, error) {
var identity struct {
AccessToken string `json:"access_token"`
}

body, err := getMetadataInfo(ctx, identityEndpoint, resource, objectID, clientID)
if err != nil {
return "", err
}

err = jsonutil.DecodeJSON(body, &identity)
if err != nil {
return "", fmt.Errorf("error parsing identity metadata response: %w", err)
}

return identity.AccessToken, nil
}

// getMetadataInfo calls the Azure metadata endpoint with the given parameters.
// An empty resource, objectID and clientID will return metadata information.
func getMetadataInfo(ctx context.Context, endpoint, resource, objectID, clientID string) ([]byte, error) {
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
Expand Down Expand Up @@ -194,7 +264,7 @@ func getMetadataInfo(ctx context.Context, endpoint, resource, objectID, clientID
}

defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading metadata from %s: %w", endpoint, err)
}
Expand Down
35 changes: 23 additions & 12 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ require (
cloud.google.com/go/monitoring v1.13.0
cloud.google.com/go/spanner v1.45.0
cloud.google.com/go/storage v1.28.1
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0
github.com/Azure/azure-storage-blob-go v0.15.0
github.com/Azure/go-autorest/autorest v0.11.29
github.com/Azure/go-autorest/autorest/adal v0.9.22
Expand Down Expand Up @@ -122,7 +124,7 @@ require (
github.com/hashicorp/raft-boltdb/v2 v2.0.0-20210421194847-a7e34179d62c
github.com/hashicorp/raft-snapshot v1.0.4
github.com/hashicorp/vault-plugin-auth-alicloud v0.15.0
github.com/hashicorp/vault-plugin-auth-azure v0.15.1
github.com/hashicorp/vault-plugin-auth-azure v0.15.2-0.20230808174847-9dfb4f4a5ba7
github.com/hashicorp/vault-plugin-auth-centrify v0.15.1
github.com/hashicorp/vault-plugin-auth-cf v0.15.0
github.com/hashicorp/vault-plugin-auth-gcp v0.16.0
Expand Down Expand Up @@ -200,19 +202,19 @@ require (
go.etcd.io/etcd/client/v3 v3.5.7
go.mongodb.org/atlas v0.28.0
go.mongodb.org/mongo-driver v1.11.6
go.opentelemetry.io/otel v1.14.0
go.opentelemetry.io/otel v1.16.0
go.opentelemetry.io/otel/sdk v1.14.0
go.opentelemetry.io/otel/trace v1.14.0
go.opentelemetry.io/otel/trace v1.16.0
go.uber.org/atomic v1.11.0
go.uber.org/goleak v1.2.1
golang.org/x/crypto v0.9.0
golang.org/x/crypto v0.10.0
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
golang.org/x/net v0.10.0
golang.org/x/oauth2 v0.8.0
golang.org/x/net v0.11.0
golang.org/x/oauth2 v0.9.0
golang.org/x/sync v0.2.0
golang.org/x/sys v0.8.0
golang.org/x/term v0.8.0
golang.org/x/text v0.9.0
golang.org/x/sys v0.9.0
golang.org/x/term v0.9.0
golang.org/x/text v0.10.0
golang.org/x/tools v0.7.0
google.golang.org/api v0.124.0
google.golang.org/grpc v1.55.0
Expand All @@ -238,8 +240,6 @@ require (
github.com/99designs/keyring v1.2.2 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go v67.2.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.1.0 // indirect
Expand Down Expand Up @@ -298,6 +298,7 @@ require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible // indirect
github.com/circonus-labs/circonusllhist v0.1.3 // indirect
github.com/cjlapao/common-go v0.0.39 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/cloudfoundry-community/go-cfclient v0.0.0-20210823134051-721f0e559306 // indirect
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe // indirect
Expand Down Expand Up @@ -338,7 +339,7 @@ require (
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.4.1 // indirect
github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/analysis v0.20.0 // indirect
Expand Down Expand Up @@ -418,6 +419,14 @@ require (
github.com/mattn/go-ieproxy v0.0.1 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mediocregopher/radix/v4 v4.1.2 // indirect
github.com/microsoft/kiota-abstractions-go v1.1.0 // indirect
github.com/microsoft/kiota-authentication-azure-go v1.0.0 // indirect
github.com/microsoft/kiota-http-go v1.0.0 // indirect
github.com/microsoft/kiota-serialization-form-go v1.0.0 // indirect
github.com/microsoft/kiota-serialization-json-go v1.0.4 // indirect
github.com/microsoft/kiota-serialization-text-go v1.0.0 // indirect
github.com/microsoftgraph/msgraph-sdk-go v1.12.0 // indirect
github.com/microsoftgraph/msgraph-sdk-go-core v1.0.0 // indirect
github.com/miekg/dns v1.1.43 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/hashstructure v1.1.0 // indirect
Expand Down Expand Up @@ -477,12 +486,14 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
github.com/zclconf/go-cty v1.12.1 // indirect
go.etcd.io/etcd/api/v3 v3.5.7 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.19.1 // indirect
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect
Expand Down
Loading