Merge pull request #1426 from fluxcd/rfc-0010
[RFC-0010] Introduce object-level workload identity for KMS decryption
This commit is contained in:
		
						commit
						d775ed3a19
					
				| 
						 | 
					@ -205,7 +205,18 @@ type Decryption struct {
 | 
				
			||||||
	// +required
 | 
						// +required
 | 
				
			||||||
	Provider string `json:"provider"`
 | 
						Provider string `json:"provider"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// ServiceAccountName is the name of the service account used to
 | 
				
			||||||
 | 
						// authenticate with KMS services from cloud providers. If a
 | 
				
			||||||
 | 
						// static credential for a given cloud provider is defined
 | 
				
			||||||
 | 
						// inside the Secret referenced by SecretRef, that static
 | 
				
			||||||
 | 
						// credential takes priority.
 | 
				
			||||||
 | 
						// +optional
 | 
				
			||||||
 | 
						ServiceAccountName string `json:"serviceAccountName,omitempty"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// The secret name containing the private OpenPGP keys used for decryption.
 | 
						// The secret name containing the private OpenPGP keys used for decryption.
 | 
				
			||||||
 | 
						// A static credential for a cloud provider defined inside the Secret
 | 
				
			||||||
 | 
						// takes priority to secret-less authentication with the ServiceAccountName
 | 
				
			||||||
 | 
						// field.
 | 
				
			||||||
	// +optional
 | 
						// +optional
 | 
				
			||||||
	SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
 | 
						SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -86,8 +86,11 @@ spec:
 | 
				
			||||||
                    - sops
 | 
					                    - sops
 | 
				
			||||||
                    type: string
 | 
					                    type: string
 | 
				
			||||||
                  secretRef:
 | 
					                  secretRef:
 | 
				
			||||||
                    description: The secret name containing the private OpenPGP keys
 | 
					                    description: |-
 | 
				
			||||||
                      used for decryption.
 | 
					                      The secret name containing the private OpenPGP keys used for decryption.
 | 
				
			||||||
 | 
					                      A static credential for a cloud provider defined inside the Secret
 | 
				
			||||||
 | 
					                      takes priority to secret-less authentication with the ServiceAccountName
 | 
				
			||||||
 | 
					                      field.
 | 
				
			||||||
                    properties:
 | 
					                    properties:
 | 
				
			||||||
                      name:
 | 
					                      name:
 | 
				
			||||||
                        description: Name of the referent.
 | 
					                        description: Name of the referent.
 | 
				
			||||||
| 
						 | 
					@ -95,6 +98,14 @@ spec:
 | 
				
			||||||
                    required:
 | 
					                    required:
 | 
				
			||||||
                    - name
 | 
					                    - name
 | 
				
			||||||
                    type: object
 | 
					                    type: object
 | 
				
			||||||
 | 
					                  serviceAccountName:
 | 
				
			||||||
 | 
					                    description: |-
 | 
				
			||||||
 | 
					                      ServiceAccountName is the name of the service account used to
 | 
				
			||||||
 | 
					                      authenticate with KMS services from cloud providers. If a
 | 
				
			||||||
 | 
					                      static credential for a given cloud provider is defined
 | 
				
			||||||
 | 
					                      inside the Secret referenced by SecretRef, that static
 | 
				
			||||||
 | 
					                      credential takes priority.
 | 
				
			||||||
 | 
					                    type: string
 | 
				
			||||||
                required:
 | 
					                required:
 | 
				
			||||||
                - provider
 | 
					                - provider
 | 
				
			||||||
                type: object
 | 
					                type: object
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,12 @@ rules:
 | 
				
			||||||
  verbs:
 | 
					  verbs:
 | 
				
			||||||
  - create
 | 
					  - create
 | 
				
			||||||
  - patch
 | 
					  - patch
 | 
				
			||||||
 | 
					- apiGroups:
 | 
				
			||||||
 | 
					  - ""
 | 
				
			||||||
 | 
					  resources:
 | 
				
			||||||
 | 
					  - serviceaccounts/token
 | 
				
			||||||
 | 
					  verbs:
 | 
				
			||||||
 | 
					  - create
 | 
				
			||||||
- apiGroups:
 | 
					- apiGroups:
 | 
				
			||||||
  - kustomize.toolkit.fluxcd.io
 | 
					  - kustomize.toolkit.fluxcd.io
 | 
				
			||||||
  resources:
 | 
					  resources:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -574,6 +574,22 @@ string
 | 
				
			||||||
</tr>
 | 
					</tr>
 | 
				
			||||||
<tr>
 | 
					<tr>
 | 
				
			||||||
<td>
 | 
					<td>
 | 
				
			||||||
 | 
					<code>serviceAccountName</code><br>
 | 
				
			||||||
 | 
					<em>
 | 
				
			||||||
 | 
					string
 | 
				
			||||||
 | 
					</em>
 | 
				
			||||||
 | 
					</td>
 | 
				
			||||||
 | 
					<td>
 | 
				
			||||||
 | 
					<em>(Optional)</em>
 | 
				
			||||||
 | 
					<p>ServiceAccountName is the name of the service account used to
 | 
				
			||||||
 | 
					authenticate with KMS services from cloud providers. If a
 | 
				
			||||||
 | 
					static credential for a given cloud provider is defined
 | 
				
			||||||
 | 
					inside the Secret referenced by SecretRef, that static
 | 
				
			||||||
 | 
					credential takes priority.</p>
 | 
				
			||||||
 | 
					</td>
 | 
				
			||||||
 | 
					</tr>
 | 
				
			||||||
 | 
					<tr>
 | 
				
			||||||
 | 
					<td>
 | 
				
			||||||
<code>secretRef</code><br>
 | 
					<code>secretRef</code><br>
 | 
				
			||||||
<em>
 | 
					<em>
 | 
				
			||||||
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
 | 
					<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
 | 
				
			||||||
| 
						 | 
					@ -583,7 +599,10 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
 | 
				
			||||||
</td>
 | 
					</td>
 | 
				
			||||||
<td>
 | 
					<td>
 | 
				
			||||||
<em>(Optional)</em>
 | 
					<em>(Optional)</em>
 | 
				
			||||||
<p>The secret name containing the private OpenPGP keys used for decryption.</p>
 | 
					<p>The secret name containing the private OpenPGP keys used for decryption.
 | 
				
			||||||
 | 
					A static credential for a cloud provider defined inside the Secret
 | 
				
			||||||
 | 
					takes priority to secret-less authentication with the ServiceAccountName
 | 
				
			||||||
 | 
					field.</p>
 | 
				
			||||||
</td>
 | 
					</td>
 | 
				
			||||||
</tr>
 | 
					</tr>
 | 
				
			||||||
</tbody>
 | 
					</tbody>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -823,33 +823,46 @@ For more information, see [remote clusters/Cluster-API](#remote-clusterscluster-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Decryption
 | 
					### Decryption
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`.spec.decryption` is an optional field to specify the configuration to decrypt
 | 
					Storing Secrets in Git repositories in plain text or base64 is unsafe,
 | 
				
			||||||
Secrets, ConfigMaps and patches that are a part of the Kustomization.
 | 
					regardless of the visibility or access restrictions of the repository.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Since Secrets are either plain text or `base64` encoded, it's unsafe to store
 | 
					In order to store Secrets safely in Git repositorioes you can use an
 | 
				
			||||||
them in plain text in a public or private Git repository. In order to store
 | 
					encryption provider and the optional field `.spec.decryption` to
 | 
				
			||||||
them safely, you can use [Mozilla SOPS](https://github.com/mozilla/sops) and
 | 
					configure decryption for Secrets that are a part of the Kustomization.
 | 
				
			||||||
encrypt your Kubernetes Secret data with [age](https://age-encryption.org/v1/)
 | 
					 | 
				
			||||||
and/or [OpenPGP](https://www.openpgp.org) keys, or with provider implementations
 | 
					 | 
				
			||||||
like Azure Key Vault, GCP KMS or Hashicorp Vault.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Also, you may want to encrypt some parts of resources as well. In order to do that,
 | 
					The only supported encryption provider is [SOPS](https://getsops.io/).
 | 
				
			||||||
you may encrypt patches as well.
 | 
					With SOPS you can encrypt your secrets with [age](https://github.com/FiloSottile/age)
 | 
				
			||||||
 | 
					or [OpenPGP](https://www.openpgp.org) keys, or with keys from Key Management Services
 | 
				
			||||||
 | 
					(KMS), like AWS KMS, Azure Key Vault, GCP KMS or Hashicorp Vault.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Note:** You must leave `metadata`, `kind` or `apiVersion` in plain text.
 | 
					**Note:** You must leave `metadata`, `kind` or `apiVersion` in plain text.
 | 
				
			||||||
An easy way to do this is to limit encrypted keys by appending `--encrypted-regex '^(data|stringData)$'`
 | 
					An easy way to do this is limiting the encrypted keys with the flag
 | 
				
			||||||
to your `sops --encrypt` command.
 | 
					`--encrypted-regex '^(data|stringData)$'` in your `sops encrypt` command.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
It has two fields:
 | 
					The `.spec.decryption` field has the following subfields:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `.provider`: The secrets decryption provider to be used. This field is required and
 | 
					- `.provider`: The secrets decryption provider to be used. This field is required and
 | 
				
			||||||
  the only supported value is `sops`.
 | 
					  the only supported value is `sops`.
 | 
				
			||||||
- `.secretRef.name`: The name of the secret that contains the keys to be used for
 | 
					- `.secretRef.name`: The name of the secret that contains the keys or cloud provider
 | 
				
			||||||
  decryption. This field can be omitted when using the
 | 
					  static credentials for KMS services to be used for decryption.
 | 
				
			||||||
  [global decryption](#controller-global-decryption) option.
 | 
					- `.serviceAccountName`: The name of the service account used for
 | 
				
			||||||
 | 
					  secret-less authentication with KMS services from cloud providers.
 | 
				
			||||||
 | 
					  See the [workload identity](/flux/installation/configuration/workload-identity/) docs
 | 
				
			||||||
 | 
					  for how to configure a cloud provider identity for this service account.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If a static credential for a given cloud provider is defined inside the secret
 | 
				
			||||||
 | 
					referenced by `.secretRef`, that static credential takes priority over secret-less
 | 
				
			||||||
 | 
					authentication for that provider. If no static credentials are defined for a given
 | 
				
			||||||
 | 
					cloud provider inside the secret, secret-less authentication is attempted for that
 | 
				
			||||||
 | 
					provider.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If `.serviceAccountName` is specified for secret-less authentication,
 | 
				
			||||||
 | 
					it takes priority over [controller global decryption](#controller-global-decryption)
 | 
				
			||||||
 | 
					for all cloud providers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```yaml
 | 
					```yaml
 | 
				
			||||||
---
 | 
					 | 
				
			||||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
 | 
					apiVersion: kustomize.toolkit.fluxcd.io/v1
 | 
				
			||||||
kind: Kustomization
 | 
					kind: Kustomization
 | 
				
			||||||
metadata:
 | 
					metadata:
 | 
				
			||||||
| 
						 | 
					@ -863,13 +876,11 @@ spec:
 | 
				
			||||||
    name: repository-with-secrets
 | 
					    name: repository-with-secrets
 | 
				
			||||||
  decryption:
 | 
					  decryption:
 | 
				
			||||||
    provider: sops
 | 
					    provider: sops
 | 
				
			||||||
 | 
					    serviceAccountName: sops-identity
 | 
				
			||||||
    secretRef:
 | 
					    secretRef:
 | 
				
			||||||
      name: sops-keys
 | 
					      name: sops-keys-and-credentials
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Note:** For information on Secrets decryption at a controller level, please
 | 
					 | 
				
			||||||
refer to [controller global decryption](#controller-global-decryption).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The Secret's `.data` section is expected to contain entries with decryption
 | 
					The Secret's `.data` section is expected to contain entries with decryption
 | 
				
			||||||
keys (for age and OpenPGP), or credentials (for any of the supported provider
 | 
					keys (for age and OpenPGP), or credentials (for any of the supported provider
 | 
				
			||||||
implementations). The controller identifies the type of the entry by the suffix
 | 
					implementations). The controller identifies the type of the entry by the suffix
 | 
				
			||||||
| 
						 | 
					@ -880,7 +891,7 @@ of the key (e.g. `.agekey`), or a fixed key (e.g. `sops.vault-token`).
 | 
				
			||||||
apiVersion: v1
 | 
					apiVersion: v1
 | 
				
			||||||
kind: Secret
 | 
					kind: Secret
 | 
				
			||||||
metadata:
 | 
					metadata:
 | 
				
			||||||
  name: sops-keys
 | 
					  name: sops-keys-and-credentials
 | 
				
			||||||
  namespace: default
 | 
					  namespace: default
 | 
				
			||||||
data:
 | 
					data:
 | 
				
			||||||
  # Exemplary age private key
 | 
					  # Exemplary age private key
 | 
				
			||||||
| 
						 | 
					@ -937,9 +948,9 @@ metadata:
 | 
				
			||||||
  namespace: default
 | 
					  namespace: default
 | 
				
			||||||
data:
 | 
					data:
 | 
				
			||||||
  sops.aws-kms: |
 | 
					  sops.aws-kms: |
 | 
				
			||||||
        aws_access_key_id: some-access-key-id
 | 
					    aws_access_key_id: some-access-key-id
 | 
				
			||||||
        aws_secret_access_key: some-aws-secret-access-key
 | 
					    aws_secret_access_key: some-aws-secret-access-key
 | 
				
			||||||
        aws_session_token: some-aws-session-token # this field is optional
 | 
					    aws_session_token: some-aws-session-token # this field is optional
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Azure Key Vault Secret entry
 | 
					#### Azure Key Vault Secret entry
 | 
				
			||||||
| 
						 | 
					@ -1408,6 +1419,8 @@ it is possible to specify global decryption settings on the
 | 
				
			||||||
kustomize-controller Pod. When the controller fails to find credentials on the
 | 
					kustomize-controller Pod. When the controller fails to find credentials on the
 | 
				
			||||||
Kustomization object itself, it will fall back to these defaults.
 | 
					Kustomization object itself, it will fall back to these defaults.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See also the [workload identity](/flux/installation/configuration/workload-identity/) docs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### AWS KMS
 | 
					#### AWS KMS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
While making use of the [IAM OIDC provider](https://eksctl.io/usage/iamserviceaccounts/)
 | 
					While making use of the [IAM OIDC provider](https://eksctl.io/usage/iamserviceaccounts/)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										11
									
								
								go.mod
								
								
								
								
							| 
						 | 
					@ -9,10 +9,12 @@ replace github.com/fluxcd/kustomize-controller/api => ./api
 | 
				
			||||||
replace github.com/opencontainers/go-digest => github.com/opencontainers/go-digest v1.0.1-0.20220411205349-bde1400a84be
 | 
					replace github.com/opencontainers/go-digest => github.com/opencontainers/go-digest v1.0.1-0.20220411205349-bde1400a84be
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
 | 
						cloud.google.com/go/kms v1.21.2
 | 
				
			||||||
	filippo.io/age v1.2.1
 | 
						filippo.io/age v1.2.1
 | 
				
			||||||
	github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6
 | 
						github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6
 | 
				
			||||||
	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0
 | 
						github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0
 | 
				
			||||||
	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0
 | 
						github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0
 | 
				
			||||||
 | 
						github.com/aws/aws-sdk-go-v2 v1.36.3
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2/credentials v1.17.67
 | 
						github.com/aws/aws-sdk-go-v2/credentials v1.17.67
 | 
				
			||||||
	github.com/cyphar/filepath-securejoin v0.4.1
 | 
						github.com/cyphar/filepath-securejoin v0.4.1
 | 
				
			||||||
	github.com/dimchansky/utfbom v1.1.1
 | 
						github.com/dimchansky/utfbom v1.1.1
 | 
				
			||||||
| 
						 | 
					@ -22,6 +24,8 @@ require (
 | 
				
			||||||
	github.com/fluxcd/pkg/apis/event v0.17.0
 | 
						github.com/fluxcd/pkg/apis/event v0.17.0
 | 
				
			||||||
	github.com/fluxcd/pkg/apis/kustomize v1.10.0
 | 
						github.com/fluxcd/pkg/apis/kustomize v1.10.0
 | 
				
			||||||
	github.com/fluxcd/pkg/apis/meta v1.11.0
 | 
						github.com/fluxcd/pkg/apis/meta v1.11.0
 | 
				
			||||||
 | 
						github.com/fluxcd/pkg/auth v0.12.0
 | 
				
			||||||
 | 
						github.com/fluxcd/pkg/cache v0.9.0
 | 
				
			||||||
	github.com/fluxcd/pkg/http/fetch v0.16.0
 | 
						github.com/fluxcd/pkg/http/fetch v0.16.0
 | 
				
			||||||
	github.com/fluxcd/pkg/kustomize v1.17.0
 | 
						github.com/fluxcd/pkg/kustomize v1.17.0
 | 
				
			||||||
	github.com/fluxcd/pkg/runtime v0.59.0
 | 
						github.com/fluxcd/pkg/runtime v0.59.0
 | 
				
			||||||
| 
						 | 
					@ -36,6 +40,7 @@ require (
 | 
				
			||||||
	github.com/ory/dockertest/v3 v3.12.0
 | 
						github.com/ory/dockertest/v3 v3.12.0
 | 
				
			||||||
	github.com/spf13/pflag v1.0.6
 | 
						github.com/spf13/pflag v1.0.6
 | 
				
			||||||
	golang.org/x/net v0.39.0
 | 
						golang.org/x/net v0.39.0
 | 
				
			||||||
 | 
						golang.org/x/oauth2 v0.29.0
 | 
				
			||||||
	k8s.io/api v0.33.0
 | 
						k8s.io/api v0.33.0
 | 
				
			||||||
	k8s.io/apimachinery v0.33.0
 | 
						k8s.io/apimachinery v0.33.0
 | 
				
			||||||
	k8s.io/client-go v0.33.0
 | 
						k8s.io/client-go v0.33.0
 | 
				
			||||||
| 
						 | 
					@ -61,7 +66,6 @@ require (
 | 
				
			||||||
	cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
 | 
						cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
 | 
				
			||||||
	cloud.google.com/go/compute/metadata v0.6.0 // indirect
 | 
						cloud.google.com/go/compute/metadata v0.6.0 // indirect
 | 
				
			||||||
	cloud.google.com/go/iam v1.5.2 // indirect
 | 
						cloud.google.com/go/iam v1.5.2 // indirect
 | 
				
			||||||
	cloud.google.com/go/kms v1.21.2 // indirect
 | 
					 | 
				
			||||||
	cloud.google.com/go/longrunning v0.6.7 // indirect
 | 
						cloud.google.com/go/longrunning v0.6.7 // indirect
 | 
				
			||||||
	cloud.google.com/go/monitoring v1.24.2 // indirect
 | 
						cloud.google.com/go/monitoring v1.24.2 // indirect
 | 
				
			||||||
	cloud.google.com/go/storage v1.51.0 // indirect
 | 
						cloud.google.com/go/storage v1.51.0 // indirect
 | 
				
			||||||
| 
						 | 
					@ -80,7 +84,6 @@ require (
 | 
				
			||||||
	github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
 | 
						github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
 | 
				
			||||||
	github.com/ProtonMail/go-crypto v1.2.0 // indirect
 | 
						github.com/ProtonMail/go-crypto v1.2.0 // indirect
 | 
				
			||||||
	github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
 | 
						github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
 | 
					 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
 | 
						github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect
 | 
						github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
 | 
						github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
 | 
				
			||||||
| 
						 | 
					@ -89,6 +92,7 @@ require (
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
 | 
						github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
 | 
						github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect
 | 
						github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect
 | 
				
			||||||
 | 
						github.com/aws/aws-sdk-go-v2/service/ecr v1.43.3 // indirect
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
 | 
						github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 // indirect
 | 
						github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 // indirect
 | 
				
			||||||
	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
 | 
						github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
 | 
				
			||||||
| 
						 | 
					@ -112,6 +116,7 @@ require (
 | 
				
			||||||
	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 | 
						github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 | 
				
			||||||
	github.com/docker/cli v28.1.1+incompatible // indirect
 | 
						github.com/docker/cli v28.1.1+incompatible // indirect
 | 
				
			||||||
	github.com/docker/docker v28.1.1+incompatible // indirect
 | 
						github.com/docker/docker v28.1.1+incompatible // indirect
 | 
				
			||||||
 | 
						github.com/docker/docker-credential-helpers v0.8.2 // indirect
 | 
				
			||||||
	github.com/docker/go-connections v0.5.0 // indirect
 | 
						github.com/docker/go-connections v0.5.0 // indirect
 | 
				
			||||||
	github.com/docker/go-units v0.5.0 // indirect
 | 
						github.com/docker/go-units v0.5.0 // indirect
 | 
				
			||||||
	github.com/emicklei/go-restful/v3 v3.12.2 // indirect
 | 
						github.com/emicklei/go-restful/v3 v3.12.2 // indirect
 | 
				
			||||||
| 
						 | 
					@ -144,6 +149,7 @@ require (
 | 
				
			||||||
	github.com/google/cel-go v0.23.2 // indirect
 | 
						github.com/google/cel-go v0.23.2 // indirect
 | 
				
			||||||
	github.com/google/gnostic-models v0.6.9 // indirect
 | 
						github.com/google/gnostic-models v0.6.9 // indirect
 | 
				
			||||||
	github.com/google/go-cmp v0.7.0 // indirect
 | 
						github.com/google/go-cmp v0.7.0 // indirect
 | 
				
			||||||
 | 
						github.com/google/go-containerregistry v0.20.3 // indirect
 | 
				
			||||||
	github.com/google/s2a-go v0.1.9 // indirect
 | 
						github.com/google/s2a-go v0.1.9 // indirect
 | 
				
			||||||
	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
 | 
						github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
 | 
				
			||||||
	github.com/google/uuid v1.6.0 // indirect
 | 
						github.com/google/uuid v1.6.0 // indirect
 | 
				
			||||||
| 
						 | 
					@ -222,7 +228,6 @@ require (
 | 
				
			||||||
	go.uber.org/zap v1.27.0 // indirect
 | 
						go.uber.org/zap v1.27.0 // indirect
 | 
				
			||||||
	golang.org/x/crypto v0.37.0 // indirect
 | 
						golang.org/x/crypto v0.37.0 // indirect
 | 
				
			||||||
	golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
 | 
						golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
 | 
				
			||||||
	golang.org/x/oauth2 v0.29.0 // indirect
 | 
					 | 
				
			||||||
	golang.org/x/sync v0.13.0 // indirect
 | 
						golang.org/x/sync v0.13.0 // indirect
 | 
				
			||||||
	golang.org/x/sys v0.32.0 // indirect
 | 
						golang.org/x/sys v0.32.0 // indirect
 | 
				
			||||||
	golang.org/x/term v0.31.0 // indirect
 | 
						golang.org/x/term v0.31.0 // indirect
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										12
									
								
								go.sum
								
								
								
								
							| 
						 | 
					@ -91,6 +91,8 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d
 | 
				
			||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
 | 
					github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
 | 
				
			||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM=
 | 
					github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM=
 | 
				
			||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
 | 
					github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
 | 
				
			||||||
 | 
					github.com/aws/aws-sdk-go-v2/service/ecr v1.43.3 h1:YyH8Hk73bYzdbvf6S8NF5z/fb/1stpiMnFSfL6jSfRA=
 | 
				
			||||||
 | 
					github.com/aws/aws-sdk-go-v2/service/ecr v1.43.3/go.mod h1:iQ1skgw1XRK+6Lgkb0I9ODatAP72WoTILh0zXQ5DtbU=
 | 
				
			||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
 | 
					github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
 | 
				
			||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
 | 
					github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
 | 
				
			||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 h1:lguz0bmOoGzozP9XfRJR1QIayEYo+2vP/No3OfLF0pU=
 | 
					github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 h1:lguz0bmOoGzozP9XfRJR1QIayEYo+2vP/No3OfLF0pU=
 | 
				
			||||||
| 
						 | 
					@ -129,6 +131,8 @@ github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73l
 | 
				
			||||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
 | 
					github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
 | 
				
			||||||
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
 | 
					github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
 | 
				
			||||||
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
 | 
					github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
 | 
				
			||||||
 | 
					github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
 | 
				
			||||||
 | 
					github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
 | 
				
			||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 | 
					github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 | 
				
			||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
 | 
					github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
 | 
				
			||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
 | 
					github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
 | 
				
			||||||
| 
						 | 
					@ -148,6 +152,8 @@ github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5M
 | 
				
			||||||
github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 | 
					github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 | 
				
			||||||
github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I=
 | 
					github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I=
 | 
				
			||||||
github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
					github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
				
			||||||
 | 
					github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
 | 
				
			||||||
 | 
					github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
 | 
				
			||||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
 | 
					github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
 | 
				
			||||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
 | 
					github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
 | 
				
			||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
 | 
					github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
 | 
				
			||||||
| 
						 | 
					@ -182,6 +188,10 @@ github.com/fluxcd/pkg/apis/kustomize v1.10.0 h1:47EeSzkQvlQZdH92vHMe2lK2iR8aOSEJ
 | 
				
			||||||
github.com/fluxcd/pkg/apis/kustomize v1.10.0/go.mod h1:UsqMV4sqNa1Yg0pmTsdkHRJr7bafBOENIJoAN+3ezaQ=
 | 
					github.com/fluxcd/pkg/apis/kustomize v1.10.0/go.mod h1:UsqMV4sqNa1Yg0pmTsdkHRJr7bafBOENIJoAN+3ezaQ=
 | 
				
			||||||
github.com/fluxcd/pkg/apis/meta v1.11.0 h1:h8q95k6ZEK1HCfsLkt8Np3i6ktb6ZzcWJ6hg++oc9w0=
 | 
					github.com/fluxcd/pkg/apis/meta v1.11.0 h1:h8q95k6ZEK1HCfsLkt8Np3i6ktb6ZzcWJ6hg++oc9w0=
 | 
				
			||||||
github.com/fluxcd/pkg/apis/meta v1.11.0/go.mod h1:+son1Va60x2eiDcTwd7lcctbI6C+K3gM7R+ULmEq1SI=
 | 
					github.com/fluxcd/pkg/apis/meta v1.11.0/go.mod h1:+son1Va60x2eiDcTwd7lcctbI6C+K3gM7R+ULmEq1SI=
 | 
				
			||||||
 | 
					github.com/fluxcd/pkg/auth v0.12.0 h1:35o0ziYMLZVgJwNvJBGsv/wd903B2fMagcrnm1ptUjc=
 | 
				
			||||||
 | 
					github.com/fluxcd/pkg/auth v0.12.0/go.mod h1:gQD2VT5OhIR1E8ZTEsTaho3bDQZidr9P10smH/awcew=
 | 
				
			||||||
 | 
					github.com/fluxcd/pkg/cache v0.9.0 h1:EGKfOLMG3fOwWnH/4Axl5xd425mxoQbZzlZoLfd8PDk=
 | 
				
			||||||
 | 
					github.com/fluxcd/pkg/cache v0.9.0/go.mod h1:jMwabjWfsC5lW8hE7NM3wtGNwSJ38Javx6EKbEi7INU=
 | 
				
			||||||
github.com/fluxcd/pkg/envsubst v1.4.0 h1:pYsb6wrmXOSfHXuXQHaaBBMt3LumhgCb8SMdBNAwV/U=
 | 
					github.com/fluxcd/pkg/envsubst v1.4.0 h1:pYsb6wrmXOSfHXuXQHaaBBMt3LumhgCb8SMdBNAwV/U=
 | 
				
			||||||
github.com/fluxcd/pkg/envsubst v1.4.0/go.mod h1:zSDFO3Wawi+vI2NPxsMQp+EkIsz/85MNg/s1Wzmqt+s=
 | 
					github.com/fluxcd/pkg/envsubst v1.4.0/go.mod h1:zSDFO3Wawi+vI2NPxsMQp+EkIsz/85MNg/s1Wzmqt+s=
 | 
				
			||||||
github.com/fluxcd/pkg/http/fetch v0.16.0 h1:XzhBTSK5HNdAPEnEGMJHwtoN2LfqQ9QFDsu3DGzl908=
 | 
					github.com/fluxcd/pkg/http/fetch v0.16.0 h1:XzhBTSK5HNdAPEnEGMJHwtoN2LfqQ9QFDsu3DGzl908=
 | 
				
			||||||
| 
						 | 
					@ -254,6 +264,8 @@ github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcb
 | 
				
			||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
					github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
				
			||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 | 
					github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 | 
				
			||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 | 
					github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 | 
				
			||||||
 | 
					github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI=
 | 
				
			||||||
 | 
					github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI=
 | 
				
			||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
					github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
				
			||||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
 | 
					github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
 | 
				
			||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
					github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,29 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2025 The Flux 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 intcache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						OperationDecryptWithAWS   = "decrypt_with_aws"
 | 
				
			||||||
 | 
						OperationDecryptWithAzure = "decrypt_with_azure"
 | 
				
			||||||
 | 
						OperationDecryptWithGCP   = "decrypt_with_gcp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var AllOperations = []string{
 | 
				
			||||||
 | 
						OperationDecryptWithAWS,
 | 
				
			||||||
 | 
						OperationDecryptWithAzure,
 | 
				
			||||||
 | 
						OperationDecryptWithGCP,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -27,8 +27,6 @@ import (
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	securejoin "github.com/cyphar/filepath-securejoin"
 | 
						securejoin "github.com/cyphar/filepath-securejoin"
 | 
				
			||||||
	"github.com/fluxcd/pkg/ssa/normalize"
 | 
					 | 
				
			||||||
	ssautil "github.com/fluxcd/pkg/ssa/utils"
 | 
					 | 
				
			||||||
	corev1 "k8s.io/api/core/v1"
 | 
						corev1 "k8s.io/api/core/v1"
 | 
				
			||||||
	apierrors "k8s.io/apimachinery/pkg/api/errors"
 | 
						apierrors "k8s.io/apimachinery/pkg/api/errors"
 | 
				
			||||||
	apimeta "k8s.io/apimachinery/pkg/api/meta"
 | 
						apimeta "k8s.io/apimachinery/pkg/api/meta"
 | 
				
			||||||
| 
						 | 
					@ -54,6 +52,7 @@ import (
 | 
				
			||||||
	apiacl "github.com/fluxcd/pkg/apis/acl"
 | 
						apiacl "github.com/fluxcd/pkg/apis/acl"
 | 
				
			||||||
	eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
 | 
						eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
 | 
				
			||||||
	"github.com/fluxcd/pkg/apis/meta"
 | 
						"github.com/fluxcd/pkg/apis/meta"
 | 
				
			||||||
 | 
						"github.com/fluxcd/pkg/cache"
 | 
				
			||||||
	"github.com/fluxcd/pkg/http/fetch"
 | 
						"github.com/fluxcd/pkg/http/fetch"
 | 
				
			||||||
	generator "github.com/fluxcd/pkg/kustomize"
 | 
						generator "github.com/fluxcd/pkg/kustomize"
 | 
				
			||||||
	"github.com/fluxcd/pkg/runtime/acl"
 | 
						"github.com/fluxcd/pkg/runtime/acl"
 | 
				
			||||||
| 
						 | 
					@ -66,11 +65,14 @@ import (
 | 
				
			||||||
	"github.com/fluxcd/pkg/runtime/predicates"
 | 
						"github.com/fluxcd/pkg/runtime/predicates"
 | 
				
			||||||
	"github.com/fluxcd/pkg/runtime/statusreaders"
 | 
						"github.com/fluxcd/pkg/runtime/statusreaders"
 | 
				
			||||||
	"github.com/fluxcd/pkg/ssa"
 | 
						"github.com/fluxcd/pkg/ssa"
 | 
				
			||||||
 | 
						"github.com/fluxcd/pkg/ssa/normalize"
 | 
				
			||||||
 | 
						ssautil "github.com/fluxcd/pkg/ssa/utils"
 | 
				
			||||||
	"github.com/fluxcd/pkg/tar"
 | 
						"github.com/fluxcd/pkg/tar"
 | 
				
			||||||
	sourcev1 "github.com/fluxcd/source-controller/api/v1"
 | 
						sourcev1 "github.com/fluxcd/source-controller/api/v1"
 | 
				
			||||||
	sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2"
 | 
						sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
 | 
						kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
 | 
				
			||||||
 | 
						intcache "github.com/fluxcd/kustomize-controller/internal/cache"
 | 
				
			||||||
	"github.com/fluxcd/kustomize-controller/internal/decryptor"
 | 
						"github.com/fluxcd/kustomize-controller/internal/decryptor"
 | 
				
			||||||
	"github.com/fluxcd/kustomize-controller/internal/inventory"
 | 
						"github.com/fluxcd/kustomize-controller/internal/inventory"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -81,6 +83,7 @@ import (
 | 
				
			||||||
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=buckets;ocirepositories;gitrepositories,verbs=get;list;watch
 | 
					// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=buckets;ocirepositories;gitrepositories,verbs=get;list;watch
 | 
				
			||||||
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=buckets/status;ocirepositories/status;gitrepositories/status,verbs=get
 | 
					// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=buckets/status;ocirepositories/status;gitrepositories/status,verbs=get
 | 
				
			||||||
// +kubebuilder:rbac:groups="",resources=configmaps;secrets;serviceaccounts,verbs=get;list;watch
 | 
					// +kubebuilder:rbac:groups="",resources=configmaps;secrets;serviceaccounts,verbs=get;list;watch
 | 
				
			||||||
 | 
					// +kubebuilder:rbac:groups="",resources=serviceaccounts/token,verbs=create
 | 
				
			||||||
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
 | 
					// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// KustomizationReconciler reconciles a Kustomization object
 | 
					// KustomizationReconciler reconciles a Kustomization object
 | 
				
			||||||
| 
						 | 
					@ -106,6 +109,7 @@ type KustomizationReconciler struct {
 | 
				
			||||||
	DisallowedFieldManagers []string
 | 
						DisallowedFieldManagers []string
 | 
				
			||||||
	StrictSubstitutions     bool
 | 
						StrictSubstitutions     bool
 | 
				
			||||||
	GroupChangeLog          bool
 | 
						GroupChangeLog          bool
 | 
				
			||||||
 | 
						TokenCache              *cache.TokenCache
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// KustomizationReconcilerOptions contains options for the KustomizationReconciler.
 | 
					// KustomizationReconcilerOptions contains options for the KustomizationReconciler.
 | 
				
			||||||
| 
						 | 
					@ -626,17 +630,20 @@ func (r *KustomizationReconciler) generate(obj unstructured.Unstructured,
 | 
				
			||||||
func (r *KustomizationReconciler) build(ctx context.Context,
 | 
					func (r *KustomizationReconciler) build(ctx context.Context,
 | 
				
			||||||
	obj *kustomizev1.Kustomization, u unstructured.Unstructured,
 | 
						obj *kustomizev1.Kustomization, u unstructured.Unstructured,
 | 
				
			||||||
	workDir, dirPath string) ([]byte, error) {
 | 
						workDir, dirPath string) ([]byte, error) {
 | 
				
			||||||
	dec, cleanup, err := decryptor.NewTempDecryptor(workDir, r.Client, obj)
 | 
						dec, cleanup, err := decryptor.NewTempDecryptor(workDir, r.Client, obj, r.TokenCache)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer cleanup()
 | 
						defer cleanup()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Import decryption keys
 | 
						// Import keys and static credentials for decryption.
 | 
				
			||||||
	if err := dec.ImportKeys(ctx); err != nil {
 | 
						if err := dec.ImportKeys(ctx); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set options for secret-less authentication with cloud providers for decryption.
 | 
				
			||||||
 | 
						dec.SetAuthOptions(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Decrypt Kustomize EnvSources files before build
 | 
						// Decrypt Kustomize EnvSources files before build
 | 
				
			||||||
	if err = dec.DecryptSources(dirPath); err != nil {
 | 
						if err = dec.DecryptSources(dirPath); err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("error decrypting sources: %w", err)
 | 
							return nil, fmt.Errorf("error decrypting sources: %w", err)
 | 
				
			||||||
| 
						 | 
					@ -1090,6 +1097,12 @@ func (r *KustomizationReconciler) finalize(ctx context.Context,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Remove our finalizer from the list and update it
 | 
						// Remove our finalizer from the list and update it
 | 
				
			||||||
	controllerutil.RemoveFinalizer(obj, kustomizev1.KustomizationFinalizer)
 | 
						controllerutil.RemoveFinalizer(obj, kustomizev1.KustomizationFinalizer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Cleanup caches.
 | 
				
			||||||
 | 
						for _, op := range intcache.AllOperations {
 | 
				
			||||||
 | 
							r.TokenCache.DeleteEventsForObject(kustomizev1.KustomizationKind, obj.GetName(), obj.GetNamespace(), op)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Stop reconciliation as the object is being deleted
 | 
						// Stop reconciliation as the object is being deleted
 | 
				
			||||||
	return ctrl.Result{}, nil
 | 
						return ctrl.Result{}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,17 +29,25 @@ import (
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gcpkmsapi "cloud.google.com/go/kms/apiv1"
 | 
				
			||||||
 | 
						"github.com/Azure/azure-sdk-for-go/sdk/azcore"
 | 
				
			||||||
 | 
						awssdk "github.com/aws/aws-sdk-go-v2/aws"
 | 
				
			||||||
	securejoin "github.com/cyphar/filepath-securejoin"
 | 
						securejoin "github.com/cyphar/filepath-securejoin"
 | 
				
			||||||
 | 
						"github.com/fluxcd/pkg/auth"
 | 
				
			||||||
 | 
						"github.com/fluxcd/pkg/auth/aws"
 | 
				
			||||||
 | 
						"github.com/fluxcd/pkg/auth/azure"
 | 
				
			||||||
 | 
						"github.com/fluxcd/pkg/auth/gcp"
 | 
				
			||||||
 | 
						"github.com/fluxcd/pkg/cache"
 | 
				
			||||||
	"github.com/getsops/sops/v3"
 | 
						"github.com/getsops/sops/v3"
 | 
				
			||||||
	"github.com/getsops/sops/v3/aes"
 | 
						"github.com/getsops/sops/v3/aes"
 | 
				
			||||||
	"github.com/getsops/sops/v3/age"
 | 
						"github.com/getsops/sops/v3/age"
 | 
				
			||||||
	"github.com/getsops/sops/v3/azkv"
 | 
					 | 
				
			||||||
	"github.com/getsops/sops/v3/cmd/sops/common"
 | 
						"github.com/getsops/sops/v3/cmd/sops/common"
 | 
				
			||||||
	"github.com/getsops/sops/v3/cmd/sops/formats"
 | 
						"github.com/getsops/sops/v3/cmd/sops/formats"
 | 
				
			||||||
	"github.com/getsops/sops/v3/config"
 | 
						"github.com/getsops/sops/v3/config"
 | 
				
			||||||
	"github.com/getsops/sops/v3/keyservice"
 | 
						"github.com/getsops/sops/v3/keyservice"
 | 
				
			||||||
	awskms "github.com/getsops/sops/v3/kms"
 | 
					 | 
				
			||||||
	"github.com/getsops/sops/v3/pgp"
 | 
						"github.com/getsops/sops/v3/pgp"
 | 
				
			||||||
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
 | 
						"golang.org/x/oauth2/google"
 | 
				
			||||||
	corev1 "k8s.io/api/core/v1"
 | 
						corev1 "k8s.io/api/core/v1"
 | 
				
			||||||
	apierrors "k8s.io/apimachinery/pkg/api/errors"
 | 
						apierrors "k8s.io/apimachinery/pkg/api/errors"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 | 
						"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 | 
				
			||||||
| 
						 | 
					@ -51,6 +59,7 @@ import (
 | 
				
			||||||
	"sigs.k8s.io/yaml"
 | 
						"sigs.k8s.io/yaml"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
 | 
						kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
 | 
				
			||||||
 | 
						intcache "github.com/fluxcd/kustomize-controller/internal/cache"
 | 
				
			||||||
	intawskms "github.com/fluxcd/kustomize-controller/internal/sops/awskms"
 | 
						intawskms "github.com/fluxcd/kustomize-controller/internal/sops/awskms"
 | 
				
			||||||
	intazkv "github.com/fluxcd/kustomize-controller/internal/sops/azkv"
 | 
						intazkv "github.com/fluxcd/kustomize-controller/internal/sops/azkv"
 | 
				
			||||||
	intkeyservice "github.com/fluxcd/kustomize-controller/internal/sops/keyservice"
 | 
						intkeyservice "github.com/fluxcd/kustomize-controller/internal/sops/keyservice"
 | 
				
			||||||
| 
						 | 
					@ -127,6 +136,8 @@ type Decryptor struct {
 | 
				
			||||||
	// injected into most resources, causing the integrity check to fail.
 | 
						// injected into most resources, causing the integrity check to fail.
 | 
				
			||||||
	// Mostly kept around for feature completeness and documentation purposes.
 | 
						// Mostly kept around for feature completeness and documentation purposes.
 | 
				
			||||||
	checkSopsMac bool
 | 
						checkSopsMac bool
 | 
				
			||||||
 | 
						// tokenCache is the cache for token credentials.
 | 
				
			||||||
 | 
						tokenCache *cache.TokenCache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// gnuPGHome is the absolute path of the GnuPG home directory used to
 | 
						// gnuPGHome is the absolute path of the GnuPG home directory used to
 | 
				
			||||||
	// decrypt PGP data. When empty, the systems' GnuPG keyring is used.
 | 
						// decrypt PGP data. When empty, the systems' GnuPG keyring is used.
 | 
				
			||||||
| 
						 | 
					@ -137,15 +148,15 @@ type Decryptor struct {
 | 
				
			||||||
	// vaultToken is the Hashicorp Vault token used to authenticate towards
 | 
						// vaultToken is the Hashicorp Vault token used to authenticate towards
 | 
				
			||||||
	// any Vault server.
 | 
						// any Vault server.
 | 
				
			||||||
	vaultToken string
 | 
						vaultToken string
 | 
				
			||||||
	// awsCredsProvider is the AWS credentials provider object used to authenticate
 | 
						// awsCredentialsProvider is the AWS credentials provider object used to authenticate
 | 
				
			||||||
	// towards any AWS KMS.
 | 
						// towards any AWS KMS.
 | 
				
			||||||
	awsCredsProvider *awskms.CredentialsProvider
 | 
						awsCredentialsProvider func(region string) awssdk.CredentialsProvider
 | 
				
			||||||
	// azureToken is the Azure credential token used to authenticate towards
 | 
						// azureTokenCredential is the Azure credential token used to authenticate towards
 | 
				
			||||||
	// any Azure Key Vault.
 | 
						// any Azure Key Vault.
 | 
				
			||||||
	azureToken *azkv.TokenCredential
 | 
						azureTokenCredential azcore.TokenCredential
 | 
				
			||||||
	// gcpCredsJSON is the JSON credential file of the service account used to
 | 
						// gcpTokenSource is the GCP token source used to authenticate towards
 | 
				
			||||||
	// authenticate towards any GCP KMS.
 | 
						// any GCP KMS.
 | 
				
			||||||
	gcpCredsJSON []byte
 | 
						gcpTokenSource oauth2.TokenSource
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// keyServices are the SOPS keyservice.KeyServiceClient's available to the
 | 
						// keyServices are the SOPS keyservice.KeyServiceClient's available to the
 | 
				
			||||||
	// decryptor.
 | 
						// decryptor.
 | 
				
			||||||
| 
						 | 
					@ -155,25 +166,28 @@ type Decryptor struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewDecryptor creates a new Decryptor for the given kustomization.
 | 
					// NewDecryptor creates a new Decryptor for the given kustomization.
 | 
				
			||||||
// gnuPGHome can be empty, in which case the systems' keyring is used.
 | 
					// gnuPGHome can be empty, in which case the systems' keyring is used.
 | 
				
			||||||
func NewDecryptor(root string, client client.Client, kustomization *kustomizev1.Kustomization, maxFileSize int64, gnuPGHome string) *Decryptor {
 | 
					func NewDecryptor(root string, client client.Client, kustomization *kustomizev1.Kustomization,
 | 
				
			||||||
 | 
						maxFileSize int64, gnuPGHome string, tokenCache *cache.TokenCache) *Decryptor {
 | 
				
			||||||
	return &Decryptor{
 | 
						return &Decryptor{
 | 
				
			||||||
		root:          root,
 | 
							root:          root,
 | 
				
			||||||
		client:        client,
 | 
							client:        client,
 | 
				
			||||||
		kustomization: kustomization,
 | 
							kustomization: kustomization,
 | 
				
			||||||
		maxFileSize:   maxFileSize,
 | 
							maxFileSize:   maxFileSize,
 | 
				
			||||||
		gnuPGHome:     pgp.GnuPGHome(gnuPGHome),
 | 
							gnuPGHome:     pgp.GnuPGHome(gnuPGHome),
 | 
				
			||||||
 | 
							tokenCache:    tokenCache,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewTempDecryptor creates a new Decryptor, with a temporary GnuPG
 | 
					// NewTempDecryptor creates a new Decryptor, with a temporary GnuPG
 | 
				
			||||||
// home directory to Decryptor.ImportKeys() into.
 | 
					// home directory to Decryptor.ImportKeys() into.
 | 
				
			||||||
func NewTempDecryptor(root string, client client.Client, kustomization *kustomizev1.Kustomization) (*Decryptor, func(), error) {
 | 
					func NewTempDecryptor(root string, client client.Client, kustomization *kustomizev1.Kustomization,
 | 
				
			||||||
 | 
						tokenCache *cache.TokenCache) (*Decryptor, func(), error) {
 | 
				
			||||||
	gnuPGHome, err := pgp.NewGnuPGHome()
 | 
						gnuPGHome, err := pgp.NewGnuPGHome()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, fmt.Errorf("cannot create decryptor: %w", err)
 | 
							return nil, nil, fmt.Errorf("cannot create decryptor: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	cleanup := func() { _ = os.RemoveAll(gnuPGHome.String()) }
 | 
						cleanup := func() { _ = os.RemoveAll(gnuPGHome.String()) }
 | 
				
			||||||
	return NewDecryptor(root, client, kustomization, maxEncryptedFileSize, gnuPGHome.String()), cleanup, nil
 | 
						return NewDecryptor(root, client, kustomization, maxEncryptedFileSize, gnuPGHome.String(), tokenCache), cleanup, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsEncryptedSecret checks if the given object is a Kubernetes Secret encrypted
 | 
					// IsEncryptedSecret checks if the given object is a Kubernetes Secret encrypted
 | 
				
			||||||
| 
						 | 
					@ -228,7 +242,6 @@ func (d *Decryptor) ImportKeys(ctx context.Context) error {
 | 
				
			||||||
					return fmt.Errorf("failed to import '%s' data from %s decryption Secret '%s': %w", name, provider, secretName, err)
 | 
										return fmt.Errorf("failed to import '%s' data from %s decryption Secret '%s': %w", name, provider, secretName, err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			case filepath.Ext(DecryptionVaultTokenFileName):
 | 
								case filepath.Ext(DecryptionVaultTokenFileName):
 | 
				
			||||||
				// Make sure we have the absolute name
 | 
					 | 
				
			||||||
				if name == DecryptionVaultTokenFileName {
 | 
									if name == DecryptionVaultTokenFileName {
 | 
				
			||||||
					token := string(value)
 | 
										token := string(value)
 | 
				
			||||||
					token = strings.Trim(strings.TrimSpace(token), "\n")
 | 
										token = strings.Trim(strings.TrimSpace(token), "\n")
 | 
				
			||||||
| 
						 | 
					@ -240,10 +253,9 @@ func (d *Decryptor) ImportKeys(ctx context.Context) error {
 | 
				
			||||||
					if err != nil {
 | 
										if err != nil {
 | 
				
			||||||
						return fmt.Errorf("failed to import '%s' data from %s decryption Secret '%s': %w", name, provider, secretName, err)
 | 
											return fmt.Errorf("failed to import '%s' data from %s decryption Secret '%s': %w", name, provider, secretName, err)
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					d.awsCredsProvider = awskms.NewCredentialsProvider(awsCreds)
 | 
										d.awsCredentialsProvider = func(string) awssdk.CredentialsProvider { return awsCreds }
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			case filepath.Ext(DecryptionAzureAuthFile):
 | 
								case filepath.Ext(DecryptionAzureAuthFile):
 | 
				
			||||||
				// Make sure we have the absolute name
 | 
					 | 
				
			||||||
				if name == DecryptionAzureAuthFile {
 | 
									if name == DecryptionAzureAuthFile {
 | 
				
			||||||
					conf := intazkv.AADConfig{}
 | 
										conf := intazkv.AADConfig{}
 | 
				
			||||||
					if err = intazkv.LoadAADConfigFromBytes(value, &conf); err != nil {
 | 
										if err = intazkv.LoadAADConfigFromBytes(value, &conf); err != nil {
 | 
				
			||||||
| 
						 | 
					@ -253,11 +265,16 @@ func (d *Decryptor) ImportKeys(ctx context.Context) error {
 | 
				
			||||||
					if err != nil {
 | 
										if err != nil {
 | 
				
			||||||
						return fmt.Errorf("failed to import '%s' data from %s decryption Secret '%s': %w", name, provider, secretName, err)
 | 
											return fmt.Errorf("failed to import '%s' data from %s decryption Secret '%s': %w", name, provider, secretName, err)
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					d.azureToken = azkv.NewTokenCredential(azureToken)
 | 
										d.azureTokenCredential = azureToken
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			case filepath.Ext(DecryptionGCPCredsFile):
 | 
								case filepath.Ext(DecryptionGCPCredsFile):
 | 
				
			||||||
				if name == DecryptionGCPCredsFile {
 | 
									if name == DecryptionGCPCredsFile {
 | 
				
			||||||
					d.gcpCredsJSON = bytes.Trim(value, "\n")
 | 
										creds, err := google.CredentialsFromJSON(ctx,
 | 
				
			||||||
 | 
											bytes.Trim(value, "\n"), gcpkmsapi.DefaultAuthScopes()...)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											return fmt.Errorf("failed to import '%s' data from %s decryption Secret '%s': %w", name, provider, secretName, err)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										d.gcpTokenSource = creds.TokenSource
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -265,6 +282,63 @@ func (d *Decryptor) ImportKeys(ctx context.Context) error {
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetAuthOptions sets the authentication options for secret-less authentication
 | 
				
			||||||
 | 
					// with cloud providers.
 | 
				
			||||||
 | 
					func (d *Decryptor) SetAuthOptions(ctx context.Context) {
 | 
				
			||||||
 | 
						if d.kustomization.Spec.Decryption == nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch d.kustomization.Spec.Decryption.Provider {
 | 
				
			||||||
 | 
						case DecryptionProviderSOPS:
 | 
				
			||||||
 | 
							var opts []auth.Option
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if d.kustomization.Spec.Decryption.ServiceAccountName != "" {
 | 
				
			||||||
 | 
								serviceAccount := types.NamespacedName{
 | 
				
			||||||
 | 
									Name:      d.kustomization.Spec.Decryption.ServiceAccountName,
 | 
				
			||||||
 | 
									Namespace: d.kustomization.GetNamespace(),
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								opts = append(opts, auth.WithServiceAccount(serviceAccount, d.client))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							involvedObject := cache.InvolvedObject{
 | 
				
			||||||
 | 
								Kind:      kustomizev1.KustomizationKind,
 | 
				
			||||||
 | 
								Name:      d.kustomization.GetName(),
 | 
				
			||||||
 | 
								Namespace: d.kustomization.GetNamespace(),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if d.awsCredentialsProvider == nil {
 | 
				
			||||||
 | 
								awsOpts := opts
 | 
				
			||||||
 | 
								if d.tokenCache != nil {
 | 
				
			||||||
 | 
									involvedObject.Operation = intcache.OperationDecryptWithAWS
 | 
				
			||||||
 | 
									awsOpts = append(awsOpts, auth.WithCache(*d.tokenCache, involvedObject))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								d.awsCredentialsProvider = func(region string) awssdk.CredentialsProvider {
 | 
				
			||||||
 | 
									awsOpts := append(awsOpts, auth.WithSTSRegion(region))
 | 
				
			||||||
 | 
									return aws.NewCredentialsProvider(ctx, awsOpts...)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if d.azureTokenCredential == nil {
 | 
				
			||||||
 | 
								azureOpts := opts
 | 
				
			||||||
 | 
								if d.tokenCache != nil {
 | 
				
			||||||
 | 
									involvedObject.Operation = intcache.OperationDecryptWithAzure
 | 
				
			||||||
 | 
									azureOpts = append(azureOpts, auth.WithCache(*d.tokenCache, involvedObject))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								d.azureTokenCredential = azure.NewTokenCredential(ctx, azureOpts...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if d.gcpTokenSource == nil {
 | 
				
			||||||
 | 
								gcpOpts := opts
 | 
				
			||||||
 | 
								if d.tokenCache != nil {
 | 
				
			||||||
 | 
									involvedObject.Operation = intcache.OperationDecryptWithGCP
 | 
				
			||||||
 | 
									gcpOpts = append(gcpOpts, auth.WithCache(*d.tokenCache, involvedObject))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								d.gcpTokenSource = gcp.NewTokenSource(ctx, gcpOpts...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SopsDecryptWithFormat attempts to load a SOPS encrypted file using the store
 | 
					// SopsDecryptWithFormat attempts to load a SOPS encrypted file using the store
 | 
				
			||||||
// for the input format, gathers the data key for it from the key service,
 | 
					// for the input format, gathers the data key for it from the key service,
 | 
				
			||||||
// and then decrypts the file data with the retrieved data key.
 | 
					// and then decrypts the file data with the retrieved data key.
 | 
				
			||||||
| 
						 | 
					@ -582,12 +656,10 @@ func (d *Decryptor) loadKeyServiceServer() {
 | 
				
			||||||
		intkeyservice.WithGnuPGHome(d.gnuPGHome),
 | 
							intkeyservice.WithGnuPGHome(d.gnuPGHome),
 | 
				
			||||||
		intkeyservice.WithVaultToken(d.vaultToken),
 | 
							intkeyservice.WithVaultToken(d.vaultToken),
 | 
				
			||||||
		intkeyservice.WithAgeIdentities(d.ageIdentities),
 | 
							intkeyservice.WithAgeIdentities(d.ageIdentities),
 | 
				
			||||||
		intkeyservice.WithGCPCredsJSON(d.gcpCredsJSON),
 | 
							intkeyservice.WithAWSCredentialsProvider{CredentialsProvider: d.awsCredentialsProvider},
 | 
				
			||||||
 | 
							intkeyservice.WithAzureTokenCredential{TokenCredential: d.azureTokenCredential},
 | 
				
			||||||
 | 
							intkeyservice.WithGCPTokenSource{TokenSource: d.gcpTokenSource},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if d.azureToken != nil {
 | 
					 | 
				
			||||||
		serverOpts = append(serverOpts, intkeyservice.WithAzureToken{Token: d.azureToken})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	serverOpts = append(serverOpts, intkeyservice.WithAWSKeys{CredsProvider: d.awsCredsProvider})
 | 
					 | 
				
			||||||
	server := intkeyservice.NewServer(serverOpts...)
 | 
						server := intkeyservice.NewServer(serverOpts...)
 | 
				
			||||||
	d.keyServices = append(make([]keyservice.KeyServiceClient, 0), keyservice.NewCustomLocalClient(server))
 | 
						d.keyServices = append(make([]keyservice.KeyServiceClient, 0), keyservice.NewCustomLocalClient(server))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -210,7 +210,7 @@ aws_session_token: test-token`),
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			inspectFunc: func(g *GomegaWithT, decryptor *Decryptor) {
 | 
								inspectFunc: func(g *GomegaWithT, decryptor *Decryptor) {
 | 
				
			||||||
				g.Expect(decryptor.awsCredsProvider).ToNot(BeNil())
 | 
									g.Expect(decryptor.awsCredentialsProvider).ToNot(BeNil())
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
| 
						 | 
					@ -233,7 +233,7 @@ aws_session_token: test-token`),
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			inspectFunc: func(g *GomegaWithT, decryptor *Decryptor) {
 | 
								inspectFunc: func(g *GomegaWithT, decryptor *Decryptor) {
 | 
				
			||||||
				g.Expect(decryptor.gcpCredsJSON).ToNot(BeNil())
 | 
									g.Expect(decryptor.gcpTokenSource).ToNot(BeNil())
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
| 
						 | 
					@ -256,7 +256,7 @@ clientSecret: some-client-secret`),
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			inspectFunc: func(g *GomegaWithT, decryptor *Decryptor) {
 | 
								inspectFunc: func(g *GomegaWithT, decryptor *Decryptor) {
 | 
				
			||||||
				g.Expect(decryptor.azureToken).ToNot(BeNil())
 | 
									g.Expect(decryptor.azureTokenCredential).ToNot(BeNil())
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
| 
						 | 
					@ -278,7 +278,7 @@ clientSecret: some-client-secret`),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			wantErr: true,
 | 
								wantErr: true,
 | 
				
			||||||
			inspectFunc: func(g *GomegaWithT, decryptor *Decryptor) {
 | 
								inspectFunc: func(g *GomegaWithT, decryptor *Decryptor) {
 | 
				
			||||||
				g.Expect(decryptor.azureToken).To(BeNil())
 | 
									g.Expect(decryptor.azureTokenCredential).To(BeNil())
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
| 
						 | 
					@ -300,7 +300,7 @@ clientSecret: some-client-secret`),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			wantErr: true,
 | 
								wantErr: true,
 | 
				
			||||||
			inspectFunc: func(g *GomegaWithT, decryptor *Decryptor) {
 | 
								inspectFunc: func(g *GomegaWithT, decryptor *Decryptor) {
 | 
				
			||||||
				g.Expect(decryptor.azureToken).To(BeNil())
 | 
									g.Expect(decryptor.azureTokenCredential).To(BeNil())
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
| 
						 | 
					@ -376,7 +376,7 @@ clientSecret: some-client-secret`),
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			d, cleanup, err := NewTempDecryptor("", cb.Build(), &kustomization)
 | 
								d, cleanup, err := NewTempDecryptor("", cb.Build(), &kustomization, nil)
 | 
				
			||||||
			g.Expect(err).ToNot(HaveOccurred())
 | 
								g.Expect(err).ToNot(HaveOccurred())
 | 
				
			||||||
			t.Cleanup(cleanup)
 | 
								t.Cleanup(cleanup)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -393,6 +393,60 @@ clientSecret: some-client-secret`),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDecryptor_SetAuthOptions(t *testing.T) {
 | 
				
			||||||
 | 
						t.Run("nil decryption settings", func(t *testing.T) {
 | 
				
			||||||
 | 
							g := NewWithT(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							d := &Decryptor{
 | 
				
			||||||
 | 
								kustomization: &kustomizev1.Kustomization{},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							d.SetAuthOptions(context.Background())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							g.Expect(d.awsCredentialsProvider).To(BeNil())
 | 
				
			||||||
 | 
							g.Expect(d.azureTokenCredential).To(BeNil())
 | 
				
			||||||
 | 
							g.Expect(d.gcpTokenSource).To(BeNil())
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("non-sops provider", func(t *testing.T) {
 | 
				
			||||||
 | 
							g := NewWithT(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							d := &Decryptor{
 | 
				
			||||||
 | 
								kustomization: &kustomizev1.Kustomization{
 | 
				
			||||||
 | 
									Spec: kustomizev1.KustomizationSpec{
 | 
				
			||||||
 | 
										Decryption: &kustomizev1.Decryption{},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							d.SetAuthOptions(context.Background())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							g.Expect(d.awsCredentialsProvider).To(BeNil())
 | 
				
			||||||
 | 
							g.Expect(d.azureTokenCredential).To(BeNil())
 | 
				
			||||||
 | 
							g.Expect(d.gcpTokenSource).To(BeNil())
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("sops provider", func(t *testing.T) {
 | 
				
			||||||
 | 
							g := NewWithT(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							d := &Decryptor{
 | 
				
			||||||
 | 
								kustomization: &kustomizev1.Kustomization{
 | 
				
			||||||
 | 
									Spec: kustomizev1.KustomizationSpec{
 | 
				
			||||||
 | 
										Decryption: &kustomizev1.Decryption{
 | 
				
			||||||
 | 
											Provider: DecryptionProviderSOPS,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							d.SetAuthOptions(context.Background())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							g.Expect(d.awsCredentialsProvider).NotTo(BeNil())
 | 
				
			||||||
 | 
							g.Expect(d.azureTokenCredential).NotTo(BeNil())
 | 
				
			||||||
 | 
							g.Expect(d.gcpTokenSource).NotTo(BeNil())
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestDecryptor_SopsDecryptWithFormat(t *testing.T) {
 | 
					func TestDecryptor_SopsDecryptWithFormat(t *testing.T) {
 | 
				
			||||||
	t.Run("decrypt INI to INI", func(t *testing.T) {
 | 
						t.Run("decrypt INI to INI", func(t *testing.T) {
 | 
				
			||||||
		g := NewWithT(t)
 | 
							g := NewWithT(t)
 | 
				
			||||||
| 
						 | 
					@ -551,7 +605,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
 | 
				
			||||||
			Provider: DecryptionProviderSOPS,
 | 
								Provider: DecryptionProviderSOPS,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus)
 | 
							d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus, nil)
 | 
				
			||||||
		g.Expect(err).ToNot(HaveOccurred())
 | 
							g.Expect(err).ToNot(HaveOccurred())
 | 
				
			||||||
		t.Cleanup(cleanup)
 | 
							t.Cleanup(cleanup)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -592,7 +646,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
 | 
				
			||||||
			Provider: DecryptionProviderSOPS,
 | 
								Provider: DecryptionProviderSOPS,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus)
 | 
							d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus, nil)
 | 
				
			||||||
		g.Expect(err).ToNot(HaveOccurred())
 | 
							g.Expect(err).ToNot(HaveOccurred())
 | 
				
			||||||
		t.Cleanup(cleanup)
 | 
							t.Cleanup(cleanup)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -627,7 +681,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
 | 
				
			||||||
			Provider: DecryptionProviderSOPS,
 | 
								Provider: DecryptionProviderSOPS,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus)
 | 
							d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus, nil)
 | 
				
			||||||
		g.Expect(err).ToNot(HaveOccurred())
 | 
							g.Expect(err).ToNot(HaveOccurred())
 | 
				
			||||||
		t.Cleanup(cleanup)
 | 
							t.Cleanup(cleanup)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -662,7 +716,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
 | 
				
			||||||
			Provider: DecryptionProviderSOPS,
 | 
								Provider: DecryptionProviderSOPS,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus)
 | 
							d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus, nil)
 | 
				
			||||||
		g.Expect(err).ToNot(HaveOccurred())
 | 
							g.Expect(err).ToNot(HaveOccurred())
 | 
				
			||||||
		t.Cleanup(cleanup)
 | 
							t.Cleanup(cleanup)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -711,7 +765,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
 | 
				
			||||||
	t.Run("nil resource", func(t *testing.T) {
 | 
						t.Run("nil resource", func(t *testing.T) {
 | 
				
			||||||
		g := NewWithT(t)
 | 
							g := NewWithT(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kustomization.DeepCopy())
 | 
							d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kustomization.DeepCopy(), nil)
 | 
				
			||||||
		g.Expect(err).ToNot(HaveOccurred())
 | 
							g.Expect(err).ToNot(HaveOccurred())
 | 
				
			||||||
		t.Cleanup(cleanup)
 | 
							t.Cleanup(cleanup)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -723,7 +777,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
 | 
				
			||||||
	t.Run("no decryption spec", func(t *testing.T) {
 | 
						t.Run("no decryption spec", func(t *testing.T) {
 | 
				
			||||||
		g := NewWithT(t)
 | 
							g := NewWithT(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kustomization.DeepCopy())
 | 
							d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kustomization.DeepCopy(), nil)
 | 
				
			||||||
		g.Expect(err).ToNot(HaveOccurred())
 | 
							g.Expect(err).ToNot(HaveOccurred())
 | 
				
			||||||
		t.Cleanup(cleanup)
 | 
							t.Cleanup(cleanup)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -739,7 +793,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
 | 
				
			||||||
		kus.Spec.Decryption = &kustomizev1.Decryption{
 | 
							kus.Spec.Decryption = &kustomizev1.Decryption{
 | 
				
			||||||
			Provider: "not-supported",
 | 
								Provider: "not-supported",
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus)
 | 
							d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus, nil)
 | 
				
			||||||
		g.Expect(err).ToNot(HaveOccurred())
 | 
							g.Expect(err).ToNot(HaveOccurred())
 | 
				
			||||||
		t.Cleanup(cleanup)
 | 
							t.Cleanup(cleanup)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2025 The Flux 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 awskms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetRegionFromKMSARN extracts the region from a KMS ARN.
 | 
				
			||||||
 | 
					func GetRegionFromKMSARN(arn string) string {
 | 
				
			||||||
 | 
						arn = strings.TrimPrefix(arn, "arn:aws:kms:")
 | 
				
			||||||
 | 
						return strings.SplitN(arn, ":", 2)[0]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2025 The Flux 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 awskms_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						. "github.com/onsi/gomega"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/fluxcd/kustomize-controller/internal/sops/awskms"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetRegionFromKMSARN(t *testing.T) {
 | 
				
			||||||
 | 
						g := NewWithT(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						arn := "arn:aws:kms:us-east-1:211125720409:key/mrk-3179bb7e88bc42ffb1a27d5038ceea25"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						region := awskms.GetRegionFromKMSARN(arn)
 | 
				
			||||||
 | 
						g.Expect(region).To(Equal("us-east-1"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,103 +0,0 @@
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
Copyright 2023 The Flux 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 azkv
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
 | 
					 | 
				
			||||||
	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DefaultTokenCredential is a modification of azidentity.NewDefaultAzureCredential,
 | 
					 | 
				
			||||||
// specifically adapted to not shell out to the Azure CLI.
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// It attempts to return an azcore.TokenCredential based on the following order:
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
//   - azidentity.NewEnvironmentCredential if environment variables AZURE_CLIENT_ID,
 | 
					 | 
				
			||||||
//     AZURE_CLIENT_ID is set with either one of the following: (AZURE_CLIENT_SECRET)
 | 
					 | 
				
			||||||
//     or (AZURE_CLIENT_CERTIFICATE_PATH and AZURE_CLIENT_CERTIFICATE_PATH) or
 | 
					 | 
				
			||||||
//     (AZURE_USERNAME, AZURE_PASSWORD)
 | 
					 | 
				
			||||||
//   - azidentity.WorkloadIdentityCredential if environment variable configuration
 | 
					 | 
				
			||||||
//     (AZURE_AUTHORITY_HOST, AZURE_CLIENT_ID, AZURE_FEDERATED_TOKEN_FILE, AZURE_TENANT_ID)
 | 
					 | 
				
			||||||
//     is set by the Azure workload identity webhook.
 | 
					 | 
				
			||||||
//   - azidentity.ManagedIdentityCredential if only AZURE_CLIENT_ID env variable is set.
 | 
					 | 
				
			||||||
func DefaultTokenCredential() (azcore.TokenCredential, error) {
 | 
					 | 
				
			||||||
	var (
 | 
					 | 
				
			||||||
		azureClientID           = "AZURE_CLIENT_ID"
 | 
					 | 
				
			||||||
		azureFederatedTokenFile = "AZURE_FEDERATED_TOKEN_FILE"
 | 
					 | 
				
			||||||
		azureAuthorityHost      = "AZURE_AUTHORITY_HOST"
 | 
					 | 
				
			||||||
		azureTenantID           = "AZURE_TENANT_ID"
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var errorMessages []string
 | 
					 | 
				
			||||||
	options := &azidentity.DefaultAzureCredentialOptions{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	envCred, err := azidentity.NewEnvironmentCredential(&azidentity.EnvironmentCredentialOptions{
 | 
					 | 
				
			||||||
		ClientOptions: options.ClientOptions, DisableInstanceDiscovery: options.DisableInstanceDiscovery},
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		return envCred, nil
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		errorMessages = append(errorMessages, "EnvironmentCredential: "+err.Error())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// workload identity requires values for AZURE_AUTHORITY_HOST, AZURE_CLIENT_ID, AZURE_FEDERATED_TOKEN_FILE, AZURE_TENANT_ID
 | 
					 | 
				
			||||||
	haveWorkloadConfig := false
 | 
					 | 
				
			||||||
	clientID, haveClientID := os.LookupEnv(azureClientID)
 | 
					 | 
				
			||||||
	if haveClientID {
 | 
					 | 
				
			||||||
		if file, ok := os.LookupEnv(azureFederatedTokenFile); ok {
 | 
					 | 
				
			||||||
			if _, ok := os.LookupEnv(azureAuthorityHost); ok {
 | 
					 | 
				
			||||||
				if tenantID, ok := os.LookupEnv(azureTenantID); ok {
 | 
					 | 
				
			||||||
					haveWorkloadConfig = true
 | 
					 | 
				
			||||||
					workloadCred, err := azidentity.NewWorkloadIdentityCredential(&azidentity.WorkloadIdentityCredentialOptions{
 | 
					 | 
				
			||||||
						ClientID:                 clientID,
 | 
					 | 
				
			||||||
						TenantID:                 tenantID,
 | 
					 | 
				
			||||||
						TokenFilePath:            file,
 | 
					 | 
				
			||||||
						ClientOptions:            options.ClientOptions,
 | 
					 | 
				
			||||||
						DisableInstanceDiscovery: options.DisableInstanceDiscovery,
 | 
					 | 
				
			||||||
					})
 | 
					 | 
				
			||||||
					if err == nil {
 | 
					 | 
				
			||||||
						return workloadCred, nil
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						errorMessages = append(errorMessages, "Workload Identity"+": "+err.Error())
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if !haveWorkloadConfig {
 | 
					 | 
				
			||||||
		err := errors.New("missing environment variables for workload identity. Check webhook and pod configuration")
 | 
					 | 
				
			||||||
		errorMessages = append(errorMessages, fmt.Sprintf("Workload Identity: %s", err))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	o := &azidentity.ManagedIdentityCredentialOptions{ClientOptions: options.ClientOptions}
 | 
					 | 
				
			||||||
	if haveClientID {
 | 
					 | 
				
			||||||
		o.ID = azidentity.ClientID(clientID)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	miCred, err := azidentity.NewManagedIdentityCredential(o)
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		return miCred, nil
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		errorMessages = append(errorMessages, "ManagedIdentity"+": "+err.Error())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil, errors.New(strings.Join(errorMessages, "\n"))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,8 @@ package keyservice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	extage "filippo.io/age"
 | 
						extage "filippo.io/age"
 | 
				
			||||||
 | 
						"github.com/Azure/azure-sdk-for-go/sdk/azcore"
 | 
				
			||||||
 | 
						awssdk "github.com/aws/aws-sdk-go-v2/aws"
 | 
				
			||||||
	"github.com/getsops/sops/v3/age"
 | 
						"github.com/getsops/sops/v3/age"
 | 
				
			||||||
	"github.com/getsops/sops/v3/azkv"
 | 
						"github.com/getsops/sops/v3/azkv"
 | 
				
			||||||
	"github.com/getsops/sops/v3/gcpkms"
 | 
						"github.com/getsops/sops/v3/gcpkms"
 | 
				
			||||||
| 
						 | 
					@ -25,6 +27,9 @@ import (
 | 
				
			||||||
	"github.com/getsops/sops/v3/keyservice"
 | 
						"github.com/getsops/sops/v3/keyservice"
 | 
				
			||||||
	awskms "github.com/getsops/sops/v3/kms"
 | 
						awskms "github.com/getsops/sops/v3/kms"
 | 
				
			||||||
	"github.com/getsops/sops/v3/pgp"
 | 
						"github.com/getsops/sops/v3/pgp"
 | 
				
			||||||
 | 
						"golang.org/x/oauth2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						intawskms "github.com/fluxcd/kustomize-controller/internal/sops/awskms"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ServerOption is some configuration that modifies the Server.
 | 
					// ServerOption is some configuration that modifies the Server.
 | 
				
			||||||
| 
						 | 
					@ -57,33 +62,38 @@ func (o WithAgeIdentities) ApplyToServer(s *Server) {
 | 
				
			||||||
	s.ageIdentities = age.ParsedIdentities(o)
 | 
						s.ageIdentities = age.ParsedIdentities(o)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithAWSKeys configures the AWS credentials on the Server
 | 
					// WithAWSCredentialsProvider configures the AWS credentials on the Server
 | 
				
			||||||
type WithAWSKeys struct {
 | 
					type WithAWSCredentialsProvider struct {
 | 
				
			||||||
	CredsProvider *awskms.CredentialsProvider
 | 
						CredentialsProvider func(region string) awssdk.CredentialsProvider
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ApplyToServer applies this configuration to the given Server.
 | 
					// ApplyToServer applies this configuration to the given Server.
 | 
				
			||||||
func (o WithAWSKeys) ApplyToServer(s *Server) {
 | 
					func (o WithAWSCredentialsProvider) ApplyToServer(s *Server) {
 | 
				
			||||||
	s.awsCredsProvider = o.CredsProvider
 | 
						s.awsCredentialsProvider = func(arn string) *awskms.CredentialsProvider {
 | 
				
			||||||
 | 
							region := intawskms.GetRegionFromKMSARN(arn)
 | 
				
			||||||
 | 
							cp := o.CredentialsProvider(region)
 | 
				
			||||||
 | 
							return awskms.NewCredentialsProvider(cp)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithGCPCredsJSON configures the GCP service account credentials JSON on the
 | 
					// WithGCPTokenSource configures the GCP token source on the Server.
 | 
				
			||||||
// Server.
 | 
					type WithGCPTokenSource struct {
 | 
				
			||||||
type WithGCPCredsJSON []byte
 | 
						TokenSource oauth2.TokenSource
 | 
				
			||||||
 | 
					 | 
				
			||||||
// ApplyToServer applies this configuration to the given Server.
 | 
					 | 
				
			||||||
func (o WithGCPCredsJSON) ApplyToServer(s *Server) {
 | 
					 | 
				
			||||||
	s.gcpCredsJSON = gcpkms.CredentialJSON(o)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// WithAzureToken configures the Azure credential token on the Server.
 | 
					 | 
				
			||||||
type WithAzureToken struct {
 | 
					 | 
				
			||||||
	Token *azkv.TokenCredential
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ApplyToServer applies this configuration to the given Server.
 | 
					// ApplyToServer applies this configuration to the given Server.
 | 
				
			||||||
func (o WithAzureToken) ApplyToServer(s *Server) {
 | 
					func (o WithGCPTokenSource) ApplyToServer(s *Server) {
 | 
				
			||||||
	s.azureToken = o.Token
 | 
						s.gcpTokenSource = gcpkms.NewTokenSource(o.TokenSource)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WithAzureTokenCredential configures the Azure credential token on the Server.
 | 
				
			||||||
 | 
					type WithAzureTokenCredential struct {
 | 
				
			||||||
 | 
						TokenCredential azcore.TokenCredential
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ApplyToServer applies this configuration to the given Server.
 | 
				
			||||||
 | 
					func (o WithAzureTokenCredential) ApplyToServer(s *Server) {
 | 
				
			||||||
 | 
						s.azureTokenCredential = azkv.NewTokenCredential(o.TokenCredential)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithDefaultServer configures the fallback default server on the Server.
 | 
					// WithDefaultServer configures the fallback default server on the Server.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,8 +28,6 @@ import (
 | 
				
			||||||
	"github.com/getsops/sops/v3/logging"
 | 
						"github.com/getsops/sops/v3/logging"
 | 
				
			||||||
	"github.com/getsops/sops/v3/pgp"
 | 
						"github.com/getsops/sops/v3/pgp"
 | 
				
			||||||
	"golang.org/x/net/context"
 | 
						"golang.org/x/net/context"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	intazkv "github.com/fluxcd/kustomize-controller/internal/sops/azkv"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Server is a key service server that uses SOPS MasterKeys to fulfill
 | 
					// Server is a key service server that uses SOPS MasterKeys to fulfill
 | 
				
			||||||
| 
						 | 
					@ -54,20 +52,19 @@ type Server struct {
 | 
				
			||||||
	// When empty, the request will be handled by defaultServer.
 | 
						// When empty, the request will be handled by defaultServer.
 | 
				
			||||||
	vaultToken hcvault.Token
 | 
						vaultToken hcvault.Token
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// azureToken is the credential token used for Encrypt and Decrypt
 | 
						// azureTokenCredential is the credential token used for Encrypt and Decrypt
 | 
				
			||||||
	// operations of Azure Key Vault requests.
 | 
						// operations of Azure Key Vault requests.
 | 
				
			||||||
	// When nil, the request will be handled by defaultServer.
 | 
						// When nil, the request will be handled by defaultServer.
 | 
				
			||||||
	azureToken *azkv.TokenCredential
 | 
						azureTokenCredential *azkv.TokenCredential
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// awsCredsProvider is the Credentials object used for Encrypt and Decrypt
 | 
						// awsCredentialsProvider is the Credentials object used for Encrypt and Decrypt
 | 
				
			||||||
	// operations of AWS KMS requests.
 | 
						// operations of AWS KMS requests.
 | 
				
			||||||
	// When nil, the request will be handled by defaultServer.
 | 
						// When nil, the request will be handled by defaultServer.
 | 
				
			||||||
	awsCredsProvider *awskms.CredentialsProvider
 | 
						awsCredentialsProvider func(arn string) *awskms.CredentialsProvider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// gcpCredsJSON is the JSON credentials used for Decrypt and Encrypt
 | 
						// gcpTokenSource is the token source used for Encrypt and Decrypt
 | 
				
			||||||
	// operations of GCP KMS requests. When nil, a default client with
 | 
						// operations of GCP KMS requests.
 | 
				
			||||||
	// environmental runtime settings will be used.
 | 
						gcpTokenSource gcpkms.TokenSource
 | 
				
			||||||
	gcpCredsJSON gcpkms.CredentialJSON
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// defaultServer is the fallback server, used to handle any request that
 | 
						// defaultServer is the fallback server, used to handle any request that
 | 
				
			||||||
	// is not eligible to be handled by this Server.
 | 
						// is not eligible to be handled by this Server.
 | 
				
			||||||
| 
						 | 
					@ -296,9 +293,7 @@ func (ks *Server) decryptWithHCVault(key *keyservice.VaultKey, ciphertext []byte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ks *Server) encryptWithAWSKMS(key *keyservice.KmsKey, plaintext []byte) ([]byte, error) {
 | 
					func (ks *Server) encryptWithAWSKMS(key *keyservice.KmsKey, plaintext []byte) ([]byte, error) {
 | 
				
			||||||
	awsKey := kmsKeyToMasterKey(key)
 | 
						awsKey := kmsKeyToMasterKey(key)
 | 
				
			||||||
	if ks.awsCredsProvider != nil {
 | 
						ks.awsCredentialsProvider(key.Arn).ApplyToMasterKey(&awsKey)
 | 
				
			||||||
		ks.awsCredsProvider.ApplyToMasterKey(&awsKey)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := awsKey.Encrypt(plaintext); err != nil {
 | 
						if err := awsKey.Encrypt(plaintext); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -308,9 +303,7 @@ func (ks *Server) encryptWithAWSKMS(key *keyservice.KmsKey, plaintext []byte) ([
 | 
				
			||||||
func (ks *Server) decryptWithAWSKMS(key *keyservice.KmsKey, cipherText []byte) ([]byte, error) {
 | 
					func (ks *Server) decryptWithAWSKMS(key *keyservice.KmsKey, cipherText []byte) ([]byte, error) {
 | 
				
			||||||
	awsKey := kmsKeyToMasterKey(key)
 | 
						awsKey := kmsKeyToMasterKey(key)
 | 
				
			||||||
	awsKey.EncryptedKey = string(cipherText)
 | 
						awsKey.EncryptedKey = string(cipherText)
 | 
				
			||||||
	if ks.awsCredsProvider != nil {
 | 
						ks.awsCredentialsProvider(key.Arn).ApplyToMasterKey(&awsKey)
 | 
				
			||||||
		ks.awsCredsProvider.ApplyToMasterKey(&awsKey)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return awsKey.Decrypt()
 | 
						return awsKey.Decrypt()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -320,17 +313,7 @@ func (ks *Server) encryptWithAzureKeyVault(key *keyservice.AzureKeyVaultKey, pla
 | 
				
			||||||
		Name:     key.Name,
 | 
							Name:     key.Name,
 | 
				
			||||||
		Version:  key.Version,
 | 
							Version:  key.Version,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if ks.azureToken == nil {
 | 
						ks.azureTokenCredential.ApplyToMasterKey(&azureKey)
 | 
				
			||||||
		// Ensure we use the default token credential if none is provided
 | 
					 | 
				
			||||||
		// _without_ shelling out to `az`.
 | 
					 | 
				
			||||||
		defaultToken, err := intazkv.DefaultTokenCredential()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, fmt.Errorf("failed to get Azure token credential to encrypt data: %w", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		azkv.NewTokenCredential(defaultToken).ApplyToMasterKey(&azureKey)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		ks.azureToken.ApplyToMasterKey(&azureKey)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := azureKey.Encrypt(plaintext); err != nil {
 | 
						if err := azureKey.Encrypt(plaintext); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -343,17 +326,7 @@ func (ks *Server) decryptWithAzureKeyVault(key *keyservice.AzureKeyVaultKey, cip
 | 
				
			||||||
		Name:     key.Name,
 | 
							Name:     key.Name,
 | 
				
			||||||
		Version:  key.Version,
 | 
							Version:  key.Version,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if ks.azureToken == nil {
 | 
						ks.azureTokenCredential.ApplyToMasterKey(&azureKey)
 | 
				
			||||||
		// Ensure we use the default token credential if none is provided
 | 
					 | 
				
			||||||
		// _without_ shelling out to `az`.
 | 
					 | 
				
			||||||
		defaultToken, err := intazkv.DefaultTokenCredential()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, fmt.Errorf("failed to get Azure token credential to decrypt data: %w", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		azkv.NewTokenCredential(defaultToken).ApplyToMasterKey(&azureKey)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		ks.azureToken.ApplyToMasterKey(&azureKey)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	azureKey.EncryptedKey = string(ciphertext)
 | 
						azureKey.EncryptedKey = string(ciphertext)
 | 
				
			||||||
	plaintext, err := azureKey.Decrypt()
 | 
						plaintext, err := azureKey.Decrypt()
 | 
				
			||||||
	return plaintext, err
 | 
						return plaintext, err
 | 
				
			||||||
| 
						 | 
					@ -363,7 +336,7 @@ func (ks *Server) encryptWithGCPKMS(key *keyservice.GcpKmsKey, plaintext []byte)
 | 
				
			||||||
	gcpKey := gcpkms.MasterKey{
 | 
						gcpKey := gcpkms.MasterKey{
 | 
				
			||||||
		ResourceID: key.ResourceId,
 | 
							ResourceID: key.ResourceId,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ks.gcpCredsJSON.ApplyToMasterKey(&gcpKey)
 | 
						ks.gcpTokenSource.ApplyToMasterKey(&gcpKey)
 | 
				
			||||||
	if err := gcpKey.Encrypt(plaintext); err != nil {
 | 
						if err := gcpKey.Encrypt(plaintext); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -374,7 +347,7 @@ func (ks *Server) decryptWithGCPKMS(key *keyservice.GcpKmsKey, ciphertext []byte
 | 
				
			||||||
	gcpKey := gcpkms.MasterKey{
 | 
						gcpKey := gcpkms.MasterKey{
 | 
				
			||||||
		ResourceID: key.ResourceId,
 | 
							ResourceID: key.ResourceId,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ks.gcpCredsJSON.ApplyToMasterKey(&gcpKey)
 | 
						ks.gcpTokenSource.ApplyToMasterKey(&gcpKey)
 | 
				
			||||||
	gcpKey.EncryptedKey = string(ciphertext)
 | 
						gcpKey.EncryptedKey = string(ciphertext)
 | 
				
			||||||
	plaintext, err := gcpKey.Decrypt()
 | 
						plaintext, err := gcpKey.Decrypt()
 | 
				
			||||||
	return plaintext, err
 | 
						return plaintext, err
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,7 +21,9 @@ import (
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gcpkmsapi "cloud.google.com/go/kms/apiv1"
 | 
				
			||||||
	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
 | 
						"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
 | 
				
			||||||
 | 
						"github.com/aws/aws-sdk-go-v2/aws"
 | 
				
			||||||
	"github.com/aws/aws-sdk-go-v2/credentials"
 | 
						"github.com/aws/aws-sdk-go-v2/credentials"
 | 
				
			||||||
	"github.com/getsops/sops/v3/age"
 | 
						"github.com/getsops/sops/v3/age"
 | 
				
			||||||
	"github.com/getsops/sops/v3/azkv"
 | 
						"github.com/getsops/sops/v3/azkv"
 | 
				
			||||||
| 
						 | 
					@ -32,6 +34,7 @@ import (
 | 
				
			||||||
	"github.com/getsops/sops/v3/pgp"
 | 
						"github.com/getsops/sops/v3/pgp"
 | 
				
			||||||
	. "github.com/onsi/gomega"
 | 
						. "github.com/onsi/gomega"
 | 
				
			||||||
	"golang.org/x/net/context"
 | 
						"golang.org/x/net/context"
 | 
				
			||||||
 | 
						"golang.org/x/oauth2/google"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestServer_EncryptDecrypt_PGP(t *testing.T) {
 | 
					func TestServer_EncryptDecrypt_PGP(t *testing.T) {
 | 
				
			||||||
| 
						 | 
					@ -151,8 +154,8 @@ func TestServer_EncryptDecrypt_HCVault_Fallback(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestServer_EncryptDecrypt_awskms(t *testing.T) {
 | 
					func TestServer_EncryptDecrypt_awskms(t *testing.T) {
 | 
				
			||||||
	g := NewWithT(t)
 | 
						g := NewWithT(t)
 | 
				
			||||||
	s := NewServer(WithAWSKeys{
 | 
						s := NewServer(WithAWSCredentialsProvider{
 | 
				
			||||||
		CredsProvider: awskms.NewCredentialsProvider(credentials.StaticCredentialsProvider{}),
 | 
							CredentialsProvider: func(region string) aws.CredentialsProvider { return credentials.StaticCredentialsProvider{} },
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	key := KeyFromMasterKey(awskms.NewMasterKeyFromArn("arn:aws:kms:us-west-2:107501996527:key/612d5f0p-p1l3-45e6-aca6-a5b005693a48", nil, ""))
 | 
						key := KeyFromMasterKey(awskms.NewMasterKeyFromArn("arn:aws:kms:us-west-2:107501996527:key/612d5f0p-p1l3-45e6-aca6-a5b005693a48", nil, ""))
 | 
				
			||||||
| 
						 | 
					@ -174,7 +177,7 @@ func TestServer_EncryptDecrypt_azkv(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	identity, err := azidentity.NewDefaultAzureCredential(nil)
 | 
						identity, err := azidentity.NewDefaultAzureCredential(nil)
 | 
				
			||||||
	g.Expect(err).ToNot(HaveOccurred())
 | 
						g.Expect(err).ToNot(HaveOccurred())
 | 
				
			||||||
	s := NewServer(WithAzureToken{Token: azkv.NewTokenCredential(identity)})
 | 
						s := NewServer(WithAzureTokenCredential{TokenCredential: identity})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	key := KeyFromMasterKey(azkv.NewMasterKey("", "", ""))
 | 
						key := KeyFromMasterKey(azkv.NewMasterKey("", "", ""))
 | 
				
			||||||
	_, err = s.Encrypt(context.TODO(), &keyservice.EncryptRequest{
 | 
						_, err = s.Encrypt(context.TODO(), &keyservice.EncryptRequest{
 | 
				
			||||||
| 
						 | 
					@ -194,24 +197,24 @@ func TestServer_EncryptDecrypt_azkv(t *testing.T) {
 | 
				
			||||||
func TestServer_EncryptDecrypt_gcpkms(t *testing.T) {
 | 
					func TestServer_EncryptDecrypt_gcpkms(t *testing.T) {
 | 
				
			||||||
	g := NewWithT(t)
 | 
						g := NewWithT(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	creds := `{ "client_id": "<client-id>.apps.googleusercontent.com",
 | 
						creds, err := google.CredentialsFromJSON(context.Background(),
 | 
				
			||||||
 		"client_secret": "<secret>",
 | 
							[]byte(`{"type":"service_account"}`), gcpkmsapi.DefaultAuthScopes()...)
 | 
				
			||||||
		"type": "authorized_user"}`
 | 
						g.Expect(err).ToNot(HaveOccurred())
 | 
				
			||||||
	s := NewServer(WithGCPCredsJSON([]byte(creds)))
 | 
						s := NewServer(WithGCPTokenSource{TokenSource: creds.TokenSource})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resourceID := "projects/test-flux/locations/global/keyRings/test-flux/cryptoKeys/sops"
 | 
						resourceID := "projects/test-flux/locations/global/keyRings/test-flux/cryptoKeys/sops"
 | 
				
			||||||
	key := KeyFromMasterKey(gcpkms.NewMasterKeyFromResourceID(resourceID))
 | 
						key := KeyFromMasterKey(gcpkms.NewMasterKeyFromResourceID(resourceID))
 | 
				
			||||||
	_, err := s.Encrypt(context.TODO(), &keyservice.EncryptRequest{
 | 
						_, err = s.Encrypt(context.TODO(), &keyservice.EncryptRequest{
 | 
				
			||||||
		Key: &key,
 | 
							Key: &key,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	g.Expect(err).To(HaveOccurred())
 | 
						g.Expect(err).To(HaveOccurred())
 | 
				
			||||||
	g.Expect(err.Error()).To(ContainSubstring("cannot create GCP KMS service"))
 | 
						g.Expect(err.Error()).To(ContainSubstring("failed to encrypt sops data key with GCP KMS key"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err = s.Decrypt(context.TODO(), &keyservice.DecryptRequest{
 | 
						_, err = s.Decrypt(context.TODO(), &keyservice.DecryptRequest{
 | 
				
			||||||
		Key: &key,
 | 
							Key: &key,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	g.Expect(err).To(HaveOccurred())
 | 
						g.Expect(err).To(HaveOccurred())
 | 
				
			||||||
	g.Expect(err.Error()).To(ContainSubstring("cannot create GCP KMS service"))
 | 
						g.Expect(err.Error()).To(ContainSubstring("failed to decrypt sops data key with GCP KMS key"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										22
									
								
								main.go
								
								
								
								
							
							
						
						
									
										22
									
								
								main.go
								
								
								
								
							| 
						 | 
					@ -32,10 +32,12 @@ import (
 | 
				
			||||||
	ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache"
 | 
						ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache"
 | 
				
			||||||
	ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
 | 
						ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
 | 
				
			||||||
	ctrlcfg "sigs.k8s.io/controller-runtime/pkg/config"
 | 
						ctrlcfg "sigs.k8s.io/controller-runtime/pkg/config"
 | 
				
			||||||
 | 
						ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
 | 
				
			||||||
	metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
 | 
						metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/fluxcd/cli-utils/pkg/kstatus/polling/clusterreader"
 | 
						"github.com/fluxcd/cli-utils/pkg/kstatus/polling/clusterreader"
 | 
				
			||||||
	"github.com/fluxcd/cli-utils/pkg/kstatus/polling/engine"
 | 
						"github.com/fluxcd/cli-utils/pkg/kstatus/polling/engine"
 | 
				
			||||||
 | 
						pkgcache "github.com/fluxcd/pkg/cache"
 | 
				
			||||||
	"github.com/fluxcd/pkg/runtime/acl"
 | 
						"github.com/fluxcd/pkg/runtime/acl"
 | 
				
			||||||
	runtimeClient "github.com/fluxcd/pkg/runtime/client"
 | 
						runtimeClient "github.com/fluxcd/pkg/runtime/client"
 | 
				
			||||||
	runtimeCtrl "github.com/fluxcd/pkg/runtime/controller"
 | 
						runtimeCtrl "github.com/fluxcd/pkg/runtime/controller"
 | 
				
			||||||
| 
						 | 
					@ -73,6 +75,10 @@ func init() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
 | 
						const (
 | 
				
			||||||
 | 
							tokenCacheDefaultMaxSize = 100
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var (
 | 
						var (
 | 
				
			||||||
		metricsAddr             string
 | 
							metricsAddr             string
 | 
				
			||||||
		eventsAddr              string
 | 
							eventsAddr              string
 | 
				
			||||||
| 
						 | 
					@ -93,6 +99,7 @@ func main() {
 | 
				
			||||||
		defaultServiceAccount   string
 | 
							defaultServiceAccount   string
 | 
				
			||||||
		featureGates            feathelper.FeatureGates
 | 
							featureGates            feathelper.FeatureGates
 | 
				
			||||||
		disallowedFieldManagers []string
 | 
							disallowedFieldManagers []string
 | 
				
			||||||
 | 
							tokenCacheOptions       pkgcache.TokenFlags
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
 | 
						flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
 | 
				
			||||||
| 
						 | 
					@ -116,6 +123,7 @@ func main() {
 | 
				
			||||||
	featureGates.BindFlags(flag.CommandLine)
 | 
						featureGates.BindFlags(flag.CommandLine)
 | 
				
			||||||
	watchOptions.BindFlags(flag.CommandLine)
 | 
						watchOptions.BindFlags(flag.CommandLine)
 | 
				
			||||||
	intervalJitterOptions.BindFlags(flag.CommandLine)
 | 
						intervalJitterOptions.BindFlags(flag.CommandLine)
 | 
				
			||||||
 | 
						tokenCacheOptions.BindFlags(flag.CommandLine, tokenCacheDefaultMaxSize)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	flag.Parse()
 | 
						flag.Parse()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -240,6 +248,19 @@ func main() {
 | 
				
			||||||
		os.Exit(1)
 | 
							os.Exit(1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var tokenCache *pkgcache.TokenCache
 | 
				
			||||||
 | 
						if tokenCacheOptions.MaxSize > 0 {
 | 
				
			||||||
 | 
							var err error
 | 
				
			||||||
 | 
							tokenCache, err = pkgcache.NewTokenCache(tokenCacheOptions.MaxSize,
 | 
				
			||||||
 | 
								pkgcache.WithMaxDuration(tokenCacheOptions.MaxDuration),
 | 
				
			||||||
 | 
								pkgcache.WithMetricsRegisterer(ctrlmetrics.Registry),
 | 
				
			||||||
 | 
								pkgcache.WithMetricsPrefix("gotk_token_"))
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								setupLog.Error(err, "unable to create token cache")
 | 
				
			||||||
 | 
								os.Exit(1)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = (&controller.KustomizationReconciler{
 | 
						if err = (&controller.KustomizationReconciler{
 | 
				
			||||||
		ControllerName:          controllerName,
 | 
							ControllerName:          controllerName,
 | 
				
			||||||
		DefaultServiceAccount:   defaultServiceAccount,
 | 
							DefaultServiceAccount:   defaultServiceAccount,
 | 
				
			||||||
| 
						 | 
					@ -257,6 +278,7 @@ func main() {
 | 
				
			||||||
		DisallowedFieldManagers: disallowedFieldManagers,
 | 
							DisallowedFieldManagers: disallowedFieldManagers,
 | 
				
			||||||
		StrictSubstitutions:     strictSubstitutions,
 | 
							StrictSubstitutions:     strictSubstitutions,
 | 
				
			||||||
		GroupChangeLog:          groupChangeLog,
 | 
							GroupChangeLog:          groupChangeLog,
 | 
				
			||||||
 | 
							TokenCache:              tokenCache,
 | 
				
			||||||
	}).SetupWithManager(ctx, mgr, controller.KustomizationReconcilerOptions{
 | 
						}).SetupWithManager(ctx, mgr, controller.KustomizationReconcilerOptions{
 | 
				
			||||||
		DependencyRequeueInterval: requeueDependency,
 | 
							DependencyRequeueInterval: requeueDependency,
 | 
				
			||||||
		HTTPRetry:                 httpRetry,
 | 
							HTTPRetry:                 httpRetry,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue