Plesk를 쓴 이후로 가장 스트레스를 받았던 것은 SSL 인증서 갱신이 실패하는 것이었다. Let’s Encrypt는 3개월만 인증이 유효하기 때문에, 2개월이 지나면 만료 전까지 하루에 한 번 자동 갱신을 시도한다. 이때 ACME 프로토콜을 사용하여 도메인의 유효성을 검사한다. 하지만, 매번 위와 같은 오류가 발생해서 갱신이 실패했다. Plesk의 Let’s Encrypt 플러그인은 Plesk DNS만 업데이트를 하는데, Lightsail DNS를 사용하니 DNS 업데이트가 안되어 인증이 되지 않기 때문이었다.
그래서, Lightsail 서비스에 접속해서 _acme-challenge.junklab.net의 TXT 레코드 값을 직접 바꿔주고 재로드를 일일이 눌러야만 했다. Route53는 Plesk 플러그인이 있어 Plesk의 DNS를 자동으로 내보낼 수 있지만, Lightsail은 아직 그런 플러그인이 없어 수동으로 해야한다.
1. _acme-challenge가 DNS에 등록이 되어야 하는 이유
참조 : https://letsencrypt.org/ko/docs/challenge-types/
와일드 카드 인증서를 사용하기 때문이다. junklab.net 외에 izone.junklab.net 같은 하위 도메인까지 한번에 인증을 받는 것을 와일드 카드 인증이라고 한다. 이게 아니면 각각의 도메인마다 인증서를 받아야 하는데, 인증서 각각 받는 것이 귀찮다고 와일드 카드 로 받았다가 더 귀찮음이 생겨버린 셈이다. 와일드 카드 인증은 DNS-01 챌린지로만 증명이 가능하며, DNS-01 챌린지는 _acme-challenge 라는 TXT 레코드를 불러오기 때문에 DNS 업데이트 API가 제공되는 경우에만 권장된다.
반대로 각각의 도메인에 인증서를 받는 것은 HTTP-01 챌린지라고 하며, 이때는 ACME 값이 http://<YOUR_DOMAIN>/.well-known/acme-challenge/ 에 저장이 된다. plesk처럼 자동 갱신이 되는 구조에서는 이것이 훨씬 간편하다. cPanel로 관리되는 웹호스팅에서도 당연히 이 방식으로 SSL 인증서 발급이 이뤄진다.
하위 도메인이 너무 많지 않다면, 단일 인증서를 받는 것을 추천한다. 와일드 카드를 쓰고 있더라도, 단일 인증서로 변경하기를 추천한다. 그래도 와일드 카드를 쓰고 싶다면, 아래 내용을 참조하길 바란다.
2. AWS CLI 설치
혹시 Lightsail에서 웹 접속을 하지 않고도 DNS 관리가 가능한지를 찾아봤다. AWS CLI로 각종 Lightsail 서비스를 수정/추가할 수 있었다. 일단, 아래 포스팅을 참조해서 일단 AWS CLI를 설치한다.
3. 새로운 acme 토큰 가져오기
Plesk에서 Let’s encrypt가 갱신을 시도하면, Plesk의 DNS 테이블에는 해당 값이 자동으로 업데이트가 된다. 이 DNS 테이블에서 acme 토큰을 가져온다.
DNS 정보를 불러오는 명령어는 plesk bin dns
이다. 아래와 같이 사용하면 현재 Plesk DNS 값이 모두 표시된다. Lightsail의 웹터미널에서 사용하는 ubuntu 계정에선 해당 명령어 권한이 없으므로 sudo
를 앞에 붙여줄 필요가 있다.
sudo plesk bin dns --info junklab.net
이 중에서 필요한 것은 acme 토큰이기 때문에, 해당 값만 빼온다. Plesk 관리 화면에 표기된 TXT 값과 동일한지 확인하자.
ubuntu@ip-172-26-0-112:~$ sudo plesk bin dns --info junklab.net | awk -F" " '/_acme/ {print $3}' 1_l0ht4LFjND46Coz2Ebnp4MgYRjhZVY2n9NtTAwd-w
4. AWS CLI로 Lightsail의 DNS 값 가져오기
AWS CLI가 제대로 설치가 되었다면, 터미널에서 도메인 정보를 잘 가져오는지 확인해봐야 한다. 인스턴스의 위치와 상관 없이 도메인 관련 명령어는 region을 us-east-1
로 설정해야 한다. output은 가공하기 좋게 text
로 설정했다.
aws lightsail get-domain --domain-name 'junklab.net' --region 'us-east-1' --output 'text'
위의 많은 정보 중에 필요한 것은 _acme-challenge의 ID 값이다. 숫자로만 구성된 두번째 열의 값이 Domain Entry의 ID이다. 토큰을 빼올 때와 마찬가지로 awk를 이용해서 ID 값을 추출했다.
ubuntu@ip-172-26-0-112:~$ aws lightsail get-domain --domain-name 'junklab.net' --region 'us-east-1' --output 'text' | awk -F" " '/_acme/ {print $2}' -1609989094
5. AWS CLI로 DNS 업데이트하기
Doman Entry의 ID와 변경할 토큰을 알고 있으니, 이제 업데이트만 하면 된다. 그런데, entry 값 입력이 잘 안되었다. 토큰은 ” 사이에 들어가 있어야 한다는 메시지가 계속 나오는데, 온갖 방식의 따옴표와 entry 구조를 다 해보다가 겨우 되는 조합을 찾았다. domain-entry는 ‘로 감싸고, 구조는 JSON 형식을 사용한다. 각각의 element는 “로 감싸고, 토큰은 \”로 한번 더 감싼다.
aws lightsail update-domain-entry --domain-name 'junklab.net' --region 'us-east-1' --output 'text' --domain-entry '{"id":"-1609989094","name":"_acme-challenge.junklab.net","target":"\"1_l0ht4LFjND46Coz2Ebnp4MgYRjhZVY2n9NtTAwd-w\"","isAlias":false,"type":"TXT"}'
위에서 얻은 토큰과 ID를 이용해서 명령어를 보내면, 원래 _acme-challenge가 지워지고, 새로운 토큰으로 domain entry가 생성된다.
ubuntu@ip-172-26-0-112:~$ aws lightsail update-domain-entry --domain-name 'junklab.net' --region 'us-east-1' --output 'text' --domain-entry '{"id":"-1609989094","name":"_acme-challenge.junklab.net","target":"\"1_l0ht4LFjND46Coz2Ebnp4MgYRjhZVY2n9NtTAwd-w\"","isAlias":false,"type":"TXT"}' OPERATIONS 2020-07-21T22:16:14.311000+09:00 d2a9fbb0-32f0-4f5f-9522-b712f8d85f7f True DeleteDomainEntry junklab.net Domain Succeeded 2020-07-21T22:16:14.311000+09:00 LOCATION all global OPERATIONS 2020-07-21T22:16:14.336000+09:00 61c96a93-4506-41a6-abd9-ddac889c6180 True CreateDomainEntry junklab.net Domain Succeeded 2020-07-21T22:16:14.336000+09:00 LOCATION all global
제대로 반영이 되었는지 도메인 정보를 다시 읽어본다. Domain Entry가 새로 생성되었기 때문에 ID도 바뀐다.
ubuntu@ip-172-26-0-112:~$ aws lightsail get-domain --domain-name junklab.net --region us-east-1 --output text | grep acme DOMAINENTRIES 1182715707 False _acme-challenge.junklab.net "1_l0ht4LFjND46Coz2Ebnp4MgYRjhZVY2n9NtTAwd-w" TXT
도메인 정보가 갱신되었기 때문에 다음 번 Let’s Encrypt가 구동될 때 인증서도 갱신이 된다. 유효 만료일이 10월로 늘어나 있음을 알 수 있다. 이제 이 모든 과정을 script로 만들어서 cron에 올려볼 차례이다. 글이 너무 길어져서 다음 포스팅으로 넘긴다.