Manage your secrets in Git with SOPS for Kubernetes ☸️

Kevin Davin - Jun 13 '20 - - Dev Community

In previous parts, we see how to manage our secrets in Git and usable by humans and CI. Right now, we will discover how
to integrate it with Kubernetes, and especially with Kubernetes Secrets.

😊 Naive usage

Since the beginning, Alice, Bobby and Devon are using only *.env file to store secrets. They have to use them
in a Kubernetes context, to create a secret named app-secret. The simple (and naive way) to do this is to use
the kubectl application to create the secret from an env files:

This solution is pretty basic and rely on the capacity of the kubectl cli to create secret from an *.env file. The main problem here is the fact we have to generate our secret from the env file. In Kubernetes context, when we have multiple manifests, we like to have the same semantic and organisation, which is simpler to read and debug.

🔒 Sops & YAML

To match the team expectation, Devon will move to a Kubernetes YAML format for secrets instead of relying on *.env files. To do so, he will use the output generated from the previous example and use sops to encrypt it.

The SOPS output is like this:

apiVersion: ENC[AES256_GCM,data:gEA=,iv:2jeZ0y0SEPXZTgrsmYXc7LgPydoPwnKzF4GVqykSw5M=,tag:YzEMrctU19sUIqat+LUFIw==,type:str]
data:
    secret: ENC[AES256_GCM,data:uJIDP3yMdFyW/7bnBAU0MJp07xmF0nlh,iv:ardoVW+p9HLNpaWfXOWrevZFdNJqGJvt00jPbkrr7p4=,tag:mB3wqa7kNE7gZ9J87IboNg==,type:str]
kind: ENC[AES256_GCM,data:tu+OHbXI,iv:QfqZeYOkTPTzJ618gh+/zGWPMDBJfjp+GwM8yBZCD+Y=,tag:XrJfNhmyDhOQNncd/uNvxA==,type:str]
metadata:
    name: ENC[AES256_GCM,data:X8WMTByIcUrjag==,iv:pQ6xt5DPchrxwXtt98l4mgCixpcpaA2ewST0lAqYY4E=,tag:fqj/1PbzbhjIhRx9Ogitxg==,type:str]
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    lastmodified: '2020-06-13T12:21:16Z'
    mac: ENC[AES256_GCM,data:dU1bm2GBwdkqMQqEa+b0EfuMG5BRiiHDxNjB3tvB4JTVxqamQ17A47tzMld93/lZnJWRb4BtDFUI2xHh/5BCpxdI4J67AvWdv91usRgpgh7z7SF4pK4UUVah3WDYd3tvo+VzugvN6b4V+oGZlfJM789dJrTDOjSCAyyaaa/Qfb0=,iv:Cdaet+2Sowq50TDIkDO1RXi4vgaVqe7rYz08PHybAv0=,tag:1aeR/f4NnydiP2Vuuvljvw==,type:str]
    pgp:
    -   created_at: '2020-06-13T12:21:16Z'
        enc: |
            -----BEGIN PGP MESSAGE-----

            hQGMA5/a4uV8wP1EAQwAshoGsWCKiYnsi1Rr/HoMtvp8Tx5cmUWLPz0OsGsQUFc/
            HQ48h7KUAjemTBnyQ6SpZUK2uI5YOLnOIqNcQJvNb4o30o0M3kuD2KNzSbsRGt3j
            vODVKcs699sT1mX/rc6/EzpLy0NUgrAlXn7IUG+rhnk97OvfMdjP6+i77OpSU9Uc
            5l6FWokBzFBvL6EENay6EO54C17MBXijoM4h6x7YajOjk5VNFtUl7wh039VGWZpQ
            7vMuTbla06DWAHIFKIOs54yEh+vMTMgnsg8NrPzz4xqKDMcmZoQIm1jwc8EvxYK4
            nGI4LpXO8VMgCyHBEcMjCGhZ5xXAiMRzF9M8XAWRfDFgYjDosL8QOQkl4vVnsf4e
            5bhITFz1ZGaJwEeVzlt82cxWy4+BNq+lfAghqEnHGAjLGFIgTiRtVB47NLk9WPIT
            T7Qv5gvATdW0rNHecfoGCKWx15ZJezXSo8Vtt5eVPgdYd3wO76Uw6XWhkxwSgQNT
            vE7W5qvLoqxZrJs4Kyd20l4BQzSEiHJRg7wmWx9Y0BhyzF1Zh6F0oQBxTzGjP0f1
            J9THfwIOhyOBwm8bWkEz7ac84o2NEItdrvlhrB0Y61ZfRl6xZKWqFxJ3N1yVHxxM
            MWRYzaQcuoh5670lRhY5
            =KjAd
            -----END PGP MESSAGE-----
        fp: 57E6DA39E907744429FB07871141FE9F63986243
    unencrypted_suffix: _unencrypted
    version: 3.5.0
Enter fullscreen mode Exit fullscreen mode

SOPS is encrypting every value of the previously generated YAML, including kind or apiVersion and metadata.name. This is problematic because the whole team need to be able to read keys, which are not sensible in this Kubernetes secret.

🔏 Encrypt specific keys only

SOPS provides an handy parameter to choose which keys should be encrypted. This could be used directly from the command line sops --encrypt --encrypted-regex '^(data|stringData)$' app-secret.yaml.

DISCLAIMER: For concision, I've removed a part of the generated files. SOPS parameters are included into the YAML and are verbose.

The file looks like this now:

apiVersion: v1
data:
    secret: ENC[AES256_GCM,data:RLfYML8bJ0d5kN0nudgGgJJQIJMHK9MB,iv:6rurG5rErwn2O7PrHVoPjRnkMT5MjenhRmF3Vxh/cVw=,tag:4B5NeHbTmm0yl5VFlA9fww==,type:str]
kind: Secret
metadata:
    name: app-secret
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    lastmodified: '2020-06-13T12:32:40Z'
    mac: ENC[AES256_GCM,data:jl00u8YtegEi3lvuHv70PtI6Zywo8tSDH2DKk2jw+b4yiUGU2y9T82t3bE6ahY1MWy1U6CRK9NYKBS8p5HJqDjH/N21rhQy7k3g4BuzG/CJt2S/xio0KaAwwGXURUtXbHpXLrko22q0cgqCrsr/IFvz65r6WNhji0utSC1FKCPQ=,iv:XoilcDIZR8ir/ySbu+xVGNO0NkEkQr/IlyKXvAyyeyM=,tag:Hraabep8cq85M3OjvjoQBA==,type:str]
    pgp: ... # Removed for concision
    encrypted_regex: ^(data|stringData)$
    version: 3.5.0
Enter fullscreen mode Exit fullscreen mode

We can do better, because SOPS provides us a solution to store configuration required for every secret directly into the .sops.yaml. We can add the key encrypted-regex to simplify the command line. This is helpful when you need to add another secret key in an existing secret.

Every team member can now read and edit Kubernetes secret simply 🎉.

🦊 CI Integration

The CI needs to be adjusted to be able to deploy those manifests. We are using the base described in the previous article.

deploy int:
  image: registry.gitlab.com/davinkevin/sops-blog-post-repository/gcloud-sops
  before_script:
  - *auth
  script: 
  - sops -d app-secret.yaml | kubectl apply -f -
Enter fullscreen mode Exit fullscreen mode

Conclusion

If you are using Kubernetes, and you want to manage your secrets in Git, SOPS will be a good match ❤️. You will be able to encrypt only desired value in a YAML file to keep it readable, by a human and other tools working on Kubernetes manifests.

You can find the source code of this article, files and scripts in this GitLab repository.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .