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

feat: rename TOKEN_FILE_PATH to AZURE_FEDERATED_TOKEN_FILE #133

Merged
merged 1 commit into from
Aug 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,15 @@ $(HELM):
chmod +x "$(TOOLS_BIN_DIR)/$(HELM_BIN)" "$(HELM)"
rm -rf helm* $(OS)-$(GOARCH)

## --------------------------------------
## E2E images
## --------------------------------------
MSAL_GO_E2E_IMAGE := $(REGISTRY)/msal-go-e2e:$(IMAGE_VERSION)

.PHONY: docker-build-e2e-msal-go
docker-build-e2e-msal-go:
docker buildx build --no-cache -t $(MSAL_GO_E2E_IMAGE) -f examples/msal-go/Dockerfile --platform="linux/amd64" --output=$(OUTPUT_TYPE) examples/msal-go
chewong marked this conversation as resolved.
Show resolved Hide resolved

## --------------------------------------
## Testing
## --------------------------------------
Expand Down Expand Up @@ -246,7 +255,7 @@ GINKGO_ARGS ?= -focus="$(GINKGO_FOCUS)" -skip="$(GINKGO_SKIP)" -nodes=$(GINKGO_N

# E2E configurations
KUBECONFIG ?= $(HOME)/.kube/config
E2E_ARGS := -kubeconfig=$(KUBECONFIG) -report-dir=$(PWD)/_artifacts -e2e.arc-cluster=$(ARC_CLUSTER)
E2E_ARGS := -kubeconfig=$(KUBECONFIG) -report-dir=$(PWD)/_artifacts -e2e.arc-cluster=$(ARC_CLUSTER) -e2e.token-exchange-image=$(MSAL_GO_E2E_IMAGE)
E2E_EXTRA_ARGS ?=

.PHONY: test-e2e-run
Expand All @@ -271,6 +280,7 @@ kind-create: $(KIND) $(KUBECTL)
.PHONY: kind-load-image
kind-load-image:
$(KIND) load docker-image $(WEBHOOK_IMAGE) --name $(KIND_CLUSTER_NAME)
$(KIND) load docker-image $(MSAL_GO_E2E_IMAGE) --name $(KIND_CLUSTER_NAME)

.PHONY: kind-delete
kind-delete: $(KIND)
Expand Down
25 changes: 25 additions & 0 deletions examples/msal-go/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM golang:1.16 as builder

WORKDIR /workspace
# Copy the Go Modules manifests
COPY go.mod go.mod
COPY go.sum go.sum
# cache deps before building and copying source so that we don't need to re-download as much
# and so that source changes don't invalidate our downloaded layer
RUN go mod download

# Copy the go source
COPY main.go main.go
COPY token_credential.go token_credential.go

# Build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o msalgo .

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
FROM gcr.io/distroless/static:nonroot
WORKDIR /
COPY --from=builder /workspace/msalgo .
USER nonroot:nonroot

ENTRYPOINT ["/msalgo"]
14 changes: 10 additions & 4 deletions examples/msal-go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"time"

"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault"
"github.com/Azure/go-autorest/autorest"
Expand All @@ -20,9 +21,14 @@ func main() {
kvClient := keyvault.New()
kvClient.Authorizer = autorest.NewBearerAuthorizerCallback(nil, clientAssertionBearerAuthorizerCallback)

secretBundle, err := kvClient.GetSecret(context.Background(), keyvaultURL, secretName, "")
if err != nil {
klog.Fatalf("failed to get secret from keyvault, err: %+v", err)
for {
secretBundle, err := kvClient.GetSecret(context.Background(), keyvaultURL, secretName, "")
if err != nil {
klog.Fatalf("failed to get secret from keyvault, err: %+v", err)
}
klog.InfoS("successfully got secret", "secret", *secretBundle.Value)

// wait for 60 seconds before polling again
time.Sleep(60 * time.Second)
}
klog.InfoS("successfully got secret", "secret", *secretBundle.Value)
}
4 changes: 2 additions & 2 deletions examples/msal-go/token_credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ func clientAssertionBearerAuthorizerCallback(tenantID, resource string) (*autore
// AZURE_CLIENT_ID with the clientID set in the service account annotation
// AZURE_TENANT_ID with the tenantID set in the service account annotation. If not defined, then
// the tenantID provided via aad-pi-webhook-config for the webhook will be used.
// TOKEN_FILE_PATH is the service account token path
// AZURE_FEDERATED_TOKEN_FILE is the service account token path
clientID := os.Getenv("AZURE_CLIENT_ID")
tokenFilePath := os.Getenv("TOKEN_FILE_PATH")
tokenFilePath := os.Getenv("AZURE_FEDERATED_TOKEN_FILE")

// generate a token using the msal confidential client
// this will always generate a new token request to AAD
Expand Down
4 changes: 2 additions & 2 deletions examples/msal-net/akvdotnet/TokenCredential.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ public MyClientAssertionCredential()
// AZURE_CLIENT_ID with the clientID set in the service account annotation
// AZURE_TENANT_ID with the tenantID set in the service account annotation. If not defined, then
// the tenantID provided via aad-pi-webhook-config for the webhook will be used.
// TOKEN_FILE_PATH is the service account token path
// AZURE_FEDERATED_TOKEN_FILE is the service account token path
var clientID = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var tokenPath = Environment.GetEnvironmentVariable("TOKEN_FILE_PATH");
var tokenPath = Environment.GetEnvironmentVariable("AZURE_FEDERATED_TOKEN_FILE");
var tenantID = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");

_confidentialClientApp = ConfidentialClientApplicationBuilder.Create(clientID)
Expand Down
12 changes: 6 additions & 6 deletions pkg/webhook/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ const (

// Environment variables injected in the pod
const (
AzureClientIDEnvVar = "AZURE_CLIENT_ID"
AzureTenantIDEnvVar = "AZURE_TENANT_ID"
TokenFilePathEnvVar = "TOKEN_FILE_PATH" // #nosec
AzureAuthorityHostEnvVar = "AZURE_AUTHORITY_HOST"
TokenFilePathName = "azure-identity-token"
TokenFileMountPath = "/var/run/secrets/tokens" // #nosec
AzureClientIDEnvVar = "AZURE_CLIENT_ID"
AzureTenantIDEnvVar = "AZURE_TENANT_ID"
AzureFederatedTokenFileEnvVar = "AZURE_FEDERATED_TOKEN_FILE" // #nosec
AzureAuthorityHostEnvVar = "AZURE_AUTHORITY_HOST"
TokenFilePathName = "azure-identity-token"
TokenFileMountPath = "/var/run/secrets/tokens" // #nosec
// DefaultAudience is the audience added to the service account token audience
// This value is to be consistent with other token exchange flows in AAD and has
// no impact on the actual token exchange flow.
Expand Down
6 changes: 3 additions & 3 deletions pkg/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,9 @@ func addEnvironmentVariables(container corev1.Container, clientID, tenantID, azu
if _, ok := m[AzureTenantIDEnvVar]; !ok {
container.Env = append(container.Env, corev1.EnvVar{Name: AzureTenantIDEnvVar, Value: tenantID})
}
// add the token file path env var
if _, ok := m[TokenFilePathEnvVar]; !ok {
container.Env = append(container.Env, corev1.EnvVar{Name: TokenFilePathEnvVar, Value: filepath.Join(TokenFileMountPath, TokenFilePathName)})
// add the token file env var
if _, ok := m[AzureFederatedTokenFileEnvVar]; !ok {
container.Env = append(container.Env, corev1.EnvVar{Name: AzureFederatedTokenFileEnvVar, Value: filepath.Join(TokenFileMountPath, TokenFilePathName)})
}
// add the azure authority host env var
if _, ok := m[AzureAuthorityHostEnvVar]; !ok {
Expand Down
24 changes: 12 additions & 12 deletions pkg/webhook/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,15 +509,15 @@ func TestAddEnvironmentVariables(t *testing.T) {
Value: "clientID",
},
{
Name: "AZURE_TENANT_ID",
Name: AzureTenantIDEnvVar,
Value: "tenantID",
},
{
Name: "TOKEN_FILE_PATH",
Name: AzureFederatedTokenFileEnvVar,
Value: filepath.Join(TokenFileMountPath, TokenFilePathName),
},
{
Name: "AZURE_AUTHORITY_HOST",
Name: AzureAuthorityHostEnvVar,
Value: "https://login.microsoftonline.com/",
},
},
Expand All @@ -534,15 +534,15 @@ func TestAddEnvironmentVariables(t *testing.T) {
Value: "myClientID",
},
{
Name: "AZURE_TENANT_ID",
Name: AzureTenantIDEnvVar,
Value: "myTenantID",
},
{
Name: "TOKEN_FILE_PATH",
Name: AzureFederatedTokenFileEnvVar,
Value: filepath.Join(TokenFileMountPath, TokenFilePathName),
},
{
Name: "AZURE_AUTHORITY_HOST",
Name: AzureAuthorityHostEnvVar,
Value: "https://login.microsoftonline.com/",
},
},
Expand All @@ -556,15 +556,15 @@ func TestAddEnvironmentVariables(t *testing.T) {
Value: "myClientID",
},
{
Name: "AZURE_TENANT_ID",
Name: AzureTenantIDEnvVar,
Value: "myTenantID",
},
{
Name: "TOKEN_FILE_PATH",
Name: AzureFederatedTokenFileEnvVar,
Value: filepath.Join(TokenFileMountPath, TokenFilePathName),
},
{
Name: "AZURE_AUTHORITY_HOST",
Name: AzureAuthorityHostEnvVar,
Value: "https://login.microsoftonline.com/",
},
},
Expand Down Expand Up @@ -595,15 +595,15 @@ func TestAddEnvironmentVariables(t *testing.T) {
Value: "clientID",
},
{
Name: "AZURE_TENANT_ID",
Name: AzureTenantIDEnvVar,
Value: "tenantID",
},
{
Name: "TOKEN_FILE_PATH",
Name: AzureFederatedTokenFileEnvVar,
Value: filepath.Join(TokenFileMountPath, TokenFilePathName),
},
{
Name: "AZURE_AUTHORITY_HOST",
Name: AzureAuthorityHostEnvVar,
Value: "https://login.microsoftonline.com/",
},
},
Expand Down
7 changes: 5 additions & 2 deletions scripts/ci-e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ create_cluster() {
download_service_account_keys
# create a kind cluster, then build and load the webhook manager image to the kind cluster
make kind-create
[[ "${SKIP_IMAGE_BUILD:-}" == "true" ]] || OUTPUT_TYPE="type=docker" make docker-build-webhook
[[ "${SKIP_IMAGE_BUILD:-}" == "true" ]] || OUTPUT_TYPE="type=docker" make docker-build-webhook docker-build-e2e-msal-go
make kind-load-image
else
: "${REGISTRY:?Environment variable empty or not defined.}"
Expand Down Expand Up @@ -106,7 +106,10 @@ test_helm_chart() {
--namespace aad-pi-webhook-system \
--wait
poll_webhook_readiness
make test-e2e-run
# TODO (aramase) remove token exchange from GINKGO_SKIP after v0.4.0 release is published
# Skipping TokenExchange test for the current release as we're using the latest msal-go image
# which is updated to use AZURE_FEDERATED_TOKEN_FILE for token path.
GINKGO_SKIP=TokenExchange make test-e2e-run

${HELM} upgrade --install pod-identity-webhook "${REPO_ROOT}/manifest_staging/charts/pod-identity-webhook" \
--set image.repository="${REGISTRY:-mcr.microsoft.com/oss/azure/aad-pod-managed-identity/webhook}" \
Expand Down
7 changes: 4 additions & 3 deletions test/e2e/e2e.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ import (
)

var (
arcCluster bool
c *kubernetes.Clientset
coreNamespaces = []string{
arcCluster bool
tokenExchangeE2EImage string
c *kubernetes.Clientset
coreNamespaces = []string{
metav1.NamespaceSystem,
"aad-pi-webhook-system",
}
Expand Down
1 change: 1 addition & 0 deletions test/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

func init() {
flag.BoolVar(&arcCluster, "e2e.arc-cluster", false, "Running on an arc-enabled cluster")
flag.StringVar(&tokenExchangeE2EImage, "e2e.token-exchange-image", "aramase/dotnet:v0.4", "The image to use for token exchange tests")
}

// handleFlags sets up all flags and parses the command line.
Expand Down
13 changes: 12 additions & 1 deletion test/e2e/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,14 +216,25 @@ func validateMutatedPod(f *framework.Framework, pod *corev1.Pod, skipContainers
for _, injected := range []string{
webhook.AzureClientIDEnvVar,
webhook.AzureTenantIDEnvVar,
webhook.TokenFilePathEnvVar,
webhook.AzureAuthorityHostEnvVar,
} {
if _, ok := m[injected]; !ok {
framework.Failf("container %s in pod %s does not have env var %s injected", container.Name, pod.Name, injected)
}
}

// v0.3.0 injects the token file path as TOKEN_FILE_PATH environment variable. For v0.3.0+ the environment variable
// is updated to AZURE_FEDERATED_TOKEN_FILE. Adding this check to support upgrade tests from v0.3.0 to v0.3.0+
// TODO (aramase) remove this after v0.4.0
framework.Logf("ensuring that the token file path environment variable is injected to %s in %s", container.Name, pod.Name)
tokenFilePathSet := false
if _, ok := m["TOKEN_FILE_PATH"]; ok {
tokenFilePathSet = true
}
if _, ok := m[webhook.AzureFederatedTokenFileEnvVar]; !ok && !tokenFilePathSet {
framework.Failf("container %s in pod %s does not have env var %s injected", container.Name, pod.Name, webhook.AzureFederatedTokenFileEnvVar)
}

framework.Logf("ensuring that azure-identity-token is mounted to %s", container.Name)
found := false
for _, volumeMount := range container.VolumeMounts {
Expand Down
6 changes: 3 additions & 3 deletions test/e2e/token_exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
var _ = ginkgo.Describe("TokenExchange [KindOnly]", func() {
f := framework.NewDefaultFramework("token-exchange")

// E2E scenario from https://github.com/Azure/aad-pod-managed-identity/tree/main/examples/msal-net/akvdotnet
// E2E scenario from https://github.com/Azure/aad-pod-managed-identity/tree/main/examples/msal-go
ginkgo.It("should exchange the service account token for a valid AAD token", func() {
clientID, ok := os.LookupEnv("APPLICATION_CLIENT_ID")
gomega.Expect(ok).To(gomega.BeTrue(), "APPLICATION_CLIENT_ID must be set")
Expand All @@ -39,7 +39,7 @@ var _ = ginkgo.Describe("TokenExchange [KindOnly]", func() {
f.ClientSet,
namespace,
serviceAccount,
"aramase/dotnet:v0.4",
tokenExchangeE2EImage,
nil,
nil,
[]corev1.EnvVar{{
Expand All @@ -63,7 +63,7 @@ var _ = ginkgo.Describe("TokenExchange [KindOnly]", func() {
return false
}
framework.Logf("stdout: %s", stdout)
return strings.Contains(stdout, "Your secret is Hello!")
return strings.Contains(stdout, `"successfully got secret" secret="Hello!"`)
}, framework.PollShortTimeout, framework.Poll).Should(gomega.BeTrue())
}
})
Expand Down