Jenkins Publish over SSH 인증시 BapPublisherException 오류 원인과 해결책

Jenkins Publish over SSH 플러그인을 제대로 설치도 하고 서버에 공개키 세팅해 주고, Jenkins에 개인키 설정까지 다 마쳤는데, 인증이 언젠가부터 안되는 분들 계시죠? Jenkins에서 BapPublisherException 발견하셨나요? 저도 원인 파악하느라 고생을 했는데, 공유합니다.

Test Configuration 실행하면 BapPublisherException 예외발생

혹시 아래와 같은 명령어로 인증서 만드셨나요? 그런데 예외가 발생하지는 않으셨나요?

만약에 오류를 만나지 않으셨다면, 아마 사용하는 서버의 OpenSSH 버전이 8.8보다 낮을 가능성이 있습니다.

ssh-keygen -t rsa -b 4096 -m PEM
ShellScript

분명 SHA256까지 적용이 됐는데 말이죠. 심지어 키 크기까지 4096bit로 잡아줬는데, Test Configuration 눌렀는데 아래와 같은 오류 메시지가 나왔나요?

jenkins.plugins.publish_over.BapPublisherException: Failed to connect and initialize SSH connection. Message: [Failed to connect session for config [My Server]. Message [Auth fail]]

일단 생각하시는대로 키는 제대로 생성된 게 맞습니다. 그런데 왜 안돼냐 말이죠! 그건 Jsch 때문인데, 자세한 내용 살펴보겠습니다.

OpenSSH 8.8 SHA-1 미지원 결정

우선 OpenSSH 8.8부터 SHA-1 해시 알고리즘을 사용하는 RSA 시그니처를 지원하지 않기로 결정했습니다. SHA-1은 해시 충돌이 발생할 수 있습니다. 암호학적으로 부서졌다는 거죠. 한마디로 요약하면 보안상 안전하지 않으니, 앞으로는 허용하지 않겠다는 거겠죠.

그런데 Publish over SSH는 JCraft에서 만든 Jsch라는 라이브러리를 사용하여 개발했습니다. 문제는 Jsch 라이브러리가 최신 기술들을 반영하고 있지 않다는 겁니다. rsa-sha2-256이나 rsa-sha2-512를 사용할 수도 없고, ed25519도 지원하지 않습니다. 그래서 Publish over SSH를 사용해서 연결을 테스트 하면 서버에 아래와 같은 로그가 찍히며 인증이 되지 않습니다. 맞습니다. OpenSSH에서 거부하는 장면입니다. 인증키를 SHA-2인 SHA256으로 생성해도 SHA-1으로 처리되는 순간입니다.

Dec 19 16:32:51 instance-20210904-0100 sshd[148076]: userauth_pubkey: key type ssh-rsa not in PubkeyAcceptedAlgorithms [preauth]

해결책: ECDSA 사용하기

OpenSSH 8.8 배포 설명에서는 ecdsa나 ed25519를 대신 사용하라고 권장하고 있습니다. 다행이 Jsch에서 ecdsa를 지원합니다. 아래와 같이 개인키/공개키 쌍을 생성해서 사용하면 제대로 인증이 되는 것을 확인할 수 있습니다.

ssh-keygen -t ecdsa -b 521 -m PEM
ShellScript

일단 당분간 큰 문제가 없는 한 ecdsa를 이용하면 되지 않을까 싶습니다. Publish over SSH 깃헙에도 해당 이슈가 아직 열려있습니다. 관심 있는 분들의 기여가 있다면 또 개선이 되지 않을까 싶습니다.

문제 해결에 도움이 된 링크 공유

OpenSSH 8.8 릴리즈 문서Github Publish over SSH의 인증 오류 관련 이슈를 참고했습니다.

같이 읽으면 좋은 글

Jenkins ssh key 설정 방법(Credentials로 git 연동) 2가지

Jenkins에서 특정 프로젝트의 git 저장소 활용은 빼놓을 수 없습니다. 아무에게나 git 저장소를 개방할 수는 없으며, 허가받은 사용자에게만 인증처리를 해주어야 합니다. 그럼 프로젝트에서 git 연동을 위한 인증 처리 방법으로 Jenkins SSH Key 설정 방법을 살펴보겠습니다.

프로젝트에서 Git 연동(feat. Jenkins SSH Key 설정)

앞서 말씀드렸듯이 Jenkins에서 git repository를 설정하는 경우 Credentials를 통해서 인증처리를 해 줘야 합니다. Jenkins가 해당 리포지터리를 접근하게 되면 Git Server(Github, Gitlab, Bitbucket 등)는 허가받은 사용자인지 확인한 후 접근을 허용합니다. 이를 위해서 여기에서는 Jenkins SSH Key 설정을 통해서 접근할 것입니다.

Jenkins에서 Repository URL을 설정해 주면 아래와 같이 원격 저장소를 읽어올 수 없다고 나옵니다. 인증된 SSH키가 등록되어 있지 않으니 당연한 결과겠죠.

그림 1. Jenkins 프로젝트에서 git 연동하기: 인증처리 아직 안 됨
그림 1. Jenkins 프로젝트에서 git 연동하기: 인증처리 아직 안 됨

Git 연동: 저장소 인증 방법 2가지

Jenkins 프로젝트에서 Git 저장소에 인증하는 것은 두 가지 방법을 사용해서 문제를 해결할 수 있습니다.

Jenkins 사용자의 home 디렉토리 활용

첫번째는 ~/.ssh에 공개키와 개인키를 생성하고, Git 서버에 공개키를 등록해 주는 방법입니다. 이 경우에는 Credentials를 별도로 생성할 필요가 없으며, ~/.ssh 하위에 있는 Jenkins 계정의 ssh 키로 인증하는 방법입니다.

별도의 개인키/공개키 쌍 활용

두번째는 이미 가지고 있는 개인키, 공개키 쌍이 있는 경우이고 jenkins의 개인키와 공개키를 별도의 용도로 사용하고 있고, 키를 함께 사용하기 원하지 않는 경우라면, Credentials에 개인키를 등록해 주고, Git 서버에 공개키를 등록해 주는 방법입니다.

어떤 방법이든 SSH 공개키(public key)와 비밀키(private key) 쌍을 필요로 합니다. 이미 공개키와 개인키 쌍이 준비된 경우에는 아래 과정을 생략합니다.

SSH 공개키와 비밀키 생성하기

Docker에서 Jenkins를 이용하는 경우로 예를 들어 설명하겠습니다. 우선 Docker 컨테이너의 shell로 접근하고, ssh-keygen 명령어를 실행해 줍니다.

docker exec -it jenkins-server /bin/sh
~ $ ssh-keygen
ShellScript

원하는 파일 경로가 있다면 입력해 주시고, 그렇지 않으면 넘어갑니다. 기본적으로 개인키와 공개키는 각각 ~/.ssh/id_rsa와 id_rsa.pub 파일로 생성됩니다. 키 쌍에 비밀번호를 입력해 주면 되며, 그냥 엔터를 치면 비밀번호 없이 만들어집니다. 그러면 핑거프린트를 알려주고, RSA 3072 bit 키를 SHA256을 사용해서 만들었다고 알려줍니다.

그림 2. ssh-keygen 사용하여 공개키/개인키 쌍 생성
그림 2. ssh-keygen 사용하여 공개키/개인키 쌍 생성

위의 경우 jenkins의 홈 디렉토리가 /var/jenkins_home이기 때문에 /var/jenkins_home/.ssh 하위에 개인키 id_rsa와 공개키 id_rsa.pub이 만들어집니다.

ls -la ~/.ssh
ShellScript

ls 명령어로 만들어진 파일을 확인합니다.

그림 3. 만들어진 RSA 개인키/공개키 쌍
그림 3. 만들어진 RSA 개인키/공개키 쌍

방금 생성한 키를 그냥 사용하실 거라면 Credentials에 별도로 등록하지 않고 마지막 단계 Git 저장소를 알려진 호스트로 등록하기로 넘어가시면 됩니다.

Jenkins에 Credentials 등록

Jenkins에 Credentials를 등록할 때에는 SSH 개인키가 필요합니다. 위에서 생성한 파일 중 id_rsa입니다. 홈디렉토리로 이동하고 .ssh로 이동해서 id_rsa의 내용을 열람한 후 복사해 둡니다.

cd ~/.ssh
cat id_rsa
ShellScript

이제 Jenkins에서 Credentials를 등록할 차례입니다. “Jenkins 관리”에서 “Manage Credentials”로 들어갑니다.

그림 4. Manage Credentials 메뉴
그림 4. Manage Credentials 메뉴

이제 Stores scoped to Jenkins에서 Domains의 (global)을 눌러주면 Add credentials가 나옵니다. 눌러줍니다.

그림 5. Jenkins에 Credentials 추가하기
그림 5. Jenkins에 Credentials 추가하기

이제 Credentials를 추가하기 위한 입력 폼이 아래와 같이 뜹니다. 우선 Scope는 SSH Username with private key를 선택합니다. ID는 비워두면 UUID가 생성되고, 원하는 ID를 직접 입력해도 됩니다. Username에 식별하기 위한 이름을 적어줍니다. 그리고 Private Key가 보이죠. Add 버튼을 누르고 위에서 복사한 Private Key의 내용을 붙여넣습니다. passphrase가 있다면 입력해 준 후, Create 버튼을 눌러줍니다.

그림 6. Jenkins SSH Key 설정: 새로운 Credentials 추가하기
그림 6. Jenkins SSH Key 설정: 새로운 Credentials 추가하기

아래와 같이 생성된 Credentials를 확인할 수 있습니다.

그림 7. Global credentials에 추가된 Credentials 확인
그림 7. Global credentials에 추가된 Credentials 확인

Private Key와 Public Key는 쌍이죠? 이제 공개키를 Github, GitLab, BitBucket 등의 리포지터리 서버에 등록해 주셔야 합니다. 서비스 마다 약간의 차이가 있겠지만, 사용자 설정으로 가시면 SSH Key를 등록할 수 있는 곳이 있습니다. 그곳에 여러분이 생성한 공개키를 추가해 주시면 됩니다.

Project에서 Git Repository 설정하기

리포지터리 주소를 입력해 주고, Credentials를 선택해 줍니다. 만약 Git Server에 공개키를 등록하지 않았다면 아래와 같은 화면을 보게 됩니다. Permission denied (publickey) 보이시죠? 그러니 서버에 공개키 꼭 등록해 주셔야 합니다.

그림 8. Git 연동에서 Git 서버에 공개키 등록하지 않은 경우 발생하는 오류
그림 8. Git 연동에서 Git 서버에 공개키 등록하지 않은 경우 발생하는 오류

서버에 공개키가 제대로 등록되었는데도, 아래와 같은 화면을 볼 수도 있습니다. 이게 무슨 일일까요? 아래와 같은 메시지가 나오는 분 계신가요?

그림 9. Jenkins 사용자의 known_hosts에 등록되어있지 않은 경우 발생하는 오류
그림 9. Jenkins 사용자의 known_hosts에 등록되어있지 않은 경우 발생하는 오류

아니, 이게 대체 무슨 일일까요? Credentials도 제대로 추가하고 선택해 주었고, Git 서버에 공개키도 정상적으로 업로드 했는데, 왜 이러는 걸까요. 여기에서 해 줄 일이 하나 더 있습니다.

Git 저장소를 알려진 호스트로 등록하기

위의 오류 메시지 “stderr: No RSA host key is known for bitbucket.org and you have requested strict checking. Host key verification failed. fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.”를 보면 bitbucket.org에 대한 알려진 RSA Host Key가 없다고 합니다. 그리고 접속 권한과 리포지터리의 존재를 분명히 해 주라고 안내해 주고 있습니다. 이 경우에는 위에 있는 command를 shell에서 입력해 줍니다. 그리고 yes를 입력해 줍니다. git@ 뒷부분은 여러분의 git 리포지터리 주소를 적으시면 됩니다. Are you sure you want to continue connecting? 이라는 질문에서 yes를 입력하시면 됩니다.

그림 10. ~/.ssh/known_hosts에 Git 저장소 추가
그림 10. ~/.ssh/known_hosts에 Git 저장소 추가

이제 bitbucket.org는 jenkins가 알고있는 호스트가 되었습니다. ~/.ssh/known_hosts에 해당 내용이 추가됐습니다.

이제 Jenkins 프로젝트 설정에 다시 들어가 보시면 어떤 오류 알림도 없는 것을 확인하실 수 있습니다. 젠킨스 SSH Key 설정을 통한 Git 연동이 성공적으로 끝난 겁니다. 축하드립니다.

Jenkins 프로젝트에서 Git 인증 설정을 처음 해 보시는 분들께 도움이 되길 바랍니다. 궁금한 것이나 오류 발생과 관련해서는 댓글 달아주시면 도와드리겠습니다.

관련 자료

Jenkins git 플러그인 공식 문서를 참고하시면 도움이 됩니다.

같이 읽으면 좋은 글

(certbot) LetsEncrypt 인증서 삭제하는 2가지 방법

LetsEncrypt certbot을 사용해서 인증서를 발행받았는데, 사용할 필요가 없어진 경우 있죠. 없어진 LetsEncrypt 인증서 삭제 방법을 알려드리겠습니다.

LetsEncrypt 인증서 삭제(certbot 명령어 사용)

대개의 경우 불필요하면 그냥 신경쓰지 않고 내버려둘 수 있겠지만, 다양한 도메인을 관리하다보면 나중에 혼란이 생길 수 있으므로 삭제하는 편이 깔끔하겠죠. 아래 명령어를 사용하면 발급받은 인증서를 삭제할 수 있습니다.

방법 1: 명령어 입력 후 도메인 선택하기

일단 첫번째 방법은 그냥 단순히 certbot delete 라고 입력하고 엔터를 치는 방법입니다.

certbot delete
ShellScript

그러면 아래와 같이 어떤 도메인의 인증서를 삭제할 것인지 물어옵니다. 원하는 번호를 입력해 주면 해당 도메인의 인증서가 삭제됩니다.

그림 1. certbot delete 명령어로 LetsEncrypt 인증서 삭제시 인증서 선택 화면
그림 1. certbot delete 명령어로 LetsEncrypt 인증서 삭제시 인증서 선택 화면

방법 2: 명령어에 도메인 특정하기

따로 도메인을 선택하는 과정이 번거롭고, 바로 도메인을 지정하길 원하신다면, 아래와 같이 certbot delete 명령어 뒤에 –cert-name과 함께 삭제하려는 인증서의 도메인주소를 함께 적어주시면 됩니다.

certbot delete --cert-name 도메인주소
ShellScript

제대로 입력하시면 아래와 같은 최종 확인 화면을 볼 수 있습니다.

The following certificate(s) are selected for deletion:
  * test.osg.kr
Are you sure you want to delete the above certificate(s)?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y
Deleted all files relating to certificate test.osg.kr.

해당 도메인과 관련된 인증서를 정말로 삭제할 것인지 마지막으로 물어보는 물음에 Y를 눌러서 응답하면, 해당 도메인에 대한 인증서가 삭제되었다는 메시지를 확인할 수 있습니다.

관련 자료

certbot 홈페이지에 사용자 가이드에서 관련 내용을 참고하였습니다.

같이 읽으면 좋은 글