Password를 어떻게 저장해야 하지

Bumjin Kim·2023년 4월 7일
0

SecOps

목록 보기
1/1
post-thumbnail

Photo by Towfiqu barbhuiya on Unsplash

오늘도 다시 한 번 패스워드를 어떻게 관리해야 하는가에 대한 best practice의 부재에 대해 뼈저리게 느끼며, 가능한 다른 방법을 찾아보던 중이었다. 보통은 이런 일이다.

  1. Password가 존재, 프로젝트 내부에서 사용해야 할 니즈가 발생
  2. 다수의 경우 .env파일로 만들어서 환경 변수로 import하여 사용, 하지만 .gitignore에 등록해서 git repository에 저장하지는 않음
  3. 그러면 이 정보가 바뀌면 어떻게 싱크해야 하나? 모름.
  4. 다수의 경우 이 파일을 이메일이나 채팅, 원드라이브로 공유하면서 사용.

하지만 패스워드 보안에 대해 심각하게 고려하고 있고, 회사에서 가이드라인까지 제공될 정도라면 위 4번과 같이 해서는 안 될 것이다. 그렇다고 랩탑 분실이나 SSD 고장과 같은 불의의 사고로 이 내용이 복구 불가능해지는 것 또한 바라지 않을 것이다.

결국 어딘가 공유 가능한 곳에 저장해야 한다.

이 글을 보면 - https://www.reddit.com/r/devops/comments/tzufc9/comment/i42bp85/
"어디든 회사 내의 패스워드를 공유하는 곳에 저장하고, 그곳에서 스크립트로 credential을 가져와서 .env를 만들어서 쓰게 하라"
고 되어 있다. 하지만 이것도 패스워드 저장소가 integration이 가능할 때나 할 수 있는 이야기이다.

그러면 source code repository에도 저장하지 않는 비밀스러운 정보를 어디에 저장한단 말인가. DotEnvDoppler 가 좋은 사용성을 제공하고 있지만, 아무리 비용을 고려하지 않더라도 회사 외부의 어딘가에 회사의 가장 비밀스러운 정보를 저장하는 것은 내키지 않는 일이다.

이 부류에서 best practice를 제공하는 서비스는 HashiCorp Vault이지만 이것은 위 서비스들과 달리 스크립팅할 때는 쓸만할 지 몰라도 극악의 UI와 Outdate된 문서로 쉽게 사용하기가 어렵다.

그럼 지금 당장 이것을 해결할 수 있는 방법은 무엇일까.

만약 나 혼자 이 문제로 고통받고 있다면 이 가이드가 도움이 될 것이다.

CNCF SOPS - https://github.com/getsops/sops

SOPS는 패스워드를 다른 곳에 보관하는 대신, 안전하게 Git Repository 안에 저장하는 방안을 제시한다. 간결한 Walk-through는 이 동영상을 참조:
https://www.youtube.com/watch?v=V2PRhxphH2w

외부 Key Management 툴과도 연동이 가능하지만, 소수의 개발자가, 혹은 외부 의존성 없이 사용하기 위해서는 개인별로 만들어진 AGE key를 이용하여 SOPS를 사용하기를 권장한다.

SOPS 설치

SOPS 설치: https://github.com/getsops/sops/releases 나 brew 사용

brew install sops

AGE 설치

AGE는 단순하고 모던한 key generation 툴로, 편의를 위해 가능하면 GPG대신 AGE를 이용하도록 추천하고 있다. 대부분의 환경에서 한 줄의 명령으로 설치가 가능하다.

Mac의 경우 간단히 brew를 사용한다

brew install age

그리고 home directory 아래에 .sops이라는 디렉토리를 만들어 key 파일을 저장한다

mkdir ~/.sops
age-keygen -o ~/.sops/key.txt

그러면 ~/.sops/key.txt에 public key와 private key가 모두 저장된다.
이 파일의 위치는 환경변수로 sops에 연동할 수 있으므로 .zshrc등에 지정해 둔다

(.zshrc)
...
# for SOPS
export SOPS_AGE_KEY_FILE=$HOME/.sops/key.txt
...

그리고 프로젝트 루트에 creation rule을 설정해 준다.

(.sops.yaml)
creation_rules:
  - path_regex: "^*.secret.(json|yaml)$"
    encrypted_regex: ^(password)$
    age: >-
      age16xt0kgspydq8w9l7es9vth8clrav4ajf2zz0s2wjk0ge4skdf9zqeza72x,
      age1ktahy6cwtxhmnr5wslw3c7semspwrzgn3h2xlj5fs7ctvssmtqks86snz5

path_regex

SOPS를 이용하여 자동으로 암호화/복호화 할 파일경로의 정규식 규칙

encrypted_regex

SOPS를 이용하여 자동으로 암호화/복호화 할 필드(key)명의 정규식 규칙

age

age를 사용하여 생성한 public key의 목록. 여러 사람이 age로 각자 생성한 public key를 미리 기재하면 된다

혹시라도 수동으로 파일을 암호화, 복호화 하고 싶을 때는 다음과 같이 활용한다

(Encryption)
sops -e --age $(cat $SOPS_AGE_KEY_FILE|grep -o "age.*") -i target.secret.yaml
(Decryption)
sops -d --age $(cat $SOPS_AGE_KEY_FILE|grep -o "age.*") -i target.secret.yaml

이 경우에 target.secret.yaml 파일도 .sops.yaml에 정의된 path_regex에 해당되어야 한다

VSCode Plugin 사용

다음의 VSCode Plugin을 설치한다.
https://marketplace.visualstudio.com/items?itemName=signageos.signageos-vscode-sops

그리고 plugin preference를 열어

  • Sops: Creation Enabled
  • Sops: Enabled

를 체크해 주면 바로 연동이 시작된다.

사용하는 법은 간단하다. abc.secret.yaml이라는 ".secret"라는 패턴이 들어간 yaml파일을 만들면, vscode에서 편집할 때 자동으로 sops로 암호화를 하여 저장한다. 그리고 복호화된 정보는 .decrypted~abc.secret.yaml이라는 파일을 임시로 만들어 보여준다. (이는 .sops.yaml의 정의에 따르며 변경이 가능하다)

혹시 모르니 .gitignore에 다음 라인을 추가해주는 것이 좋다.

...
*.decrypted~*
...

그러면 .secret.(yaml|json) 파일을 편집하려고 열 때마다 자동을 복호화하여 편집할 수 있다.

Node.js project에서 사용

Node.js 프로젝트에서 사용하기 위해서는 이 파일들이 반드시 복호화 되어있어야 한다. 왜냐하면 암호화 되는 순간 원래 파일 형식을 벗어난 json형태로 무조건 바뀌게 되고, 파일 구조도 완전히 달라져서 어차피 정상적인 사용이 불가능하기 때문이다. 개인적으로 이를 편리하게 하기 위해 다음과 같이 기대하였다.

(package.json)
...
"scripts": {
  ...
  "start": "npm run decrypt && node ./src-ssr/index",
  "encrypt": "(for f in ./src-ssr/config/*.secret.yaml; do sops -e --age $(cat $SOPS_AGE_KEY_FILE|grep -o \"age.*\") -i $f && sops updatekeys -y $f; done) || true",
  "decrypt": "(for f in ./src-ssr/config/*.secret.yaml; do sops -d --age $(cat $SOPS_AGE_KEY_FILE|grep -o \"age.*\") -i $f; done) || true",
  ...
}
...

loop를 넣은 이유는 sops cli가 wildcard 패턴에 대한 반복을 지원하지 않아서이다. 따라서 wildcard 패턴을 넣으면 딱 1개의 파일만 적용되고 끝난다. 관련해서 이슈가 등록되어 있다.
그리고 이미 복호화되어 있어 명령이 실패할 때를 위해 || true 연산을 추가했다.

주의할 점은 encrypt할 때는 주어지 pubkey만으로 암호화를 한다는 점이다. (위 예제에서는 $(cat $SOPS_AGE_KEY_FILE|grep -o "age.*") 의 결과값). 먼저 만들어 놓은 .sops.yaml에 등록한 모든 pubkey로 복호화 가능하게 하기 위해서는 update를 한번 해 주어야 한다.

sops updatekeys -y <filename>

|| true 는 encrypt를 중복으로 여러 번 실행하거나 decrypt를 중복으로 여러 번 실행하더라도 오류로 프로세스가 중단되지 않도록 하는 역할을 한다.

사람마다 활용법에 차이가 있을 수 있겠지만, 나는 docker container를 빌드하는 경우는 key와 sops를 포함시켜 내부에서 보호화하는 것 보다 복호화한 후 container안에 넣는 방향을 선택했다. 가장 이상적인 방법은 애플리케이션이 암호화된 json이나 yaml파일을 직접 복호화한 후 사용할 수 있도록 연동하는 것이 좋을 것이라 생각한다.

속지말자

  • Github Secret: Github Action에서만 읽기가 가능하다. 로컬 환경에서는 원본을 알 수 없다.
  • Teller: Command line에서 다른 credential storage와 연동할 수 있는 툴. 읽기가 되는 서비스가 어디가 있는지 꼭 확인하자.

기대중

  • Infisical: Self-hosting가능한 Doppler라고 볼 수 있다. Backend를 Hashicorp Vault로 사용하면 정말 좋을 것 같아 Issue를 넣었고, 팀의 방향성에도 잘 맞는 것 같아 기대중

참고

더 친절히 써 주신 다른 분의 글 링크:
https://heojay.dev/dev/sops/
SOPS와 AGE를 이용한 자세한 가이드의 링크:
https://docs.technotim.live/posts/secret-encryption-sops/

profile
Developer exploring 21c

0개의 댓글