Skip to content

Commit

Permalink
Merge pull request #209 from fluxcd/sops
Browse files Browse the repository at this point in the history
Mozilla SOPS
  • Loading branch information
stefanprodan authored Sep 6, 2020
2 parents 36ba8f0 + ee33702 commit d6c6c88
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 14 deletions.
37 changes: 28 additions & 9 deletions cmd/gotk/create_kustomization.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,17 @@ var createKsCmd = &cobra.Command{
}

var (
ksSource string
ksPath string
ksPrune bool
ksDependsOn []string
ksValidation string
ksHealthCheck []string
ksHealthTimeout time.Duration
ksSAName string
ksSANamespace string
ksSource string
ksPath string
ksPrune bool
ksDependsOn []string
ksValidation string
ksHealthCheck []string
ksHealthTimeout time.Duration
ksSAName string
ksSANamespace string
ksDecryptionProvider string
ksDecryptionSecret string
)

func init() {
Expand All @@ -94,6 +96,8 @@ func init() {
createKsCmd.Flags().StringArrayVar(&ksDependsOn, "depends-on", nil, "Kustomization that must be ready before this Kustomization can be applied")
createKsCmd.Flags().StringVar(&ksSAName, "sa-name", "", "service account name")
createKsCmd.Flags().StringVar(&ksSANamespace, "sa-namespace", "", "service account namespace")
createKsCmd.Flags().StringVar(&ksDecryptionProvider, "decryption-provider", "", "enables secrets decryption, provider can be 'sops'")
createKsCmd.Flags().StringVar(&ksDecryptionSecret, "decryption-secret", "", "set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption")
createCmd.AddCommand(createKsCmd)
}

Expand Down Expand Up @@ -178,6 +182,21 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
}
}

if ksDecryptionProvider != "" {
if !utils.containsItemString(supportedDecryptionProviders, ksDecryptionProvider) {
return fmt.Errorf("decryption provider %s is not supported, can be %v",
ksDecryptionProvider, supportedDecryptionProviders)
}

kustomization.Spec.Decryption = &kustomizev1.Decryption{
Provider: ksDecryptionProvider,
}

if ksDecryptionSecret != "" {
kustomization.Spec.Decryption.SecretRef = &corev1.LocalObjectReference{Name: ksDecryptionSecret}
}
}

if export {
return exportKs(kustomization)
}
Expand Down
11 changes: 6 additions & 5 deletions cmd/gotk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,12 @@ var (
)

var (
defaultComponents = []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"}
defaultVersion = "latest"
defaultNamespace = "gitops-system"
defaultNotification = "notification-controller"
supportedArch = []string{"arm64", "amd64"}
defaultComponents = []string{"source-controller", "kustomize-controller", "helm-controller", "notification-controller"}
defaultVersion = "latest"
defaultNamespace = "gitops-system"
defaultNotification = "notification-controller"
supportedArch = []string{"arm64", "amd64"}
supportedDecryptionProviders = []string{"sops"}
)

func init() {
Expand Down
2 changes: 2 additions & 0 deletions docs/cmd/gotk_create_kustomization.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ gotk create kustomization [name] [flags]
### Options

```
--decryption-provider string enables secrets decryption, provider can be 'sops'
--decryption-secret string set the Kubernetes secret name that contains the OpenPGP private keys used for sops decryption
--depends-on stringArray Kustomization that must be ready before this Kustomization can be applied
--health-check stringArray workload to be included in the health assessment, in the format '<kind>/<name>.<namespace>'
--health-check-timeout duration timeout of health checking operations (default 2m0s)
Expand Down
166 changes: 166 additions & 0 deletions docs/guides/mozilla-sops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Manage Kubernetes secrets with Mozilla SOPS

In order to store secrets safely in a public or private Git repository, you can use
Mozilla's [SOPS](https://github.com/mozilla/sops) CLI to encrypt
Kubernetes secrets with OpenPGP, AWS KMS, GCP KMS and Azure Key Vault.

## Prerequisites

To follow this guide you'll need a Kubernetes cluster with the GitOps
toolkit controllers installed on it.
Please see the [get started guide](../get-started/index.md)
or the [installation guide](installation.md).

Install [gnupg](https://www.gnupg.org/) and [sops](https://github.com/mozilla/sops):

```sh
brew install gnupg sops
```

## Generate a GPG key

Generate a GPG key with OpenPGP without specifying a passphrase:

```console
$ gpg --full-generate-key

Real name: stefanprodan
Email address: [email protected]
Comment:
You selected this USER-ID:
"stefanprodan <[email protected]>"
```

Retrieve the GPG key ID (second row of the sec column):

```console
$ gpg --list-secret-keys [email protected]

sec rsa3072 2020-09-06 [SC]
1F3D1CED2F865F5E59CA564553241F147E7C5FA4
```

Export the public and private keypair from your local GPG keyring and
create a Kubernetes secret named `sops-gpg` in the `gitops-system` namespace:

```sh
gpg --export-secret-keys \
--armor 1F3D1CED2F865F5E59CA564553241F147E7C5FA4 |
kubectl create secret generic sops-gpg \
--namespace=gitops-system \
--from-file=sops.asc=/dev/stdin
```

## Encrypt secrets

Generate a Kubernetes secret manifest with kubectl:

```sh
kubectl -n default create secret generic basic-auth \
--from-literal=user=admin \
--from-literal=password=change-me \
--dry-run=client \
-o yaml > basic-auth.yaml
```

Encrypt the secret with sops using your GPG key:

```sh
sops --encrypt \
--pgp=1F3D1CED2F865F5E59CA564553241F147E7C5FA4 \
--encrypted-regex '^(data|stringData)$' \
--in-place basic-auth.yaml
```

!!! hint
Note that you should encrypt only the `data` section. Encrypting the Kubernetes
secret metadata, kind or apiVersion is not supported by kustomize-controller.

You can now commit the encrypted secret to your Git repository.

## Configure secrets decryption

Registry the Git repository on your cluster:

```sh
gotk create source git my-secrets \
--url=https://github.com/my-org/my-secrets
```

Create a kustomization for reconciling the secrets on the cluster:

```sh
gotk create kustomization my-secrets \
--source=my-secrets \
--prune=true \
--interval=10m \
--decryption-provider=sops \
--decryption-secret=sops-gpg
```

Note that the `sops-gpg` can contain more than one key, sops will try to decrypt the
secrets by iterating over all the private keys until it finds one that works.

!!! hint KMS
When using AWS/GCP KMS or Azure Key Vault, you'll have to bind an IAM Role
with read access to the KMS keys to the `default` service account of the
`gitops-system` namespace for kustomize-controller to be able to fetch
keys from KMS.

## GitOps workflow

A cluster admin should create the Kubernetes secret with the PGP keys on each cluster and
add the GitRepository/Kustomization manifests to the fleet repository.

Git repository manifest:

```yaml
apiVersion: source.toolkit.fluxcd.io/v1alpha1
kind: GitRepository
metadata:
name: my-secrets
namespace: gitops-system
spec:
interval: 1m
url: https://github.com/my-org/my-secrets
```
Kustomization manifest:
```yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1alpha1
kind: Kustomization
metadata:
name: my-secrets
namespace: gitops-system
spec:
interval: 10m0s
sourceRef:
kind: GitRepository
name: my-secrets
path: ./
prune: true
decryption:
provider: sops
secretRef:
name: sops-gpg
```
!!! hint
You can generate the above manifests using `gotk create <kind> --export > manifest.yaml`.

Assuming a team member wants to deploy an application that needs to connect
to a database using a username and password, they'll be doing the following:

* create a Kubernetes Secret manifest locally with the db credentials e.g. `db-auth.yaml`
* encrypt the secret `data` field with sops
* create a Kubernetes Deployment manifest for the app e.g. `app-deployment.yaml`
* add the Secret to the Deployment manifest as a [volume mount or env var](https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets)
* commit the manifests `db-auth.yaml` and `app-deployment.yaml` to a Git repository that's being synced by the GitOps toolkit controllers

Once the manifests have been pushed to the Git repository, the following happens:

* source-controller pulls the changes from Git
* kustomize-controller loads the GPG keys from the `sops-pgp` secret
* kustomize-controller decrypts the Kubernetes secrets with sops and applies them on the cluster
* kubelet creates the pods and mounts the secret as a volume or env variable inside the app container
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ nav:
- Setup Notifications: guides/notifications.md
- Setup Webhook Receivers: guides/webhook-receivers.md
- Sealed Secrets: guides/sealed-secrets.md
- Mozilla SOPS: guides/mozilla-sops.md
- Toolkit Components:
- Source Controller:
- Overview: components/source/controller.md
Expand Down

0 comments on commit d6c6c88

Please sign in to comment.