SOPS(Secrets OPerationS) 란?

sops 는 YAML, JSON, ENV, INI, BINARY 형식의 파일 암호화를 지원하고 AWS KMS, GCP KMS, Azure Key Vault, age 등을 이용하여 암호화하는 파일 편집기 입니다.

출처 : https://github.com/mozilla/sops

vs kubeseal

GitOps 를 하다보면 Secret 혹은 API Token, Key 등을 git repository 에 실수로 Push 하여 침해 사고를 겪는 경우가 많이 발생 됩니다.
그리하여 kubeseal 이라는 Sealed Secret 도구를 이용하여 ConfigMap 혹은 Secret 을 Sealed 하여 Git 에 Push 하는 방식을 선택하기도 합니다.

kubeseal 도 보안을 강화하는 장점이 있지만,
GitOps 를 위해 초기 보안 강화 도입이 목적이라면 SOPS 가 더 편리하고 좋을 것으로 보입니다.

이유는 아래와 같습니다.

  • kubeseal 은 Kubernetes Cluster 에 CRD 와 Operator 를 배포해야 됩니다.
  • 원본 Yaml 을 Sealed 하는 과정 후, 제거를 안한 원본 Yaml 을 실수로 Git push 하는 경우가 실수가 발생 할 수 있습니다.

SOPS 설치

SOPS 설치는 아래와 같은 방식으로 진행합니다.

(test) root@testvm-c7 ~/chhan/rpm $ wget https://github.com/mozilla/sops/releases/download/v3.7.3/sops-3.7.3-1.x86_64.rpm
(test) root@testvm-c7 ~/chhan/rpm $ yum localinstall -y sops-3.7.3-1.x86_64.rpm

(test) root@testvm-c7 ~/chhan/rpm $ rpm -ql sops-3.7.3-1.x86_64
/usr/local/bin/sops

Age 설치

SOPS 에서 사용 가능한 암호화 방식중 Local 에서 바로 암호화 하는 방식을 이용하기 위해 Age 를 설치하도록 합니다.

(test) root@testvm-c7 ~/chhan/rpm $ wget https://github.com/FiloSottile/age/releases/download/v1.0.0/age-v1.0.0-linux-amd64.tar.gz
(test) root@testvm-c7 ~/chhan/rpm $ tar xzvf age-v1.0.0-linux-amd64.tar.gz
(test) root@testvm-c7 ~/chhan/rpm $ cd age/
(test) root@testvm-c7 ~/chhan/rpm/age $ cp age* /usr/local/bin
(test) root@testvm-c7 ~/chhan/rpm/age $ ls -la /usr/local/bin | grep age
-rwxr-xr-x  1 root root   4453430 Aug 16 15:23 age
-rwxr-xr-x  1 root root   2660116 Aug 16 15:23 age-keygen

Age 암호화 Key 생성

age-keygen 명령을 통해 암호화 Key 를 생성합니다.

(test) root@testvm-c7 ~/chhan/rpm $ age-keygen -o key.txt
Public key: age1f8y5zwet3mxv6cw0vt96yjeka7hedkqeze3spff8jxzz2wgcs9aspl99c9

(test) root@testvm-c7 ~/chhan/rpm $ cat key.txt
# created: 2022-08-16T15:26:16+09:00
# public key: age1f8y5zwet3mxv6cw0vt96yjeka7hedkqeze3spff8jxzz2wgcs9aspl99c9
AGE-SECRET-KEY-1EYSCLWDNDMKWPK9HJMWG6LFARXSKU06SUCF6XY4N4L2QZTSQRYAQ7TE2TV

Secret/ConfigMap 암호화

.bashrc 혹은 .zshrc 등 Shell ENV 에 Age Key File 정보 및 Age RECIPIENTS 정보를 추가합니다.

export SOPS_AGE_RECIPIENTS=$(cat key.txt |grep -oP "public key: \K(.*)")
export SOPS_AGE_KEY_FILE=$(pwd)/key.txt
  • 테스트 환경에 따라 경로 및 file name 을 수정합니다.

암호화에 사용할 원본 Yaml 은 아래와 같습니다.

(test) root@testvm-c7 ~/chhan/rpm $ kubectl create secret generic my-secret --from-literal=superpw=supersecret -o yaml --dry-run > test.yml
 
(test) root@testvm-c7 ~/chhan/rpm $ cat test.yml
apiVersion: v1
data:
  superpw: c3VwZXJzZWNyZXQ=
kind: Secret
metadata:
  creationTimestamp: null
  name: my-secret

test.yml 을 암호화 해보겠습니다.

(ENV 선언이 없는 경우) $ sops --encrypt --age $(cat key.txt |grep -oP "public key: \K(.*)") test.yml
(ENV 선언이 된 경우)   $ sops -e test.yml
apiVersion: ENC[AES256_GCM,data:lzU=,iv:oVl0aEc/QCWgsOentMO/3dTt3hj1CwKusqPu8HzsFqg=,tag:pueW5DwAhVvRu8uVoEJnGg==,type:str]
data:
    superpw: ENC[AES256_GCM,data:wr4ye83kOHkvVs1ttcH4LQ==,iv:7mWUK3OjcJzOI4OhakGsWPAndx431LqO7TNfHE61yZE=,tag:Mvej5RAZrPKO/W4PDgX5VQ==,type:str]
kind: ENC[AES256_GCM,data:/Eba+isu,iv:cdJCdv2//6gpZ+8E8yftppqw4foSwB/ko+U2SWWPew0=,tag:boo5PlBsGUbKyUGTwruXOg==,type:str]
metadata:
    creationTimestamp: null
    name: ENC[AES256_GCM,data:dnBOFT9I8Ex/,iv:+w8vI4Jow4gLtqCYu6+Vbpe8MAYVH71H2RFzb/mXvJY=,tag:oIcxsvXP1VEeZnQ2iwNSeg==,type:str]
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    hc_vault: []
    age:
        - recipient: age1f8y5zwet3mxv6cw0vt96yjeka7hedkqeze3spff8jxzz2wgcs9aspl99c9
          enc: |
            -----BEGIN AGE ENCRYPTED FILE-----
            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTeWV4VzB2Zm56aUlPTnpk
            enBoZTlYNjZHMXVlQlRLSS92RTZ4MEhYMldzCjZ3ZjNjK2wySjZ2bWc1VDBob2FW
            Y2c3SjhZbGhwMjBDMHVkcmJxMTlBL28KLS0tIGRoM0I5cFYzQmQ4T3hMLy8ySnoz
            aG1GeWx0bk02QVFxeVROU0M2KzVGV0EK8pKGUc8wa7ZVwCDB97MJQktHc2r0BN7K
            YU4x3dSpL2KeC2XNYmV3EPqeB21+xHXz37k8PPLj5QpZXPGkcbVx5w==
            -----END AGE ENCRYPTED FILE-----
    lastmodified: "2022-08-16T06:53:18Z"
    mac: ENC[AES256_GCM,data:tfJZ5Ooi/GNQRedtdqEd1y0G8O9EDKT3J82xZeysHqjxW/h7MqpLI2dQKSqXxpIKycFNtkHEzxfpcD0tTTA/qFXUeLTqMN7jvOohBdjkaWT9tER86jqslGUeT0DRsm1dvI9sJ1N0Q+KY0fR9DAnCalXEoiTHcuHcPDGj4XSfSIA=,iv:Uhxp0iJ6/ijZHentb5MKX+bUg8/NulzgkrRHnaJhv78=,tag:t8s//SjRx600Lf6jDo0STw==,type:str]
    pgp: []
    unencrypted_suffix: _unencrypted
    version: 3.7.3

기본적으로 -e (encrypt) 옵션을 사용하면 출력만 되고 -i (write output back to the same file instead of stdout) 을 사용하면 해당 파일에 바로 작성하는 방법이 있습니다.
-i 옵션을 미사용하는 경우, 아래와 같은 방법으로 encrypt 된 파일을 생성 할 수 있습니다.

$ sops -e test.yml > test.enc.yml

복호화

암호화된 파일을 아래와 같은 방법으로 복호화가 가능합니다.

(test) root@testvm-c7 ~/chhan/rpm $ sops -d test.yml
apiVersion: v1
data:
    superpw: c3VwZXJzZWNyZXQ=
kind: Secret
metadata:
    creationTimestamp: null
    name: my-secret

Kubernetes 에서 사용

암호화된 Secret 을 아래와 같은 방법으로 바로 Kubernetes 에 사용이 가능합니다.

(test) root@testvm-c7 ~/chhan/rpm $ sops -d test.yml | kubectl create -f -
secret/my-secret created
(test) root@testvm-c7 ~/chhan/rpm $ kubectl get secret my-secret
NAME        TYPE     DATA   AGE
my-secret   Opaque   1      9s

암호화된 파일 수정

따로 원본을 수정하고 다시 암호화하는 과정이 필요없고 암호화된 파일을 바로 vim 와 같은 시스템에 설정된 editor 으로 수정이 가능합니다.

(test) root@testvm-c7 ~/chhan/rpm $ sops test.yml
"편집 화면"
apiVersion: v1
data:
    superpw: c3VwZXJzZWNyZXQ=
kind: Secret
metadata:
    creationTimestamp: null
    name: my-secret

Git Diff 적용

기본적으로 Git diff 를 사용하면 암호화된 파일을 아래와 같이 diff 됩니다.

# git diff base.enc.yml
diff --git a/base.enc.yml b/base.enc.yml
index 1471633..6c181fb 100644
--- a/base.enc.yml
+++ b/base.enc.yml
@@ -1,6 +1,7 @@
 apiVersion: ENC[AES256_GCM,data:7C4=,iv:qLmEYR1pqnH6EOMAsTdS9vNANNMQcV6qeSaTXSCWzxs=,tag:BrSYC4e+XLyUUEO0n1PGgw==,type:str]
 data:
     superpw: ENC[AES256_GCM,data:4Aq+erGRt5QYYFIyeEX8Kw==,iv:9/bo92QzP0d6/uKN0z68jBa0sdtjbe0TVRDSpB7Nml0=,tag:RFNj5ehJyZpGEbjAzYXGjQ==,type:str]
+    superpw2: ENC[AES256_GCM,data:BCTgOGr/8j2oYTrp0jPHdA==,iv:o/9KVllyIDXty8NcFjiIqF2pI0K6GxOuxeCEkXWtAbY=,tag:IloTiPZqcG6pcNGFqkAP1w==,type:str]
 kind: ENC[AES256_GCM,data:6jKb68d5,iv:256KcMI62cJ99ZYJLx99yUIVWWwkHfyZW2HlRa/Xtu4=,tag:Y8SsTkzx73Ng6G0247P/Nw==,type:str]
 metadata:
     creationTimestamp: null
@@ -20,8 +21,8 @@ sops:
             OGw3dEVhbUNwYW4ySDlSU2N6aWVzaFEKjo+QF4388+g/Jxe1OurwWfHhOS0NvHSo
             r+dfBDFciZV9ofMP0r4+Cp9yRe58lNlvJ4IMspFvfyw3raepIMcX8A==
             -----END AGE ENCRYPTED FILE-----
-    lastmodified: "2022-08-17T01:04:01Z"
-    mac: ENC[AES256_GCM,data:wNToFg7hsBMjCwXjdVd9OAJkrVf1/pP+pKNfhH8RD8lvkZYe2xYcVB9sRE1/b4F0/U++UW4J33J6EXQmJZstwGeLKesTVBi6Rizjg5XxoR05aU/T8IM1o8gAEkXeZCyL7CaoBj7nKNFf4m/yLZxOqo4TOxMT2wRwX7Tl7xg6APg=,iv:gqW
+    lastmodified: "2022-08-17T01:05:59Z"
+    mac: ENC[AES256_GCM,data:EWnw62B39yLU/tGsP3Gca/+oUsgO+QKjNeEetIXO8CsstbxcKZgLFsckiSI9DNZPKEoPa02GUltYgSbREKAscTpQANIrCJ0Rj1SoZbfzMVH45/+TPU/TRzZpEkQaN6/ulzH3BcecZaEbIWQVoyRQnfSUFN/CvdG5ueF/wCmF0xg=,iv:ZcN
     pgp: []
     unencrypted_suffix: _unencrypted
     version: 3.7.3

Source 에서 무엇이 수정된 건지 정확하게 파악이 안됩니다.

아래와 같이 git 설정을 진행하면 복호화된 git diff 를 볼 수 있습니다.

root@testvm-c7 ~/chhan/rpm/gitdiff (main)$ cat .gitattributes
*.yml diff=sopsdiffer

.gitattributes 를 생성합니다. sopsdiffer 를 사용할 파일 형식을 지정합니다.
여기서는 *.yml 에 대해서만 diff 하는 경우 sopsdiffer 를 이용하도록 설정합니다.

root@testvm-c7 ~/chhan/rpm/gitdiff (main)$ git config diff.sopsdiffer.textconv "sops -d"
root@testvm-c7 ~/chhan/rpm/gitdiff (main)$ git diff base.enc.yml
diff --git a/base.enc.yml b/base.enc.yml
index 1471633..6c181fb 100644
--- a/base.enc.yml
+++ b/base.enc.yml
@@ -1,6 +1,7 @@
 apiVersion: v1
 data:
     superpw: c3VwZXJzZWNyZXQ=
+    superpw2: c3VwZXJzZWNyZXQ=         << 추가 혹은 변경된 부분
 kind: Secret
 metadata:
     creationTimestamp: null

다양한 OS 지원

SOPS 는 Linux, Mac, Windows 등 다양한 운영체제를 지원하여 VSCode 연동등 여러가지 방법으로 활용이 가능합니다.

참고 자료

chhanz's profile image

chhanz

2022-08-17

Read more posts by this author