1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00

Merge branch 'main' into lockbox

This commit is contained in:
zamysel 2021-08-25 11:23:15 +03:00
commit 0dc8842b85
19 changed files with 147 additions and 83 deletions

View file

@ -1,4 +1,4 @@
FROM alpine:3.14.0
FROM alpine:3.14.1
ARG TARGETOS
ARG TARGETARCH
COPY bin/external-secrets-${TARGETOS}-${TARGETARCH} /bin/external-secrets

View file

@ -31,7 +31,8 @@ type GCPSMAuthSecretRef struct {
// GCPSMProvider Configures a store to sync secrets using the GCP Secret Manager provider.
type GCPSMProvider struct {
// Auth defines the information necessary to authenticate against GCP
Auth GCPSMAuth `json:"auth"`
// +optional
Auth GCPSMAuth `json:"auth,omitempty"`
// ProjectID project where secret is located
ProjectID string `json:"projectID,omitempty"`

View file

@ -18,7 +18,7 @@ package v1
// In some instances, `key` is a required field.
type SecretKeySelector struct {
// The name of the Secret resource being referred to.
Name string `json:"name"`
Name string `json:"name,omitempty"`
// Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
// to the namespace of the referent.
// +optional

View file

@ -2,8 +2,8 @@ apiVersion: v2
name: external-secrets
description: External secret management for Kubernetes
type: application
version: "0.3.3"
appVersion: "v0.3.3"
version: "0.3.4"
appVersion: "v0.3.4"
kubeVersion: ">= 1.11.0-0"
keywords:
- kubernetes-external-secrets

View file

@ -4,7 +4,7 @@
[//]: # (README.md generated by gotmpl. DO NOT EDIT.)
![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![Version: 0.3.3](https://img.shields.io/badge/Version-0.3.3-informational?style=flat-square)
![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![Version: 0.3.4](https://img.shields.io/badge/Version-0.3.4-informational?style=flat-square)
External secret management for Kubernetes
@ -49,11 +49,13 @@ The command removes all the Kubernetes components associated with the chart and
| podAnnotations | object | `{}` | |
| podLabels | object | `{}` | |
| podSecurityContext | object | `{}` | |
| priorityClassName | string | `""` | Pod priority class name. |
| prometheus.enabled | bool | `false` | Specifies whether to expose Service resource for collecting Prometheus metrics |
| prometheus.service.port | int | `8080` | |
| rbac.create | bool | `true` | Specifies whether role and rolebinding resources should be created. |
| replicaCount | int | `1` | |
| resources | object | `{}` | |
| scopedNamespace | string | `""` | If set external secrets are only reconciled in the provided namespace |
| securityContext | object | `{}` | |
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account. |
| serviceAccount.create | bool | `true` | Specifies whether a service account should be created. |

View file

@ -2,6 +2,7 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "external-secrets.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "external-secrets.labels" . | nindent 4 }}
spec:
@ -38,11 +39,14 @@ spec:
{{- end }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- if or (.Values.leaderElect) (.Values.extraArgs) }}
{{- if or (.Values.leaderElect) (.Values.scopedNamespace) (.Values.extraArgs) }}
args:
{{- if .Values.leaderElect }}
- --enable-leader-election=true
{{- end }}
{{- if .Values.scopedNamespace }}
- --namespace={{ .Values.scopedNamespace }}
{{- end }}
{{- range $key, $value := .Values.extraArgs }}
{{- if $value }}
- --{{ $key }}={{ $value }}
@ -74,3 +78,6 @@ spec:
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.priorityClassName }}
priorityClassName: {{ .Values.priorityClassName }}
{{- end }}

View file

@ -3,6 +3,7 @@ apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "external-secrets.serviceAccountName" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "external-secrets.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}

View file

@ -17,6 +17,10 @@ fullnameOverride: ""
# than one instance of external-secrets operates at a time.
leaderElect: false
# -- If set external secrets are only reconciled in the
# provided namespace
scopedNamespace: ""
serviceAccount:
# -- Specifies whether a service account should be created.
create: true
@ -66,3 +70,6 @@ nodeSelector: {}
tolerations: []
affinity: {}
# -- Pod priority class name.
priorityClassName: ""

View file

@ -108,8 +108,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
secretAccessKeySecretRef:
description: The SecretAccessKey is used for authentication
@ -130,8 +128,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
type: object
type: object
@ -179,8 +175,6 @@ spec:
to. Ignored if referent is not cluster-scoped. cluster-scoped
defaults to the namespace of the referent.
type: string
required:
- name
type: object
clientSecret:
description: The Azure ClientSecret of the service principle
@ -200,8 +194,6 @@ spec:
to. Ignored if referent is not cluster-scoped. cluster-scoped
defaults to the namespace of the referent.
type: string
required:
- name
type: object
required:
- clientId
@ -249,8 +241,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
type: object
required:
@ -259,8 +249,6 @@ spec:
projectID:
description: ProjectID project where secret is located
type: string
required:
- auth
type: object
ibm:
description: IBM configures this store to sync secrets using IBM
@ -291,8 +279,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
type: object
required:
@ -351,8 +337,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
required:
- path
@ -384,8 +368,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
secretRef:
description: SecretRef to a key in a Secret resource
@ -408,8 +390,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
type: object
jwt:
@ -441,8 +421,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
type: object
kubernetes:
@ -483,8 +461,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
serviceAccountRef:
description: Optional service account field containing
@ -537,8 +513,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
username:
description: Username is a LDAP user name used to
@ -566,8 +540,6 @@ spec:
to. Ignored if referent is not cluster-scoped. cluster-scoped
defaults to the namespace of the referent.
type: string
required:
- name
type: object
type: object
caBundle:

View file

@ -108,8 +108,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
secretAccessKeySecretRef:
description: The SecretAccessKey is used for authentication
@ -130,8 +128,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
type: object
type: object
@ -179,8 +175,6 @@ spec:
to. Ignored if referent is not cluster-scoped. cluster-scoped
defaults to the namespace of the referent.
type: string
required:
- name
type: object
clientSecret:
description: The Azure ClientSecret of the service principle
@ -200,8 +194,6 @@ spec:
to. Ignored if referent is not cluster-scoped. cluster-scoped
defaults to the namespace of the referent.
type: string
required:
- name
type: object
required:
- clientId
@ -249,8 +241,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
type: object
required:
@ -259,8 +249,6 @@ spec:
projectID:
description: ProjectID project where secret is located
type: string
required:
- auth
type: object
ibm:
description: IBM configures this store to sync secrets using IBM
@ -291,8 +279,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
type: object
required:
@ -351,8 +337,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
required:
- path
@ -384,8 +368,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
secretRef:
description: SecretRef to a key in a Secret resource
@ -408,8 +390,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
type: object
jwt:
@ -441,8 +421,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
type: object
kubernetes:
@ -483,8 +461,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
serviceAccountRef:
description: Optional service account field containing
@ -537,8 +513,6 @@ spec:
cluster-scoped defaults to the namespace of
the referent.
type: string
required:
- name
type: object
username:
description: Username is a LDAP user name used to
@ -566,8 +540,6 @@ spec:
to. Ignored if referent is not cluster-scoped. cluster-scoped
defaults to the namespace of the referent.
type: string
required:
- name
type: object
type: object
caBundle:

View file

@ -2,11 +2,7 @@
External Secrets Operator integrates with [GCP Secret Manager](https://cloud.google.com/secret-manager) for secret management.
### Authentication
At the moment, we only support [service account key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys) authentication.
#### Service account key authentication
### Service account key authentication
A service account key is created and the JSON keyfile is stored in a `Kind=Secret`. The `project_id` and `private_key` should be configured for the project.
@ -33,3 +29,63 @@ The operator will fetch the GCP Secret Manager secret and inject it as a `Kind=S
```
kubectl get secret secret-to-be-created -n <namespace> | -o jsonpath='{.data.dev-secret-test}' | base64 -d
```
## Authentication with Workload Identity
This makes it possible for your Google Kubernetes Engine (GKE) applications to consume services provided by Google APIs, namely Secrets Manager service in this case.
Here we will assume that you installed ESO using helm and that you named the chart installation `external-secrets` and the namespace where it lives `es` like:
```sh
helm install external-secrets external-secrets/external-secrets --namespace es
```
Then most of the resources would have this name, the important one here being the k8s service account attached to the external-secrets operator deployment:
```
# ...
containers:
- image: ghcr.io/external-secrets/external-secrets:vVERSION
name: external-secrets
ports:
- containerPort: 8080
protocol: TCP
restartPolicy: Always
schedulerName: default-scheduler
serviceAccount: external-secrets
serviceAccountName: external-secrets # <--- here
```
### Following the documentation
You can find the documentation for Workload Identity under [this url](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity). We will walk you through how to navigate it here.
#### Changing Values
Search [the documment](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) for this editable values and change them to your values:
- CLUSTER_NAME: The name of your cluster
- PROJECT_ID: Your project ID (not your Project number nor your Project name)
- K8S_NAMESPACE: For us folowing these steps here it will be `es`, but this will be the namespace where you deployed the external-secrets operator
- KSA_NAME: external-secrets (if you are not creating a new one to attach to the deployemnt)
- GSA_NAME: external-secrets for simplicity, or something else if you have to follow different naming convetions for cloud resources
- ROLE_NAME: roles/secretmanager.secretAccessor so you make the pod only be able to access secrets on Secret Manager
#### Following through
You can follow through the documentation and adapt it to your specific use case. If you want to just use the serviceaccount that we deployed with the helm chart, for example, you don't need to create a new service account on 2 of [Authenticating to Google Cloud](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#authenticating_to).
#### SecretStore with WorkloadIdentity
To use workload identity you can just omit the auth field of the secret store and let the operator client fall back to defaults using the roles attached to your service account.
```
apiVersion: external-secrets.io/v1alpha1
kind: SecretStore
metadata:
name: example
spec:
provider:
gcpsm:
projectID: pid
```

View file

@ -18,7 +18,8 @@ spec:
path: "approle"
# RoleID configured in the App Role authentication backend
roleId: "db02de05-fa39-4855-059b-67221c5c2f63"
# Reference to a key in a K8 Secret that contains the App Role SecretId
secretRef:
name: "my-secret"
namespace: "secret-admin"
key: "vault-token"
key: "secret-id"

View file

@ -1153,6 +1153,7 @@ GCPSMAuth
</em>
</td>
<td>
<em>(Optional)</em>
<p>Auth defines the information necessary to authenticate against GCP</p>
</td>
</tr>

View file

@ -40,6 +40,9 @@ var _ = SynchronizedBeforeSuite(func() []byte {
By("installing eso")
addon.InstallGlobalAddon(addon.NewESO(), cfg)
By("installing scoped eso")
addon.InstallGlobalAddon(addon.NewScopedESO(), cfg)
return nil
}, func([]byte) {})

View file

@ -27,3 +27,14 @@ func NewESO() *ESO {
},
}
}
func NewScopedESO() *ESO {
return &ESO{
&HelmChart{
Namespace: "default",
ReleaseName: "eso-aws-sm",
Chart: "/k8s/deploy/charts/external-secrets",
Values: []string{"/k8s/eso.scoped.values.yaml"},
},
}
}

View file

@ -0,0 +1,12 @@
installCRDs: false
image:
repository: local/external-secrets
tag: test
scopedNamespace: test
extraEnv:
- name: AWS_SECRETSMANAGER_ENDPOINT
value: "http://localstack.default"
- name: AWS_STS_ENDPOINT
value: "http://localstack.default"
- name: AWS_SSM_ENDPOINT
value: "http://localstack.default"

View file

@ -46,12 +46,14 @@ func main() {
var controllerClass string
var enableLeaderElection bool
var loglevel string
var namespace string
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&controllerClass, "controller-class", "default", "the controller is instantiated with a specific controller name and filters ES based on this property")
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
flag.StringVar(&loglevel, "loglevel", "info", "loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal")
flag.StringVar(&namespace, "namespace", "", "watch external secrets scoped in the provided namespace only")
flag.Parse()
var lvl zapcore.Level
@ -69,6 +71,7 @@ func main() {
Port: 9443,
LeaderElection: enableLeaderElection,
LeaderElectionID: "external-secrets-controller",
Namespace: namespace,
})
if err != nil {
setupLog.Error(err, "unable to start manager")

View file

@ -167,9 +167,15 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
return ctrl.Result{RequeueAfter: refreshInt}, nil
}
// Target Secret Name should default to the ExternalSecret name if not explicitly specified
secretName := externalSecret.Spec.Target.Name
if secretName == "" {
secretName = externalSecret.ObjectMeta.Name
}
secret := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: externalSecret.Spec.Target.Name,
Name: secretName,
Namespace: externalSecret.Namespace,
},
Data: make(map[string][]byte),
@ -194,9 +200,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
// no template: copy data and return
if externalSecret.Spec.Target.Template == nil {
for k, v := range dataMap {
secret.Data[k] = v
}
secret.Data = dataMap
return nil
}

View file

@ -39,12 +39,12 @@ const (
defaultVersion = "latest"
errGCPSMStore = "received invalid GCPSM SecretStore resource"
errGCPSMCredSecretName = "invalid GCPSM SecretStore resource: missing GCP Secret Access Key"
errClientClose = "unable to close SecretManager client: %w"
errInvalidClusterStoreMissingSAKNamespace = "invalid ClusterSecretStore: missing GCP SecretAccessKey Namespace"
errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w"
errMissingSAK = "missing SecretAccessKey"
errUnableProcessJSONCredentials = "failed to process the provided JSON credentials: %w"
errUnableProcessDefaultCredentials = "failed to process the default credentials: %w"
errUnableCreateGCPSMClient = "failed to create GCP secretmanager client: %w"
errUninitalizedGCPProvider = "provider GCP is not initialized"
errClientGetSecretAccess = "unable to access Secret from SecretManager Client: %w"
@ -73,9 +73,6 @@ type gClient struct {
func (c *gClient) setAuth(ctx context.Context) error {
credentialsSecret := &corev1.Secret{}
credentialsSecretName := c.store.Auth.SecretRef.SecretAccessKey.Name
if credentialsSecretName == "" {
return fmt.Errorf(errGCPSMCredSecretName)
}
objectKey := types.NamespacedName{
Name: credentialsSecretName,
Namespace: c.namespace,
@ -88,7 +85,10 @@ func (c *gClient) setAuth(ctx context.Context) error {
}
objectKey.Namespace = *c.store.Auth.SecretRef.SecretAccessKey.Namespace
}
if credentialsSecretName == "" {
c.credentials = nil
return nil
}
err := c.kube.Get(ctx, objectKey, credentialsSecret)
if err != nil {
return fmt.Errorf(errFetchSAKSecret, err)
@ -122,12 +122,23 @@ func (sm *ProviderGCP) NewClient(ctx context.Context, store esv1alpha1.GenericSt
sm.projectID = cliStore.store.ProjectID
config, err := google.JWTConfigFromJSON(cliStore.credentials, CloudPlatformRole)
if err != nil {
return nil, fmt.Errorf(errUnableProcessJSONCredentials, err)
if cliStore.credentials != nil {
config, err := google.JWTConfigFromJSON(cliStore.credentials, CloudPlatformRole)
if err != nil {
return nil, fmt.Errorf(errUnableProcessJSONCredentials, err)
}
ts := config.TokenSource(ctx)
clientGCPSM, err := secretmanager.NewClient(ctx, option.WithTokenSource(ts))
if err != nil {
return nil, fmt.Errorf(errUnableCreateGCPSMClient, err)
}
sm.SecretManagerClient = clientGCPSM
return sm, nil
}
ts, err := google.DefaultTokenSource(ctx, CloudPlatformRole)
if err != nil {
return nil, fmt.Errorf(errUnableProcessDefaultCredentials, err)
}
ts := config.TokenSource(ctx)
clientGCPSM, err := secretmanager.NewClient(ctx, option.WithTokenSource(ts))
if err != nil {
return nil, fmt.Errorf(errUnableCreateGCPSMClient, err)