Skip to content

Commit bee6cb5

Browse files
Acr managed identity (#12)
* Updated authn to work with Azure AKS managed identities To use this, provide the client id during helm install. * Cleaned up reamde for official use * Formatting fixes * updated jwt lib * linting * README updates * Update pkg/authn/provider.go Co-authored-by: Cody Soyland <[email protected]> * Update README.md Co-authored-by: Cody Soyland <[email protected]> --------- Co-authored-by: Cody Soyland <[email protected]>
1 parent d8065bc commit bee6cb5

File tree

10 files changed

+236
-94
lines changed

10 files changed

+236
-94
lines changed

README.md

Lines changed: 83 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ feature](https://open-policy-agent.github.io/gatekeeper/website/docs/externaldat
44
with [Artifact attestations](https://github.com/actions/attest) to determine whether
55
the images are valid by verifying its signatures.
66

7+
> [!IMPORTANT]
8+
> For this to work, OPA Gatekeeper must run with
9+
> `enableExternalData=true`, which can be configured during
10+
> installation.
11+
712
## Limitations
813

914
* mTLS between OPA Gatekeeper and the external data provider is not
@@ -12,129 +17,125 @@ the images are valid by verifying its signatures.
1217

1318
## Installation
1419

15-
## Verification
20+
### Preparation
1621

17-
## Local installation
22+
Before the installation starts, two steps are required to prepare:
1823

19-
1. Create a [kind
20-
cluster](https://kind.sigs.k8s.io/docs/user/quick-start/).
21-
2. Prepare `pull-secret` for private OCI registry (Optional)
24+
1. How OPA Gatekeeper authenticates the OPA External Data
25+
Provider. This is done via regular TLS certificates, but they must
26+
be created and made available to the services.
27+
1. If private OPI registries are used, the authentication must be
28+
configured.
2229

23-
```
24-
$ kubectl create secret docker-registry \
25-
ghcr-login-secret \
26-
--docker-server=https://ghcr.io \
27-
--docker-username=$YOUR_GITHUB_USERNAME \
28-
--docker-password=$YOUR_GITHUB_TOKEN \
29-
--docker-email=$YOUR_EMAIL
30-
```
30+
#### OCI Authentication
3131

32-
3. Prepare a pull secret for the OPA external data provider (Optional)
32+
Currently there are two tested authentication methods known to work
33+
with private OCI registries:
3334

34-
```
35-
$ kubectl create secret \
36-
-n provider-system \
37-
docker-registry aa-ghcr-login-secret \
38-
--docker-server=https://ghcr.io \
39-
--docker-username=$YOUR_GITHUB_USERNAME \
40-
--docker-password=$YOUR_GITHUB_TOKEN \
41-
--docker-email=$YOUR_EMAIL
42-
```
35+
1. Using `imagePullSecrets`
36+
2. Using Managed Identities with Azure Managed Kubernetes Service
37+
(AKS)
4338

44-
4. Install Gatekeeper and **enable external data feature**
39+
Although these are the only tested authentication methods, others may
40+
work (Azure using Sevice Principals, GKE and EKS configurations) as
41+
long as the POD/Service Accounts are configured properly.
4542

46-
```
47-
# Add the Gatekeeper Helm repository
48-
$ helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
49-
50-
# Install the latest version of Gatekeeper with the external data feature enabled.
51-
$ helm install gatekeeper/gatekeeper \
52-
--set enableExternalData=true \
53-
--name-template=gatekeeper \
54-
--namespace gatekeeper-system \
55-
--create-namespace
56-
```
43+
To use `imagePullSecrets`, prepare the secret in the namespace used by
44+
the Artifact Attestations OPA Provider. The default name is
45+
`aa-login-secret` but can be changed if needed, just make sure to
46+
update the value in `values.yaml` before installing.
5747

58-
5. Generate server TLS for the external data provider
48+
To use Azure Managed Identities, first configure the Managed Identity
49+
with the required permission (ACR Pull against the relevant
50+
registries), then [configure a federated
51+
credential](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-config-app-trust-managed-identity?tabs=microsoft-entra-admin-center#configure-a-federated-identity-credential-on-an-existing-application)
52+
against the K8s cluster.
5953

60-
```
61-
$ ./scripts/gen_certs.sh
62-
```
54+
To enable the use of Azure Managed Identities, you must provide the
55+
Managed Identity's Client ID: `--set azureClientId=${AZURE_CLIENT_ID}`
56+
during helm install.
6357

64-
6. Build and load the docker image
58+
#### TLS certificates
6559

66-
```
67-
$ make docker
68-
$ make kind-load-image
69-
```
60+
> [!NOTE]
61+
> Tested version of OPA Gatekeeper up to version 3.18.2 only supports
62+
> RSA keys for the TLS certificates.
63+
64+
OPA Gatekeeper relies on TLS authentication when communicating with
65+
external data providers. There is a provided
66+
[script](scripts/gen_certs.sh) to generate a self signed CA and TLS
67+
certificate. The certificate can be created via other means, as long
68+
as the private key can be mounted as a secret to the Artifact
69+
Attestations OPA Provider POD.
70+
71+
When installing the Artifacts Attestations OPA Provider, the CA
72+
certificate bundle must be provided to configure the root of trust.
7073

71-
7. Install the data provider
74+
The secret containing the TLS certificate and private key can be
75+
automatically created, or created separately from the helm
76+
installation. The secret must have the name `provider-tls-cert`.
7277

73-
To automatically provision the server tls certificate/key secret
78+
### Install via helm
79+
80+
#### Using `imagePullSecrets`
7481

7582
```
7683
$ helm install artifact-attestations-opa-provider charts/artifact-attestations-opa-provider \
7784
--set provider.tls.caBundle="$(cat certs/ca.crt | base64 | tr -d '\n\r')" \
7885
--set serverCert="$(cat certs/tls.crt | base64 | tr -d '\n\r')" \
7986
--set serverKey="$(cat certs/tls.key | base64 | tr -d '\n\r')" \
87+
--set imagePullSecrets=<name-of-your-secret> \
8088
--namespace provider-system \
8189
--create-namespace
8290
```
8391

84-
or if the secret is already created
92+
#### Using Azure Managed Identity
8593

8694
```
8795
$ helm install artifact-attestations-opa-provider charts/artifact-attestations-opa-provider \
8896
--set provider.tls.caBundle="$(cat certs/ca.crt | base64 | tr -d '\n\r')" \
89-
--set serverCert="" \
97+
--set serverCert="$(cat certs/tls.crt | base64 | tr -d '\n\r')" \
98+
--set serverKey="$(cat certs/tls.key | base64 | tr -d '\n\r')" \
99+
--set azureClientId=${AZURE_CLIENT_ID} \
90100
--namespace provider-system \
91101
--create-namespace
92102
```
93103

94-
8. Install constraint template and constraint.
95-
96-
From repo:
97-
98-
```
99-
$ kubectl apply -f validation/from-repo-constraint-template.yml
100-
$ kubectl apply -f validation/from-repo-constraint.yml
101-
```
102-
103-
or from org:
104-
105-
```
106-
$ kubectl apply -f validation/from-org-constraint-template.yml
107-
$ kubectl apply -f validation/from-org-constraint.yml
108-
```
109-
110-
or from org with signer (reusable workflow):
104+
#### Using Azure Managed Identity and an existing TLS secret
111105

112106
```
113-
$ kubectl apply -f validation/from-org-with-signer-constraint-template.yml
114-
$ kubectl apply -f validation/from-org-with-signer-constraint.yml
107+
$ helm install artifact-attestations-opa-provider charts/artifact-attestations-opa-provider \
108+
--set provider.tls.caBundle="$(cat certs/ca.crt | base64 | tr -d '\n\r')" \
109+
--set serverCert="" \
110+
--set azureClientId=${AZURE_CLIENT_ID} \
111+
--namespace provider-system \
112+
--create-namespace
115113
```
116114

117-
9. Test with an image from Tina's repository (PGI Sigstore)
115+
## Verification
118116

119-
```
120-
$ kubectl run nginx --image=ghcr.io/tinaheidinger/test-container:latest --dry-run=server -ojson
121-
```
117+
Three examples are provided that can be used as references when
118+
building out the policy.
122119

123-
10. Test with image from Fredrik's repository (private package, GitHub
124-
Sigstore)
120+
The policies are regular OPA Gatekeeper Constraints, where the
121+
enforcement action can be set, and the targeted/ignored resources.
125122

126-
```
127-
$ kubectl run nginx --image=ghcr.io/kommendorkapten/ghademo:latest --dry-run=server -ojson
128-
```
123+
When writing the policy using the provided examples, the organization
124+
should be the name of the GitHub org, like `octo-org`, and the
125+
repository must include the org name like `octo-org/octo-repo`.
129126

130-
11. Test with image from Fredrik's repository (private package, GitHub
131-
Sigstore) using a reusable workflow
127+
The following three examples are provided:
132128

133-
```
134-
$ kubectl run nginx --image=ghcr.io/kommendorkapten/ghademo:reusable --dry-run=server -ojson
135-
```
129+
1. [Verify image](validation/from-org-constraint-template.yaml) is
130+
built from a list of provided organizations.
131+
1. [Verify image](validation/from-repo-constraint-template.yaml) is
132+
built from a list of provided repositories.
133+
1. [Verify
134+
image](validation/from-org-with-signer-constraint-template.yaml) is
135+
originating from a list of organizations, and built with a reusable
136+
workflow from a list of provided repositories.
136137

137-
### Cleaning up
138+
## Uninstall
138139

139140
```
140141
$ kubectl delete -f validation

charts/artifact-attestations-opa-provider/templates/deployment.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ spec:
1212
metadata:
1313
labels:
1414
run: artifact-attestations-opa-provider
15+
{{- if .Values.azureClientId }}
16+
azure.workload.identity/use: "true"
17+
{{- end }}
1518
spec:
1619
serviceAccountName: {{ .Values.serviceAccount }}
1720
automountServiceAccountToken: true
@@ -32,7 +35,7 @@ spec:
3235
type: RuntimeDefault
3336
args:
3437
- -namespace={{ .Release.Namespace }}
35-
- -image-pull-secret=aa-ghcr-login-secret
38+
- -image-pull-secret={{ .Values.imagePullSecrets }}
3639
- -certs={{ .Values.certDir }}
3740
- -port={{ .Values.port }}
3841
ports:

charts/artifact-attestations-opa-provider/templates/service-account.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ kind: ServiceAccount
33
metadata:
44
name: {{ .Values.serviceAccount }}
55
namespace: {{ .Release.Namespace }}
6+
annotations:
7+
{{- if .Values.azureClientId }}
8+
azure.workload.identity/client-id: {{ .Values.azureClientId }}
9+
{{- end }}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
certDir: /certs
22
serviceAccount: opa-provider-sa
33
port: 8090
4+
imagePullSecrets: aa-login-secret
45
provider:
5-
timeout: 5
6+
timeout: 10
67
tls:
78
caBundle: ""

go.mod

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,51 @@ toolchain go1.24.1
66

77
require (
88
github.com/google/go-containerregistry v0.20.3
9+
github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20250225234217-098045d5e61f
910
github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20250225234217-098045d5e61f
1011
github.com/open-policy-agent/frameworks/constraint v0.0.0-20250310182122-79a9477fa575
1112
github.com/sigstore/sigstore-go v0.7.1-0.20250321070118-95b41380066f
1213
)
1314

1415
require (
16+
cloud.google.com/go/compute/metadata v0.6.0 // indirect
17+
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
1518
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 // indirect
19+
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
20+
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
21+
github.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect
22+
github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 // indirect
23+
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
24+
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
25+
github.com/Azure/go-autorest/logger v0.2.1 // indirect
26+
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
1627
github.com/OneOfOne/xxhash v1.2.8 // indirect
1728
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
29+
github.com/aws/aws-sdk-go-v2 v1.36.1 // indirect
1830
github.com/aws/aws-sdk-go-v2/config v1.29.6 // indirect
31+
github.com/aws/aws-sdk-go-v2/credentials v1.17.59 // indirect
32+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 // indirect
33+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 // indirect
34+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 // indirect
35+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect
36+
github.com/aws/aws-sdk-go-v2/service/ecr v1.38.4 // indirect
37+
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.29.2 // indirect
38+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect
39+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 // indirect
1940
github.com/aws/aws-sdk-go-v2/service/kms v1.37.18 // indirect
41+
github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 // indirect
42+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 // indirect
43+
github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 // indirect
44+
github.com/aws/smithy-go v1.22.2 // indirect
45+
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20250115170608-608f37feb051 // indirect
2046
github.com/blang/semver v3.5.1+incompatible // indirect
47+
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect
2148
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
2249
github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 // indirect
2350
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
2451
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect
2552
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect
53+
github.com/dimchansky/utfbom v1.1.1 // indirect
2654
github.com/docker/cli v28.0.2+incompatible // indirect
2755
github.com/docker/distribution v2.8.3+incompatible // indirect
2856
github.com/docker/docker-credential-helpers v0.9.3 // indirect
@@ -44,6 +72,7 @@ require (
4472
github.com/go-openapi/swag v0.23.1 // indirect
4573
github.com/go-openapi/validate v0.24.0 // indirect
4674
github.com/gogo/protobuf v1.3.2 // indirect
75+
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
4776
github.com/golang/protobuf v1.5.4 // indirect
4877
github.com/google/certificate-transparency-go v1.3.1 // indirect
4978
github.com/google/gnostic-models v0.6.9 // indirect
@@ -56,6 +85,7 @@ require (
5685
github.com/in-toto/in-toto-golang v0.9.0 // indirect
5786
github.com/inconshreveable/mousetrap v1.1.0 // indirect
5887
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect
88+
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect
5989
github.com/josharian/intern v1.0.0 // indirect
6090
github.com/json-iterator/go v1.1.12 // indirect
6191
github.com/klauspost/compress v1.17.11 // indirect

0 commit comments

Comments
 (0)