sops setup
Table of Content
- steps
- install gpg and sops
- generate gpg key
- retrieve the fingerprint of the generated gpg key
- export public and private keypair as secret on the kubernetes cluster
- upload gpg public key to the gitops repo
- delete local gpg keypair
- import public key for encryption
- add sops configuration file to the sops directory
- set flux kustomization on SOPS directory
- demo on secret encryption
sops setup¶
https://fluxcd.io/flux/guides/mozilla-sops/
In GitOps, all the declarative changes including secrets must be placed on a repository. SOPS can provide encryption for the GitOps repository files.
The official document above explains the steps to setup SOPS using a separate git repository, but here I will setup SOPS on the same GitOps repository managing the cluster.
steps¶
- install gpg and sops
- generate gpg key
install gpg and sops¶
GPG should be available from the distribution package.
SOPS should be installed separately.
https://github.com/getsops/sops
curl -LO https://github.com/getsops/sops/releases/download/v3.9.4/sops-v3.9.4.linux.amd64
mv sops-v3.9.4.linux.amd64 ~/.local/bin/sops
chmod u+x ~/.local/bin/sops
sops --version
generate gpg key¶
export KEY_NAME="lab-hlv3.lab.blink-1x52.net"
export KEY_COMMENT="flux secrets"
gpg --batch --full-generate-key <<EOF
%no-protection
Key-Type: 1
Key-Length: 4096
Subkey-Type: 1
Subkey-Length: 4096
Expire-Date: 0
Name-Comment: ${KEY_COMMENT}
Name-Real: ${KEY_NAME}
EOF
retrieve the fingerprint of the generated gpg key¶
export public and private keypair as secret on the kubernetes cluster¶
Find the fingerprint from the second row of the "sec:" output and export it.
Here is the example output from the flux document.
And generate the secret.
# create gpg keypair secret on flux-system
gpg --export-secret-keys --armor "${KEY_FP}" |
kubectl create secret generic sops-gpg \
--namespace=flux-system \
--from-file=sops.asc=/dev/stdin
# confirm
kubectl get secret sops-gpg -n flux-system
upload gpg public key to the gitops repo¶
# on gitops repo
mkdir -p sops/lab-hlv3
gpg --export --armor "${KEY_FP}" > sops/lab-hlv3/.sops.pub.asc
delete local gpg keypair¶
Delete the generated keypair to remove private key off host.
import public key for encryption¶
Import gpg public key on any host that needs to encrypt gitops manifest file.
add sops configuration file to the sops directory¶
# on gitops repo
cat <<EOF > sops/lab-hlv3/.sops.yaml
creation_rules:
- path_regex: .*.yaml
encrypted_regex: ^(data|stringData)$
pgp: ${KEY_FP}
EOF
set flux kustomization on SOPS directory¶
This is similar to the other flux kustomizations created to setup separate layers of infrastructure and apps kustomizations. It is using the same sourceRef which is the very GitOps repository flux was bootstrapped with. The main difference is the .spec.decryption
section specifying the provider and the gpg keypair to use (secret generated from keypair export earlier).
cat <<'EOF' > clusters/lab-hlv3/sops.yaml
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: sops
namespace: flux-system
spec:
decryption:
provider: sops
secretRef:
name: sops-gpg
interval: 1m0s
path: ./sops/lab-hlv3
prune: true
sourceRef:
kind: GitRepository
name: flux-system
EOF
Wait for the reconciliation and you will see the new flux kustomization "sops" in the list.
$ flux get ks
NAME REVISION SUSPENDED READY MESSAGE
apps main@sha1:1b76dba2 False False dependency 'flux-system/infra-configs' revision is not up to date
flux-system main@sha1:de9c4295 False True Applied revision: main@sha1:de9c4295
infra-configs main@sha1:1b76dba2 False False dependency 'flux-system/infra-controllers' is not ready
infra-controllers main@sha1:de9c4295 False True Applied revision: main@sha1:de9c4295
sops main@sha1:de9c4295 False True Applied revision: main@sha1:de9c4295
demo on secret encryption¶
Let's create a dummy secret in the placeholder namespace.
# on gitops repo
mkdir sops/lab-hlv3/placeholder
# create a secret yaml file
kubectl -n placeholder create secret generic dummy \
--from-literal=user=username \
--from-literal=password=passwordforusername \
--dry-run=client \
-o yaml > sops/lab-hlv3/placeholder/dummy.yaml
# encrypt
# chdir where sops configuration file is placed at
cd sops/lab-hlv3
sops --encrypt --in-place placeholder/dummy.yaml
# commit and push
Here is the content of the yaml file, before and after the sops encryption.
# before
$ cat sops/lab-hlv3/placeholder/dummy.yaml
apiVersion: v1
data:
password: cGFzc3dvcmRmb3J1c2VybmFtZQ==
user: dXNlcm5hbWU=
kind: Secret
metadata:
creationTimestamp: null
name: basic-auth
namespace: default
# after the encryption
$ cat sops/lab-hlv3/placeholder/dummy.yaml
apiVersion: v1
data:
password: ENC[AES256_GCM,data:DWjyV2ooXZc2z+jBvzWfKrzz2oU8sSa/evt3rQ==,iv:m243VqNqgMgITXuygRuDIKDPDb8g1dbXT6+nkDL960E=,tag:ThXtGSft4AWcWvZrkFjaQA==,type:str]
user: ENC[AES256_GCM,data:yFbRgz3holhq1jZh,iv:T2KWIc84ntnbWF69AUsqlCNVh0iI8o0jdPy7Lwmy6oc=,tag:mi+n1lTCYiDpYIrdbC4Ntw==,type:str]
kind: Secret
metadata:
creationTimestamp: null
name: basic-auth
namespace: default
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age: []
lastmodified: "2025-03-27T08:14:27Z"
mac: ENC[AES256_GCM,data:npyzMmL71epRGsqTIDAyCMpiIYYR+qJPpC05Fd8XRxnaPGKbZ3z9rzRJIb/f0ei0J7qyi5f2SKmkYvGpN2DIdiV10Gake59LohUG909XTUuTdPS1Cr85/ygUN20AY3PaUJ82wTpIO1eM8rJfW7SJSrvlcXo81h/QTQtMMyF1w8U=,iv:d98j1wrTz6PVQrHDC5quS9ldGrRrIuYAqwu8KdAcp8Q=,tag:vVg3ceWRgzdtMPbPBaPE+Q==,type:str]
pgp:
- created_at: "2025-03-27T08:14:27Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQIMA1ZiUHDvVyFzARAAlb55Oj8tQw+/UoqQq+cPFOxn4Fkor3ffeFQgHyMUXiuc
sTSM1plrY6VqHBSszKr7qHdj8FKw/KdgSxlNvo8C8bYtgoo0SICLnUSnLwdP7co6
bEWJ9b+gFeqUgXbs+ugJbwQPy1QgyRjU9hS2YILg5WyOO4Yxs0K6ov6Aq8QEQxnM
6WLKr+7rAlsZvZiXz4ylQPw5motoHD5NJnc/S20nWKQXoNPQglH8nF2NK9ormQ1w
XD2cqzkF20CEHiMKe/9pdyuz9U3n53giu8y1UtCmEO6dZvuWLa8Vw/Pme+8RQ7s4
O4P55VQvCSCwrWByl9ZRDRUhOzz+gLAz9zSAyksokN49A43B7XUprmVZsUOokGa/
hZoDRnq5PBlWzVIUJ6iQgXrce/JlWKPXpTr/pGOp5LhJwn4PhBeQNrOKMzO/Il1W
/BfgwCGwdpdvNGBxNCjkHa2zCeqdjfgSQfdjggV5VxpPJbSd/BTOyHwlOj4rLdAS
4rghIGB+2fRxgzj7MWpZdYBqfz+UE1jdFQg9zcJGwuLKvnUZNmhIuuGFj7odWGI4
cHwyek9iW5x9WtPCnDV3XsLHevpuUqrm/9TmM/Vv13PvuL734bkw4Wn4m6F0FM3o
54s6SKQobGuTgJwtVhMYCbqvykGiq9b9CBtIX+ot/dpuB25OExmOPOfM2+GM8HfU
aAEJAhDeX2/rkXQMr12PWjj0rsTJ41p+6aZgcsiY0QjgRDH5z0wfA1jtK1suEz/z
w074qr7/2JEqpjJSrjwxum6MvG2ve04Rh5sWmV1f8tgyOKj6fjyj2xXGaCaSUA8c
yZznilTPD8zc
=7/av
-----END PGP MESSAGE-----
fp: FF44D91FBED62563A999B5E0A9697E97CDDD9973
encrypted_regex: ^(data|stringData)$
version: 3.9.4
Once flux reconciliation is complete for sops ks, run kubectl get secret dummy -n placeholder -o yaml
and you can confirm the same content before the encryption is on the cluster.