Skip to content

Commit

Permalink
Merge branch 'pr/malancas/1424' into release
Browse files Browse the repository at this point in the history
Signed-off-by: Cody Soyland <[email protected]>
  • Loading branch information
codysoyland committed May 17, 2024
2 parents 09395a2 + 96b6cb8 commit b851539
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 7 deletions.
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,68 @@ If you would like to use the local Kind registry instead of a live one,
do not include the `registry-url` flag when calling the CLI. It will default to using the local registry. But before running the CLI, you must add the following line to your `/etc/hosts` file first:
`127.0.0.1 registry.local`

## Using Policy Controller with Azure Container Registry (ACR)

To allow the webhook to make requests to ACR, you must use one of the following
methods to authenticate:

1. Managed identities (used with AKS clusters)
1. Service principals (used with AKS clusters)
1. Pod imagePullSecrets (used with non AKS clusters)

See the [official documentation](https://learn.microsoft.com/en-us/azure/container-registry/authenticate-kubernetes-options#scenarios).

### Managed Identities for AKS Clusters

See the [official documentation](https://learn.microsoft.com/en-us/azure/aks/cluster-container-registry-integration?toc=%2Fazure%2Fcontainer-registry%2Ftoc.json&bc=%2Fazure%2Fcontainer-registry%2Fbreadcrumb%2Ftoc.json&tabs=azure-cli) for more details.

1. You must enable managed identities for the cluster using the `--enable-managed-identities` flag with either the `az aks create` or `az aks update` commands
1. You must attach the ACR to the AKS cluster using the `--attach-acr` with either
the `az aks create` or `az aks update` commands. See [here](https://learn.microsoft.com/en-us/azure/aks/cluster-container-registry-integration?toc=%2Fazure%2Fcontainer-registry%2Ftoc.json&bc=%2Fazure%2Fcontainer-registry%2Fbreadcrumb%2Ftoc.json&tabs=azure-cli#create-a-new-aks-cluster-and-integrate-with-an-existing-acr) for more details
1. You must set the `AZ_CLIENT_ID` environment variable to the managed identity's client ID.
This will detected by the Azure credential manager

When you create a cluster that has managed identities enabled,
a user assigned managed identity called
`<AKS cluster name>-agentpool`. Use this identity's client ID
when setting `AZ_CLIENT_ID`. Make sure the ACR is attached to
your cluster.

#### Installing Policy Controller from this repository

If you are deploying policy-controller directly from this repository with
`make ko-apply`, you will need to add `AZ_CLIENT_ID` to the list of environment
variables in the [webhook deployment configuration](config/webhook.yaml).

#### Installing Policy Controller from the Helm chart

You can provide the managed identity's client ID as a custom environment
variable when installing the Helm chart:

```bash
helm upgrade --install policy-controller sigstore/policy-controller --version 0.9.0 \
--set webhook.env.AZ_CLIENT_ID=my-managed-id-client-id
```

### Service Principals for AKS Clusters

#### Installing Policy Controller from this repository

If you are deploying policy-controller directly from this repository with
`make ko-apply`, you will need to add `AZ_CLIENT_ID` and `AZURE_TENANT_ID` to
the list of environment variables in the
[webhook deployment configuration](config/webhook.yaml).

#### Installing Policy Controller from the Helm chart

You should be able to provide the service principal client ID and tenant ID
as a workload identity annotations:

```bash
helm upgrade --install policy-controller sigstore/policy-controller --version 0.9.0 \
--set-json webhook.serviceAccount.annotations="{\"azure.workload.identity/client-id\": \"${SERVICE_PRINCIPAL_CLIENT_ID}\", \"azure.workload.identity/tenant-id\": \"${TENANT_ID}\"}"
```

## Support Policy

This policy-controller's versions are able to run in the following versions of Kubernetes:
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ require (
require github.com/spf13/cobra v1.8.0

require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8
github.com/docker/docker v26.1.2+incompatible
github.com/docker/docker-credential-helpers v0.8.0
github.com/docker/go-connections v0.5.0
github.com/go-jose/go-jose/v3 v3.0.3
github.com/sigstore/protobuf-specs v0.3.2
Expand All @@ -83,8 +87,6 @@ require (
cuelang.org/go v0.8.2 // indirect
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 // indirect
Expand Down Expand Up @@ -129,7 +131,6 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect
github.com/aws/smithy-go v1.20.2 // indirect
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
Expand All @@ -150,7 +151,6 @@ require (
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/cli v24.0.7+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
Expand Down
3 changes: 2 additions & 1 deletion pkg/webhook/clusterimagepolicy/clusterimagepolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
"github.com/sigstore/policy-controller/pkg/apis/policy/v1alpha1"
signaturealgo "github.com/sigstore/policy-controller/pkg/apis/signaturealgo"
"github.com/sigstore/policy-controller/pkg/webhook/registryauth"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"k8s.io/apimachinery/pkg/types"
"knative.dev/pkg/apis"
Expand Down Expand Up @@ -254,7 +255,7 @@ func (a *Authority) SourceSignaturePullSecretsOpts(ctx context.Context, namespac
ImagePullSecrets: signaturePullSecrets,
}

kc, err := k8schain.New(ctx, kubeclient.Get(ctx), opt)
kc, err := registryauth.NewK8sKeychain(ctx, kubeclient.Get(ctx), opt)
if err != nil {
logging.FromContext(ctx).Errorf("failed creating keychain: %+v", err)
return nil, err
Expand Down
62 changes: 62 additions & 0 deletions pkg/webhook/registryauth/azure/acrhelper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// Copyright 2024 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package azure

import (
"context"
"fmt"

"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/docker/docker-credential-helpers/credentials"
)

type ACRHelper struct{}

func NewACRHelper() credentials.Helper {
return &ACRHelper{}
}

func (a ACRHelper) Add(_ *credentials.Credentials) error {
return fmt.Errorf("add is unimplemented")
}

func (a ACRHelper) Delete(_ string) error {
return fmt.Errorf("delete is unimplemented")
}

func (a ACRHelper) Get(_ string) (string, string, error) {
azCred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return "", "", fmt.Errorf("failed to obtain a credential: %w", err)
}

// We need to set the desired token policy to https://management.azure.com
// to get a token that can be used to authenticate to the Azure Container Registry.
opts := policy.TokenRequestOptions{
Scopes: []string{"https://management.azure.com/.default"},
}
token, err := azCred.GetToken(context.Background(), opts)
if err != nil {
return "", "", fmt.Errorf("failed to get token: %w", err)
}

return token.Token, "", nil
}

func (a ACRHelper) List() (map[string]string, error) {
return nil, fmt.Errorf("list is unimplemented")
}
60 changes: 60 additions & 0 deletions pkg/webhook/registryauth/registryauth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// Copyright 2024 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package registryauth

import (
"context"
"io"

ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/authn/k8schain"
kauth "github.com/google/go-containerregistry/pkg/authn/kubernetes"
"github.com/google/go-containerregistry/pkg/v1/google"
"github.com/sigstore/policy-controller/pkg/webhook/registryauth/azure"
"k8s.io/client-go/kubernetes"
)

/*
This file is based the K8s auth key chain constructor defined in the
go-containerregistry library in
https://github.com/google/go-containerregistry/blob/ff385a972813c79bbd5fc89357ff2cefe3e5b43c/pkg/authn/k8schain/k8schain.go
The ony difference in this implementation is the Azure key chain. It is created
using the current Azure credential handler defined in github.com/Azure/azure-sdk-for-go/sdk/azidentity.
The K8s auth key chain constructor in go-containerregistry uses an old Azure credential handler.
We should eventually try to get the Azure credential handler updated upstream in
go-containerregistry and remove this file. But for now, this custom constructor
should fix authentication errors encountered when using the policy controller
with ACR and AKS clusters.
*/
var amazonKeychain authn.Keychain = authn.NewKeychainFromHelper(ecr.NewECRHelper(ecr.WithLogger(io.Discard)))

func NewK8sKeychain(ctx context.Context, client kubernetes.Interface, opt k8schain.Options) (authn.Keychain, error) {
k8s, err := kauth.New(ctx, client, opt)
if err != nil {
return nil, err
}

return authn.NewMultiKeychain(
k8s,
authn.DefaultKeychain,
google.Keychain,
amazonKeychain,
authn.NewKeychainFromHelper(azure.NewACRHelper()),
), nil
}
5 changes: 3 additions & 2 deletions pkg/webhook/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
policyduckv1beta1 "github.com/sigstore/policy-controller/pkg/apis/duck/v1beta1"
policycontrollerconfig "github.com/sigstore/policy-controller/pkg/config"
webhookcip "github.com/sigstore/policy-controller/pkg/webhook/clusterimagepolicy"
"github.com/sigstore/policy-controller/pkg/webhook/registryauth"
rekor "github.com/sigstore/rekor/pkg/client"
"github.com/sigstore/rekor/pkg/generated/client"
"github.com/sigstore/sigstore/pkg/cryptoutils"
Expand Down Expand Up @@ -267,7 +268,7 @@ func (v *Validator) ValidateCronJob(ctx context.Context, c *duckv1.CronJob) *api
}

func (v *Validator) validatePodSpec(ctx context.Context, namespace, kind, apiVersion string, labels map[string]string, ps *corev1.PodSpec, opt k8schain.Options) (errs *apis.FieldError) {
kc, err := k8schain.New(ctx, kubeclient.Get(ctx), opt)
kc, err := registryauth.NewK8sKeychain(ctx, kubeclient.Get(ctx), opt)
if err != nil {
logging.FromContext(ctx).Warnf("Unable to build k8schain: %v", err)
return apis.ErrGeneric(err.Error(), apis.CurrentField)
Expand Down Expand Up @@ -1126,7 +1127,7 @@ func (v *Validator) ResolveCronJob(ctx context.Context, c *duckv1.CronJob) {
var remoteResolveDigest = ociremote.ResolveDigest

func (v *Validator) resolvePodSpec(ctx context.Context, ps *corev1.PodSpec, opt k8schain.Options) {
kc, err := k8schain.New(ctx, kubeclient.Get(ctx), opt)
kc, err := registryauth.NewK8sKeychain(ctx, kubeclient.Get(ctx), opt)
if err != nil {
logging.FromContext(ctx).Warnf("Unable to build k8schain: %v", err)
return
Expand Down

0 comments on commit b851539

Please sign in to comment.