feat: use age instead of gpg for sops (#153)
* feat: use age instead of gpg Signed-off-by: Devin Buhl <devin@buhl.casa> * fix: update comment in config env Signed-off-by: Devin Buhl <devin@buhl.casa> * fix: set some debug output in configure.sh Signed-off-by: Devin Buhl <devin@buhl.casa> * fix: use /Users/devin instead of full path Signed-off-by: Devin Buhl <devin@buhl.casa> * fix: check if BOOTSTRAP_AGE_PUBLIC_KEY starts with age Signed-off-by: Devin Buhl <devin@buhl.casa>
This commit is contained in:
1 parent
ab48ebb928
commit
35d41ca8db
9 files changed
+48
-78
No files matched your search
+3
-6
@@ -19,12 +19,9 @@ export BOOTSTRAP_METALLB_LB_RANGE=""
|
|||||||
# The load balancer IP for traefik, choose from one of the available IPs above
|
# The load balancer IP for traefik, choose from one of the available IPs above
|
||||||
# e.g. 192.168.1.220
|
# e.g. 192.168.1.220
|
||||||
export BOOTSTRAP_METALLB_TRAEFIK_ADDR=""
|
export BOOTSTRAP_METALLB_TRAEFIK_ADDR=""
|
||||||
# Your personal pgp key
|
# Age Public Key - string should start with age
|
||||||
# e.g. 772154FFF783DE317KLCA0EC77149AC618D75581
|
# e.g. age15uzrw396e67z9wdzsxzdk7ka0g2gr3l460e0slaea563zll3hdfqwqxdta
|
||||||
export BOOTSTRAP_PERSONAL_KEY_FP=""
|
export BOOTSTRAP_AGE_PUBLIC_KEY=""
|
||||||
# The flux pgp key
|
|
||||||
# e.g. AB675CE4CC64251G3S9AE1DAA88ARRTY2C009E2D
|
|
||||||
export BOOTSTRAP_FLUX_KEY_FP=""
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Ansible related variables
|
# Ansible related variables
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#shellcheck disable=SC2148,SC2155
|
#shellcheck disable=SC2148,SC2155
|
||||||
export KUBECONFIG=$(expand_path ./provision/kubeconfig)
|
export KUBECONFIG=$(expand_path ./provision/kubeconfig)
|
||||||
export ANSIBLE_CONFIG=$(expand_path ./ansible.cfg)
|
export ANSIBLE_CONFIG=$(expand_path ./ansible.cfg)
|
||||||
export GPG_TTY=$(tty)
|
export ANSIBLE_HOST_KEY_CHECKING="False"
|
||||||
|
export SOPS_AGE_KEY_FILE=$(expand_path ~/.config/sops/age/keys.txt)
|
||||||
@@ -8,6 +8,7 @@ kubeconfig
|
|||||||
# vscode-sops
|
# vscode-sops
|
||||||
.decrypted~*.yaml
|
.decrypted~*.yaml
|
||||||
.config.env
|
.config.env
|
||||||
|
*.agekey
|
||||||
# Ansible
|
# Ansible
|
||||||
xanmanning.k3s*
|
xanmanning.k3s*
|
||||||
# Terraform
|
# Terraform
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Highly opinionated template for deploying a single [k3s](https://k3s.io) cluster with [Ansible](https://www.ansible.com) and [Terraform](https://www.terraform.io) backed by [Flux](https://toolkit.fluxcd.io/) and [SOPS](https://toolkit.fluxcd.io/guides/mozilla-sops/).
|
Highly opinionated template for deploying a single [k3s](https://k3s.io) cluster with [Ansible](https://www.ansible.com) and [Terraform](https://www.terraform.io) backed by [Flux](https://toolkit.fluxcd.io/) and [SOPS](https://toolkit.fluxcd.io/guides/mozilla-sops/).
|
||||||
|
|
||||||
The purpose here is to showcase how you can deploy an entire Kubernetes cluster and show it off to the world using the [GitOps](https://www.weave.works/blog/what-is-gitops-really) tool [Flux](https://toolkit.fluxcd.io/). When completed, your Git repository will be driving the state of your Kubernetes cluster. In addition with the help of the [Ansible](https://github.com/ansible-collections/community.sops), [Terraform](https://github.com/carlpett/terraform-provider-sops) and [Flux](https://toolkit.fluxcd.io/guides/mozilla-sops/) SOPS integrations you'll be able to commit GPG encrypted secrets to your public repo.
|
The purpose here is to showcase how you can deploy an entire Kubernetes cluster and show it off to the world using the [GitOps](https://www.weave.works/blog/what-is-gitops-really) tool [Flux](https://toolkit.fluxcd.io/). When completed, your Git repository will be driving the state of your Kubernetes cluster. In addition with the help of the [Ansible](https://github.com/ansible-collections/community.sops), [Terraform](https://github.com/carlpett/terraform-provider-sops) and [Flux](https://toolkit.fluxcd.io/guides/mozilla-sops/) SOPS integrations you'll be able to commit Age encrypted secrets to your public repo.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -51,23 +51,22 @@ For provisioning the following tools will be used:
|
|||||||
#### Required
|
#### Required
|
||||||
|
|
||||||
| Tool | Purpose |
|
| Tool | Purpose |
|
||||||
|--------------------------------------------------------------------|---------------------------------------------------------------------|
|
|----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| [ansible](https://www.ansible.com) | Preparing Ubuntu for Kubernetes and installing k3s |
|
| [ansible](https://www.ansible.com) | Preparing Ubuntu for Kubernetes and installing k3s |
|
||||||
| [direnv](https://github.com/direnv/direnv) | Exports env vars based on present working directory |
|
| [direnv](https://github.com/direnv/direnv) | Exports env vars based on present working directory |
|
||||||
| [flux](https://toolkit.fluxcd.io/) | Operator that manages your k8s cluster based on your Git repository |
|
| [flux](https://toolkit.fluxcd.io/) | Operator that manages your k8s cluster based on your Git repository |
|
||||||
| [gnupg](https://gnupg.org/) | Encrypts and signs your data |
|
| [age](https://github.com/FiloSottile/age) | A simple, modern and secure encryption tool (and Go library) with small explicit keys, no config options, and UNIX-style composability. |
|
||||||
| [go-task](https://github.com/go-task/task) | A task runner / simpler Make alternative written in Go |
|
| [go-task](https://github.com/go-task/task) | A task runner / simpler Make alternative written in Go |
|
||||||
| [ipcalc](http://jodies.de/ipcalc) | Used to verify settings in the configure script |
|
| [ipcalc](http://jodies.de/ipcalc) | Used to verify settings in the configure script |
|
||||||
| [jq](https://stedolan.github.io/jq/) | Used to verify settings in the configure script |
|
| [jq](https://stedolan.github.io/jq/) | Used to verify settings in the configure script |
|
||||||
| [kubectl](https://kubernetes.io/docs/tasks/tools/) | Allows you to run commands against Kubernetes clusters |
|
| [kubectl](https://kubernetes.io/docs/tasks/tools/) | Allows you to run commands against Kubernetes clusters |
|
||||||
| [pinentry](https://gnupg.org/related_software/pinentry/index.html) | Allows GnuPG to read passphrases and PIN numbers |
|
| [sops](https://github.com/mozilla/sops) | Encrypts k8s secrets with Age |
|
||||||
| [sops](https://github.com/mozilla/sops) | Encrypts k8s secrets with GnuPG |
|
|
||||||
| [terraform](https://www.terraform.io) | Prepare a Cloudflare domain to be used with the cluster |
|
| [terraform](https://www.terraform.io) | Prepare a Cloudflare domain to be used with the cluster |
|
||||||
|
|
||||||
#### Optional
|
#### Optional
|
||||||
|
|
||||||
| Tool | Purpose |
|
| Tool | Purpose |
|
||||||
|--------------------------------------------------------------------|---------------------------------------------------------------------|
|
|--------------------------------------------------------|----------------------------------------------------------|
|
||||||
| [helm](https://helm.sh/) | Manage Kubernetes applications |
|
| [helm](https://helm.sh/) | Manage Kubernetes applications |
|
||||||
| [kustomize](https://kustomize.io/) | Template-free way to customize application configuration |
|
| [kustomize](https://kustomize.io/) | Template-free way to customize application configuration |
|
||||||
| [pre-commit](https://github.com/pre-commit/pre-commit) | Runs checks pre `git commit` |
|
| [pre-commit](https://github.com/pre-commit/pre-commit) | Runs checks pre `git commit` |
|
||||||
@@ -118,56 +117,31 @@ Clone the repo to you local workstation and `cd` into it.
|
|||||||
|
|
||||||
:round_pushpin: **All of the below commands** are run on your **local** workstation, **not** on any of your cluster nodes.
|
:round_pushpin: **All of the below commands** are run on your **local** workstation, **not** on any of your cluster nodes.
|
||||||
|
|
||||||
### :closed_lock_with_key: Setting up GnuPG keys
|
### :closed_lock_with_key: Setting up Age
|
||||||
|
|
||||||
:round_pushpin: Here we will create a personal and a Flux GPG key. Using SOPS with GnuPG allows us to encrypt and decrypt secrets.
|
:round_pushpin: Here we will create a Age Private and Public key. Using SOPS with Age allows us to encrypt and decrypt secrets.
|
||||||
|
|
||||||
1. Create a Personal GPG Key, password protected, and export the fingerprint. It's **strongly encouraged** to back up this key somewhere safe so you don't lose it.
|
1. Create a Age Private / Public Key
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
export GPG_TTY=$(tty)
|
age-keygen -o age.agekey
|
||||||
export PERSONAL_KEY_NAME="First name Last name (location) <email>"
|
|
||||||
|
|
||||||
gpg --batch --full-generate-key <<EOF
|
|
||||||
Key-Type: 1
|
|
||||||
Key-Length: 4096
|
|
||||||
Subkey-Type: 1
|
|
||||||
Subkey-Length: 4096
|
|
||||||
Expire-Date: 0
|
|
||||||
Name-Real: ${PERSONAL_KEY_NAME}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
gpg --list-secret-keys "${PERSONAL_KEY_NAME}"
|
|
||||||
# pub rsa4096 2021-03-11 [SC]
|
|
||||||
# 772154FFF783DE317KLCA0EC77149AC618D75581
|
|
||||||
# uid [ultimate] k8s@home (Macbook) <k8s-at-home@gmail.com>
|
|
||||||
# sub rsa4096 2021-03-11 [E]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Create a Flux GPG Key and export the fingerprint
|
2. Set up the directory for the Age key and move the Age file to it
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
export GPG_TTY=$(tty)
|
mkdir -p ~/.config/sops/age
|
||||||
export FLUX_KEY_NAME="Cluster name (Flux) <email>"
|
mv age.agekey ~/.config/sops/age/keys.txt
|
||||||
|
|
||||||
gpg --batch --full-generate-key <<EOF
|
|
||||||
%no-protection
|
|
||||||
Key-Type: 1
|
|
||||||
Key-Length: 4096
|
|
||||||
Subkey-Type: 1
|
|
||||||
Subkey-Length: 4096
|
|
||||||
Expire-Date: 0
|
|
||||||
Name-Real: ${FLUX_KEY_NAME}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
gpg --list-secret-keys "${FLUX_KEY_NAME}"
|
|
||||||
# pub rsa4096 2021-03-11 [SC]
|
|
||||||
# AB675CE4CC64251G3S9AE1DAA88ARRTY2C009E2D
|
|
||||||
# uid [ultimate] Home cluster (Flux) <k8s-at-home@gmail.com>
|
|
||||||
# sub rsa4096 2021-03-11 [E]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
3. You will need the Fingerprints in the configuration section below. For example, in the above steps you will need `772154FFF783DE317KLCA0EC77149AC618D75581` and `AB675CE4CC64251G3S9AE1DAA88ARRTY2C009E2D`
|
3. Export the `SOPS_AGE_KEY_FILE` variable in your `bashrc`, `zshrc` or `config.fish` and source it, e.g.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Fill out the Age public key in the `.config.env` under `BOOTSTRAP_AGE_PUBLIC_KEY`, **note** the public key should start with `age`...
|
||||||
|
|
||||||
### :cloud: Global Cloudflare API Key
|
### :cloud: Global Cloudflare API Key
|
||||||
|
|
||||||
@@ -248,14 +222,12 @@ flux --kubeconfig=./provision/kubeconfig check --pre
|
|||||||
kubectl --kubeconfig=./provision/kubeconfig create namespace flux-system --dry-run=client -o yaml | kubectl --kubeconfig=./provision/kubeconfig apply -f -
|
kubectl --kubeconfig=./provision/kubeconfig create namespace flux-system --dry-run=client -o yaml | kubectl --kubeconfig=./provision/kubeconfig apply -f -
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Add the Flux GPG key in-order for Flux to decrypt SOPS secrets
|
3. Add the Age key in-order for Flux to decrypt SOPS secrets
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
source .config.env
|
cat ~/.config/sops/age/keys.txt |
|
||||||
gpg --export-secret-keys --armor "${BOOTSTRAP_FLUX_KEY_FP}" |
|
kubectl -n default create secret generic sops-age \
|
||||||
kubectl --kubeconfig=./provision/kubeconfig create secret generic sops-gpg \
|
--from-file=age.agekey=/dev/stdin
|
||||||
--namespace=flux-system \
|
|
||||||
--from-file=sops.asc=/dev/stdin
|
|
||||||
```
|
```
|
||||||
|
|
||||||
:round_pushpin: Variables defined in `./cluster/base/cluster-secrets.sops.yaml` and `./cluster/base/cluster-settings.sops.yaml` will be usable anywhere in your YAML manifests under `./cluster`
|
:round_pushpin: Variables defined in `./cluster/base/cluster-secrets.sops.yaml` and `./cluster/base/cluster-settings.sops.yaml` will be usable anywhere in your YAML manifests under `./cluster`
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ spec:
|
|||||||
decryption:
|
decryption:
|
||||||
provider: sops
|
provider: sops
|
||||||
secretRef:
|
secretRef:
|
||||||
name: sops-gpg
|
name: sops-age
|
||||||
postBuild:
|
postBuild:
|
||||||
substitute: {}
|
substitute: {}
|
||||||
substituteFrom:
|
substituteFrom:
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ spec:
|
|||||||
decryption:
|
decryption:
|
||||||
provider: sops
|
provider: sops
|
||||||
secretRef:
|
secretRef:
|
||||||
name: sops-gpg
|
name: sops-age
|
||||||
postBuild:
|
postBuild:
|
||||||
substitute: {}
|
substitute: {}
|
||||||
substituteFrom:
|
substituteFrom:
|
||||||
|
|||||||
+11
-11
@@ -31,7 +31,7 @@ main() {
|
|||||||
verify_ansible_hosts
|
verify_ansible_hosts
|
||||||
verify_metallb
|
verify_metallb
|
||||||
verify_kubevip
|
verify_kubevip
|
||||||
verify_gpg
|
verify_age
|
||||||
verify_git_repository
|
verify_git_repository
|
||||||
verify_cloudflare
|
verify_cloudflare
|
||||||
success
|
success
|
||||||
@@ -117,22 +117,22 @@ _has_valid_ip() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
verify_gpg() {
|
verify_age() {
|
||||||
_has_envar "BOOTSTRAP_PERSONAL_KEY_FP"
|
_has_envar "BOOTSTRAP_AGE_PUBLIC_KEY"
|
||||||
_has_envar "BOOTSTRAP_FLUX_KEY_FP"
|
_has_envar "SOPS_AGE_KEY_FILE"
|
||||||
|
|
||||||
if ! gpg --list-keys "${BOOTSTRAP_PERSONAL_KEY_FP}" >/dev/null 2>&1; then
|
if [[ ! "$BOOTSTRAP_AGE_PUBLIC_KEY" =~ ^age.* ]]; then
|
||||||
_log "ERROR" "Invalid Personal GPG FP ${BOOTSTRAP_PERSONAL_KEY_FP}"
|
_log "ERROR" "BOOTSTRAP_AGE_PUBLIC_KEY does not start with age"
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
_log "INFO" "Found Personal GPG Fingerprint '${BOOTSTRAP_PERSONAL_KEY_FP}'"
|
_log "INFO" "Age public key is in the correct format"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! gpg --list-keys "${BOOTSTRAP_FLUX_KEY_FP}" >/dev/null 2>&1; then
|
if [[ ! -f ~/.config/sops/age/keys.txt ]]; then
|
||||||
_log "ERROR" "Invalid Flux GPG FP '${BOOTSTRAP_FLUX_KEY_FP}'"
|
_log "ERROR" "Unable to find Age file keys.txt in ~/.config/sops/age"
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
_log "INFO" "Found Flux GPG Fingerprint '${BOOTSTRAP_FLUX_KEY_FP}'"
|
_log "INFO" "Found Age public key '${BOOTSTRAP_AGE_PUBLIC_KEY}'"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ verify_binaries() {
|
|||||||
_has_binary "envsubst"
|
_has_binary "envsubst"
|
||||||
_has_binary "flux"
|
_has_binary "flux"
|
||||||
_has_binary "git"
|
_has_binary "git"
|
||||||
_has_binary "gpg"
|
_has_binary "age"
|
||||||
_has_binary "helm"
|
_has_binary "helm"
|
||||||
_has_binary "ipcalc"
|
_has_binary "ipcalc"
|
||||||
_has_binary "jq"
|
_has_binary "jq"
|
||||||
|
|||||||
+4
-5
@@ -2,10 +2,9 @@
|
|||||||
creation_rules:
|
creation_rules:
|
||||||
- path_regex: provision/.*\.sops\.ya?ml
|
- path_regex: provision/.*\.sops\.ya?ml
|
||||||
unencrypted_regex: "^(kind)$"
|
unencrypted_regex: "^(kind)$"
|
||||||
pgp: >-
|
age: >-
|
||||||
${BOOTSTRAP_PERSONAL_KEY_FP}
|
${BOOTSTRAP_AGE_PUBLIC_KEY}
|
||||||
- path_regex: cluster/.*\.ya?ml
|
- path_regex: cluster/.*\.ya?ml
|
||||||
encrypted_regex: "^(data|stringData)$"
|
encrypted_regex: "^(data|stringData)$"
|
||||||
pgp: >-
|
age: >-
|
||||||
${BOOTSTRAP_FLUX_KEY_FP},
|
${BOOTSTRAP_AGE_PUBLIC_KEY}
|
||||||
${BOOTSTRAP_PERSONAL_KEY_FP}
|
|
||||||
@@ -25,4 +25,4 @@ spec:
|
|||||||
decryption:
|
decryption:
|
||||||
provider: sops
|
provider: sops
|
||||||
secretRef:
|
secretRef:
|
||||||
name: sops-gpg
|
name: sops-age
|
||||||
Reference in new issue
Block a user