2020-11-19 15:26:01 +00:00
```yaml
---
2020-11-30 12:09:24 +00:00
title: External Secrets Operator CRD
2020-11-24 10:35:53 +00:00
version: v1alpha1
2020-11-19 15:26:01 +00:00
authors: all of us
creation-date: 2020-09-01
status: draft
---
```
2020-11-30 12:09:24 +00:00
# External Secrets Operator CRD
2020-11-19 15:26:01 +00:00
## Table of Contents
<!-- toc -->
- [Summary ](#summary )
- [Motivation ](#motivation )
- [Goals ](#goals )
- [Non-Goals ](#non-goals )
- [Terminology ](#terminology )
- [Use-Cases ](#use-cases )
- [Proposal ](#proposal )
- [API ](#api )
- [Alternatives ](#alternatives )
<!-- /toc -->
## Summary
2020-11-30 12:09:24 +00:00
This is a proposal to standardize the External Secrets operator CRDs in an combined effort through all projects that deal with syncing external secrets. This proposal aims to do find the _common denominator_ for all users of an External Secrets project.
2020-11-19 15:26:01 +00:00
## Motivation
There are a lot of different projects in the wild that essentially do the same thing: sync secrets with Kubernetes. The idea is to unify efforts into a single project that serves the needs of all users in this problem space.
2020-11-24 10:35:53 +00:00
As a starting point I (@moolen) would like to define a **common denominator** for a Custom Resource Definition that serves all known use-cases. This CRD should follow the standard alpha -> beta -> GA feature process.
2020-11-19 15:26:01 +00:00
Once the CRD API is defined we can move on with more delicate discussions about technology, organization and processes.
List of Projects known so far or related:
* https://github.com/godaddy/kubernetes-external-secrets
* https://github.com/itscontained/secret-manager
* https://github.com/ContainerSolutions/externalsecret-operator
* https://github.com/mumoshu/aws-secret-operator
* https://github.com/cmattoon/aws-ssm
* https://github.com/tuenti/secrets-manager
* https://github.com/kubernetes-sigs/k8s-gsm-tools
### Goals
2020-11-24 10:35:53 +00:00
- Define an alpha CRD
2020-11-19 15:26:01 +00:00
- Fully document the Spec and use-cases
### Non-Goals
This KEP proposes the CRD Spec and documents the use-cases, not the choice of technology or migration path towards implementing the CRD.
We do not want to sync secrets into a `ConfigMap` .
## Terminology
2020-11-30 12:09:24 +00:00
* External Secrets Operator `ESO` : A Application that runs a control loop which syncs secrets
* ESO `instance` : A single entity that runs a control loop
* Provider: Is a **source** for secrets. The Provider is external to ESO. It can be a hosted service like Alibaba Cloud SecretsManager, AWS SystemsManager, Azure KeyVault etc
* SecretStore `ST` : A Custom Resource to authenticate and configure the connection between the ESO instance and the Provider
2020-11-24 10:35:53 +00:00
* ExternalSecret `ES` : A Custom Resource that declares which secrets should be synced
* Frontend: A **sink** for the synced secrets, usually a `Secret` resource
* Secret: Credentials that act as a key to sensitive information
2020-11-19 15:26:01 +00:00
## Use-Cases
2020-11-30 12:09:24 +00:00
* One global ESO instance that manages ES in **all namespaces** , which gives access to **all providers** , with ACL
* Multiple global ESO instances, each manages access to a single or multiple providers (e.g.: shard by stage or team...)
* One ESO per namespace (a user manages their own ESO instance)
2020-11-19 15:26:01 +00:00
2020-11-24 10:35:53 +00:00
### User Definitions
2020-11-30 12:09:24 +00:00
* `operator :=` I manage one or multiple `ESO` instances
* `user :=` I only create `ES` , ESO is managed by someone else
2020-11-19 15:26:01 +00:00
### User Stories
2020-11-24 10:35:53 +00:00
From that we can derive the following requirements or user stories:
2020-11-30 12:09:24 +00:00
1. As a ESO operator I want to run multiple ESO instances per cluster (e.g. one ESO instance per DEV/PROD)
1. As a ESO operator or user I want to integrate **multiple SecretStores** with a **single ESO instance** (e.g. dev namespace has access only to dev secrets)
1. As a ESO user I want to control the Frontend for the secrets, usually a `Secret` resource
1. As a ESO user I want to fetch **from multiple** Providers and store the secrets **in a single** Frontend
1. As a ESO operator I want to limit the access to certain stores or sub resources (e.g. having one central ESO instance that handles all ES - similar to `iam.amazonaws.com/permitted` annotation per namespace)
1. As a ESO user I want to provide an application with a configuration that contains a secret
2020-11-19 15:26:01 +00:00
2020-11-19 15:33:04 +00:00
### Providers
2020-11-19 15:26:01 +00:00
2020-11-19 15:33:04 +00:00
These providers are relevant for the project:
2020-11-19 15:26:01 +00:00
* AWS Secure Systems Manager Parameter Store
* AWS Secrets Manager
* Hashicorp Vault
* Azure Key Vault
* Alibaba Cloud KMS Secret Manager
* Google Cloud Platform Secret Manager
2020-11-24 10:35:53 +00:00
* Kubernetes (see [#422 ](https://github.com/external-secrets/kubernetes-external-secrets/issues/422 ))
* noop (see [#476 ](https://github.com/external-secrets/kubernetes-external-secrets/issues/476 ))
2020-11-19 15:26:01 +00:00
### Frontends
2020-11-24 10:35:53 +00:00
* A Secret Kubernetes resource
* *potentially* we could sync Provider to Provider
2020-11-19 15:26:01 +00:00
## Proposal
### API
### External Secret
2020-11-24 10:35:53 +00:00
The `ExternalSecret` Custom Resource Definition is **namespaced** . It defines the following:
1. Source for the secret (`SecretStore`)
2. Sink for the secret (Fronted)
3. A mapping to translate the keys
2020-11-19 15:26:01 +00:00
```yaml
2020-11-30 12:18:58 +00:00
apiVersion: external-secrets.io/v1alpha1
2020-11-19 15:26:01 +00:00
kind: ExternalSecret
metadata: {...}
spec:
2020-11-24 10:35:53 +00:00
# SecretStoreRef defines which SecretStore to fetch the ExternalSecret data
secretStoreRef:
name: secret-store-name
kind: SecretStore # or ClusterSecretStore
2020-11-30 12:09:24 +00:00
# RefreshInterval is the amount of time before the values reading again from the SecretStore provider
# Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" (from time.ParseDuration)
# May be set to zero to fetch and create it once
2020-11-19 15:26:01 +00:00
refreshInterval: "1h"
2020-11-24 10:35:53 +00:00
# There can only be one target per ES
2020-11-19 15:26:01 +00:00
target:
2020-11-24 10:35:53 +00:00
2020-11-19 15:26:01 +00:00
# The secret name of the resource
2020-11-24 10:35:53 +00:00
# Defaults to .metadata.name of the ExternalSecret
# It is immutable
2020-11-19 15:26:01 +00:00
name: my-secret
# Enum with values: 'Owner', 'Merge', or 'None'
# Default value of 'Owner'
# Owner creates the secret and sets .metadata.ownerReferences of the resource
# Merge does not create the secret, but merges in the data fields to the secret
# None does not create a secret (future use with injector)
creationPolicy: 'Merge'
2020-11-24 10:35:53 +00:00
# Specify a blueprint for the resulting Kind=Secret
2020-11-19 15:26:01 +00:00
template:
type: kubernetes.io/dockerconfigjson # or TLS...
metadata:
annotations: {}
labels: {}
2020-11-24 10:35:53 +00:00
# Use inline templates to construct your desired config file that contains your secret
2020-11-19 15:26:01 +00:00
data:
config.yml: |
endpoints:
- https://{{ .data.user }}:{{ .data.password }}@api.exmaple.com
# Uses an existing template from configmap
2020-11-24 10:35:53 +00:00
# Secret is fetched, merged and templated within the referenced configMap data
2020-11-19 15:26:01 +00:00
# It does not update the configmap, it creates a secret with: data["alertmanager.yml"] = ...result...
templateFrom:
- configMap:
name: alertmanager
items:
- key: alertmanager.yaml
2020-11-24 10:35:53 +00:00
# Data defines the connection between the Kubernetes Secret keys and the Provider data
2020-11-19 15:26:01 +00:00
data:
2020-11-24 10:35:53 +00:00
- secretKey: secret-key-to-be-managed
remoteRef:
key: provider-key
version: provider-key-version
property: provider-key-property
# Used to fetch all properties from the Provider key
# If multiple dataFrom are specified, secrets are merged in the specified order
2020-11-19 15:26:01 +00:00
dataFrom:
2020-11-24 10:35:53 +00:00
- remoteRef:
key: provider-key
version: provider-key-version
property: provider-key-property
2020-11-19 15:26:01 +00:00
status:
2020-12-22 16:43:53 +00:00
# refreshTime is the time and date the external secret was fetched and
# the target secret updated
refreshTime: "2019-08-12T12:33:02Z"
# Standard condition schema
2020-11-19 15:26:01 +00:00
conditions:
2020-12-22 16:43:53 +00:00
# ExternalSecret ready condition indicates the secret is ready for use.
# This is defined as:
# - The target secret exists
# - The target secret has been refreshed within the last refreshInterval
# - The target secret content is up-to-date based on any target templates
- type: Ready
status: "True" # False if last refresh was not successful
2020-11-19 15:26:01 +00:00
reason: "SecretSynced"
message: "Secret was synced"
lastTransitionTime: "2019-08-12T12:33:02Z"
```
#### Behavior
The ExternalSecret control loop **ensures** that the target resource exists and stays up to date with the upstream provider. Because most upstream APIs are limited in throughput the control loop must implement some sort of jitter and retry/backoff mechanic.
2020-11-24 10:35:53 +00:00
### Secret Store
2020-11-19 15:26:01 +00:00
The store configuration in an `ExternalSecret` may contain a lot of redundancy, this can be factored out into its own CRD.
2020-11-24 10:35:53 +00:00
These stores are defined in a particular namespace using `SecretStore` **or** globally with `ClusterSecretStore` .
2020-11-19 15:26:01 +00:00
```yaml
2020-11-30 12:09:24 +00:00
apiVerson: external-secrets.io/v1alpha1
2020-11-19 15:26:01 +00:00
kind: SecretStore # or ClusterSecretStore
metadata:
name: vault
namespace: example-ns
spec:
2020-11-30 12:09:24 +00:00
# Used to select the correct ESO controller (think: ingress.ingressClassName)
# The ESO controller is instantiated with a specific controller name and filters ES based on this property
2020-11-24 10:35:53 +00:00
# Optional
controller: dev
2020-12-09 07:31:29 +00:00
# provider field contains the configuration to access the provider which contains the secret
2020-12-22 16:43:53 +00:00
# exactly one provider must be configured.
2020-12-09 07:31:29 +00:00
provider:
# AWSSM configures this store to sync secrets using AWS Secret Manager provider
awssm:
2020-12-22 16:43:53 +00:00
# Auth defines the information necessary to authenticate against AWS by
2020-12-09 07:31:29 +00:00
# getting the accessKeyID and secretAccessKey from an already created Kubernetes Secret
auth:
secretRef:
accessKeyID:
name: awssm-secret
key: access-key
secretAccessKey:
name: awssm-secret
key: secret-access-key
# Role is a Role ARN which the SecretManager provider will assume
role: iam-role
# AWS Region to be used for the provider
region: eu-central-1
2020-11-19 15:26:01 +00:00
status:
2020-12-22 16:43:53 +00:00
# Standard condition schema
2020-11-19 15:26:01 +00:00
conditions:
2020-12-22 16:43:53 +00:00
# SecretStore ready condition indicates the given store is in ready
# state and able to referenced by ExternalSecrets
# If the `status` of this condition is `False` , ExternalSecret controllers
# should prevent attempts to fetch secrets
2020-11-19 15:26:01 +00:00
- type: Ready
status: "False"
2020-12-22 16:43:53 +00:00
reason: "ConfigError"
message: "SecretStore validation failed"
2020-11-19 15:26:01 +00:00
lastTransitionTime: "2019-08-12T12:33:02Z"
```
2020-11-30 12:09:24 +00:00
## Workflow in a ESO instance
2020-11-19 15:26:01 +00:00
2020-11-24 10:35:53 +00:00
1. A user creates a `SecretStore` with a certain `spec.controller`
2020-11-19 15:26:01 +00:00
2. A controller picks up the `ExternalSecret` if it matches the `controller` field
2020-11-24 10:35:53 +00:00
3. The controller fetches the secret from the Provider and stores it as Secret Kubernetes resource in the same namespace as ES
2020-11-19 15:26:01 +00:00
## Backlog
We have a bunch of features which are not relevant for the MVP implementation. We keep the features here in this backlog. Order is not specific:
1. Secret injection with a mutating Webhook [#81 ](https://github.com/godaddy/kubernetes-external-secrets/issues/81 )