GitLab CI/CD에서 SSH 키 사용하기
GitLab CI/CD에서 SSH 키 사용하기
GitLab은 빌드 환경(즉, GitLab Runner가 실행되는 곳)에서 SSH 키를 관리하는 내장 기능을 제공하지 않아요. 그래서 필요한 순간에 SSH 키를 빌드 환경에 주입해서 쓰는 방법을 직접 구성해야 해요. 이 페이지에서는 SSH 키를 만들고 CI/CD 잡에서 사용하는 방법을 안내해요.
출처: 문서
본문
- Tier: Free, Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
SSH 키는 다음과 같은 상황에서 사용해요.
- 내부 서브모듈을 체크아웃할 때.
- 패키지 매니저로 비공개 패키지를 다운로드할 때. 예를 들어 Bundler요.
- 애플리케이션을 자신의 서버나 Heroku 같은 곳에 배포할 때.
- 빌드 환경에서 원격 서버로 SSH 명령을 실행할 때.
- 빌드 환경에서 원격 서버로 Rsync로 파일을 보낼 때.
가장 널리 지원되는 방법은 .gitlab-ci.yml을 확장해서 SSH 키를 빌드 환경에 주입하는 거예요. 이 방식은 Docker나 shell 같은 모든 종류의 executor에서 동작해요.
CI/CD에서 SSH 키를 쓸 때는 개인 키를 안전하게 저장하고, 자동화된 잡에 개인 SSH 키를 재사용하지 마세요. 무단 접근 위험을 줄이려면 키를 주기적으로 교체하세요.
SSH 키 생성하고 사용하기
GitLab CI/CD에서 SSH 키를 만들고 사용하는 방법은 이래요.
- 새 SSH 키 쌍을 생성해요.
- 개인 키를
SSH_PRIVATE_KEY라는 이름의 파일 유형 CI/CD 변수로 추가해요. - 잡에서 ssh-agent를 실행해서 개인 키를 로드해요.
- 공개 키를 접근하려는 서버에 복사해요(보통
~/.ssh/authorized_keys). 비공개 GitLab 저장소에 접근한다면 공개 키를 배포 키로도 추가해야 해요.
아래 예시에서 ssh-add - 명령은 $SSH_PRIVATE_KEY 값을 잡 로그에 표시하지 않아요. 다만 디버그 로깅을 활성화하면 노출될 수 있으니 주의해야 해요. 파이프라인 가시성도 함께 확인해보는 게 좋아요.
SSH 키를 파일 유형 변수로 추가하기
프로젝트에 SSH 키를 추가하려면 키를 파일 유형 CI/CD 변수로 추가해요.
- Visibility를 Visible로 설정해요. SSH 키에는 공백 문자가 포함되어 있는데, Masked 또는 Masked and hidden 변수는 공백 문자를 포함할 수 없기 때문에 가시성이 Visible이어야 해요. SSH 키는 잡 로그에 나타나면 마스킹되지 않으므로 변수에 대해
cat이나tee같은 명령을 실행하면 안 돼요. - Key 텍스트 상자에 변수 이름을 입력해요. 예를 들어
SSH_PRIVATE_KEY요. - Value 텍스트 상자에 개인 키 내용을 붙여넣어요. 값은 반드시 개행 문자(
LF)로 끝나야 해요. 저장하기 전에 마지막 줄 끝에서 Enter 또는 Return을 눌러 개행을 추가해요.
SSH 키를 일반 변수로 추가하기
파일 유형 CI/CD 변수를 쓰고 싶지 않다면 예시 SSH 프로젝트를 참고해요. 이 방법은 파일 유형 변수 대신 일반 CI/CD 변수를 사용해요. 일반적으로 파일 유형 변수가 여러 줄 형식을 보존하고 형식 관련 오류 위험을 줄여주므로 더 선호돼요.
Docker executor에서 SSH 키 사용하기
CI/CD 잡이 Docker 컨테이너에서 실행되면 환경이 격리돼 있어요. 비공개 서버에 코드를 배포하려면 SSH 키 쌍을 사용할 수 있어요.
- 새 SSH 키 쌍을 생성해요. SSH 키에 패스프레이즈를 추가하지 마세요. 추가하면
before_script가 패스프레이즈를 물어봐요. - 개인 키를
SSH_PRIVATE_KEY라는 이름의 파일 유형 CI/CD 변수로 추가해요. before_script동작으로.gitlab-ci.yml을 수정해요. 아래 예시는 Debian 기반 이미지와 패키지 설치 권한이 있는 컨테이너에서 잡이 실행된다고 가정해요. before_script는 기본값으로 설정하거나 잡별로 설정할 수 있어요.
before_script:
##
## Install ssh-agent if not already installed, it is required by Docker.
## (change apt-get to yum if you use an RPM-based image)
##
- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'
##
## Run ssh-agent (inside the build environment)
##
- eval $(ssh-agent -s)
##
## Give the right permissions, otherwise ssh-add will refuse to add files
## Add the SSH key stored in SSH_PRIVATE_KEY file type CI/CD variable to the agent store
##
- chmod 400 "$SSH_PRIVATE_KEY"
- ssh-add "$SSH_PRIVATE_KEY"
##
## Create the SSH directory and give it the right permissions
##
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
##
## Optionally, if you use Git commands, set the user name and email.
##
# - git config --global user.email "[email protected]"
# - git config --global user.name "User name"
- 비공개 서버의 SSH 호스트 키가 검증되는지 확인해요.
- 마지막으로 첫 단계에서 만든 키의 공개 키를, 빌드 환경 안에서 접근하고 싶은 서비스에 추가해요. 비공개 GitLab 저장소에 접근한다면 배포 키로 추가해야 해요.
이제 빌드 환경에서 비공개 서버나 저장소에 접근할 수 있어요.
Shell executor에서 SSH 키 사용하기
Docker가 아닌 Shell executor를 사용한다면 SSH 키 설정이 더 쉬워요.
GitLab Runner가 설치된 머신에서 SSH 키를 생성하고, 그 키를 이 머신에서 실행되는 모든 프로젝트에 사용할 수 있어요.
- 먼저 잡을 실행하는 서버에 로그인해요.
- 그다음 터미널에서
gitlab-runner사용자로 로그인해요.
sudo su - gitlab-runner
- 새 SSH 키 쌍을 생성해요. SSH 키에 패스프레이즈를 추가하지 마세요. 추가하면
before_script가 물어봐요. - 마지막으로 앞서 만든 키의 공개 키를 빌드 환경 안에서 접근하고 싶은 서비스에 추가해요. 비공개 GitLab 저장소에 접근한다면 배포 키로 추가해야 해요.
키를 생성한 뒤 원격 서버에 로그인해서 지문(fingerprint)을 수락해보세요.
ssh example.com
GitLab.com의 저장소에 접근하려면 [email protected]을 사용하면 돼요.
SSH 호스트 키 검증하기
비공개 서버의 공개 키를 확인해서 중간자 공격(man-in-the-middle)의 대상이 되지 않는지 확인하는 건 좋은 습관이에요. 이상한 일이 생기면 잡이 실패해서(공개 키가 일치하지 않으면 SSH 연결이 실패) 알아차릴 수 있어요.
서버의 호스트 키를 알아내려면 신뢰할 수 있는 네트워크(가급적이면 비공개 서버 자체)에서 ssh-keyscan 명령을 실행해요.
## Use the domain name
ssh-keyscan example.com
## Or use an IP
ssh-keyscan 10.0.2.2
호스트를 파일 유형 CI/CD 변수로 프로젝트에 추가해요. 다만 다음은 달라요.
- Key로
SSH_KNOWN_HOSTS를 사용해요. - Value로
ssh-keyscan의 출력을 사용해요.
여러 서버에 연결해야 한다면 모든 서버의 호스트 키를 변수의 Value에 모아둬야 해요. 줄마다 키 하나씩이에요.
.gitlab-ci.yml안에서 바로 ssh-keyscan을 쓰는 대신 파일 유형 CI/CD 변수를 사용하면, 어떤 이유로 호스트 도메인 이름이 바뀌어도.gitlab-ci.yml을 바꿀 필요가 없어요. 게다가 값이 미리 정의되어 있어서 호스트 키가 갑자기 바뀌면 CI/CD 잡이 실패하므로 서버나 네트워크에 문제가 있다는 걸 알 수 있어요. ssh-keyscan을 CI/CD 잡에서 직접 실행하지 마세요. 중간자 공격에 취약한 보안 위험이에요.
SSH_KNOWN_HOSTS 변수를 만들었다면, .gitlab-ci.yml의 내용에 다음을 추가해야 해요.
before_script:
##
## Assuming you created the SSH_KNOWN_HOSTS file type CI/CD variable:
##
- cp "$SSH_KNOWN_HOSTS" ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
문제 해결 (Troubleshooting)
오류: ... error in libcrypto
CI/CD 잡에서 SSH 키를 로드할 때 다음 오류가 발생할 수 있어요.
Error loading key "/builds/path/SSH_PRIVATE_KEY": error in libcrypto
이 문제는 SSH 키 값이 개행 문자(LF)로 끝나지 않을 때 발생할 수 있어요.
이 문제를 해결하려면 파일 유형 CI/CD 변수를 편집하고, 변수를 저장하기 전에 SSH 키의 -----END OPENSSH PRIVATE KEY----- 줄 끝에서 Enter 또는 Return을 눌러 개행을 추가해요.
오류: ... value cannot contain...
SSH 키를 CI/CD 변수로 저장할 때 다음 오류가 발생할 수 있어요.
Unable to create masked variable because: The value cannot contain the
following characters: whitespace characters.
이 문제는 변수의 Visibility가 Masked 또는 Masked and hidden으로 설정되어 있을 때 발생해요. 마스킹된 변수는 공백이 없는 단일 줄이어야 하는데, SSH 키는 마스킹과 호환되지 않는 공백 문자를 포함하고 있어요.
이 문제를 해결하려면 SSH 키를 파일 유형 변수로 추가할 때 Visibility를 Visible로 설정해요. 파일 유형 변수는 잡 로그에 노출되지 않아 키 값에 추가 보호 계층을 제공해요.
더 알아보기
SSH 키 관리를 더 체계적으로 하려면 사용자 SSH 키 문서에서 로컬 키 생성부터 배포 키까지 전체 흐름을 익혀두는 게 좋아요. 또 서브모듈 사용과 결합하면 비공개 저장소를 서브모듈로 가져올 때도 같은 키를 활용할 수 있어요.