포스트

홈서버 (재)구축하기 - 3. 웹서버 구성 (1)

ddclient로 DDNS 설정, letsencrypt & certbot으로 SSL 인증서 발급 및 자동 갱신 설정하기

홈서버 (재)구축하기 - 3. 웹서버 구성 (1)

Cloudflare의 Global API Key 받기

오늘 다룰 두 서비스는 DNS에 레코드를 추가, 변경하는 작업을 자동화하므로 DNS의 API 키가 필요하다. 필자가 사용하고 있는 Cloudflare를 기준으로 작성한다.

  1. 오른쪽 상단 My Profile에 들어간 후 API Tokens 선택
    Cloudflare의 오른쪽 상단 메뉴 My Profile 메뉴

  2. Global API Key의 View 버튼 클릭 및 키 복사 Global API Key 화면 API Key는 노출되지 않도록 주의한다.

DDNS 설정 - ddclient

홈서버는 가정용 인터넷 망을 사용하며, 기업과 달리 서버 운용을 염두해두지 않기 때문에 IP가 고정이 아니다. 따라서 기기가 IP를 오랫동안 점유하지 않거나, 점유를 하더라도 오랜 시간이 지나면 할당한 IP를 회수하기도 한다.

내가 보유한 도메인으로 내 서버에 접속하려면 도메인 서버에 레코드를 등록하여 도메인과 IP를 연결해야한다. 위의 이유로 IP가 바뀌게 된다면 레코드에 기록된 IP와 서버의 실제 IP가 달라지므로 서버를 접속할 수 없게 된다. 이럴 때 필요한 것이 DDNS(Dynamic Domain Name Server)다.

DDNS를 설정할 수 있는 공유기 제품이 있지만, 지금 사용 중인 공유기는 그런 기능이 없어서 서버에 DDNS 클라이언트를 설치하고자 한다. ddclient는 이러한 클라이언트 중 하나로, DNS 계정(혹은 API)를 통해 DDNS를 쉽게 구성할 수 있게 해준다.

ddclient는 다양한 DNS를 지원하고 있으나 한국의 DNS는 지원하지 않는다. 필자는 Cloudflare를 사용하므로 이를 기준으로 작성한다.

설치

  1. 패키지 목록을 업데이트 후 설치
    1
    2
    
     $ sudo apt update
     $ sudo apt install ddclient
    
  2. DDNS를 제공하는 DNS 서비스 선택 Other 선택 Other를 선택한다. Cloudflare 선택 상단의 Cloudflare를 선택한다.

  3. 아이디, Global API Key 입력 아이디 입력 Cloudflare을 로그인할 때 사용하는 이메일을 입력한다. Global API Key 입력 아까 발급한 Global API Key를 입력한다.

  4. IP를 받아올 방식 선택 Web-based IP discovery service 선택 서버가 공유기와 방화벽을 거치므로 Web-based IP discovery service(웹 기반 IP 탐색)을 선택한다.

  5. DDNS를 설정할 도메인 입력 도메인 입력 도메인이 여러 개일 경우 쉼표로 구분한다.

  6. 실행 확인
    1
    
     $ sudo service ddclient status
    

    위 명령어로 ddclient가 잘 작동하는지 확인해보자.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
     ● ddclient.service - Update dynamic domain name service entries
         Loaded: loaded (/usr/lib/systemd/system/ddclient.service; enabled; preset: enabled)
         Active: active (running) since Wed 2024-12-18 17:12:47 UTC; 41min ago
         Docs: man:ddclient(8)
         Process: 9354 ExecStart=/usr/bin/ddclient -daemon $daemon_interval -syslog -pid /run/ddclient.pid (code=exited, status=0/SUCCESS)
     Main PID: 9359 (ddclient - slee)
         Tasks: 1 (limit: 18935)
         Memory: 11.1M (peak: 15.1M)
             CPU: 263ms
         CGroup: /system.slice/ddclient.service
                     └─9359 "ddclient - sleeping for 210 seconds"
    
     Dec 18 17:27:47 homeserver ddclient[9528]: WARNING:  skipping host: test.example.kr: 'zone=' is an invalid fully qualified host name.
     Dec 18 17:32:47 homeserver ddclient[9556]: WARNING:  skipping host: test.example.kr: 'zone=' is an invalid fully qualified host name.
     Dec 18 17:37:47 homeserver ddclient[9588]: WARNING:  skipping host: test.example.kr: 'zone=' is an invalid fully qualified host name.
     Dec 18 17:37:47 homeserver ddclient[9591]: WARNING:  skipping host: test.example.com: 'zone=' is an invalid fully qualified host name.
     Dec 18 17:42:47 homeserver ddclient[9623]: WARNING:  skipping host: test.example.kr: 'zone=' is an invalid fully qualified host name.
     .
     .
     .
    

    skipping이라고 되어있으니 잘 작동하지 않는 것이다. 'zone=' is an invalid fully qualified host name.라는 에러 메세지에서 알 수 있듯, zone과 관련된 문제가 있는 것으로 보인다.

세부 설정

ddclient의 설정은 /etc/ddclient.conf에서 확인할 수 있다.

1
2
3
4
5
6
7
8
9
# Configuration file for ddclient generated by debconf
#
# /etc/ddclient.conf

protocol=cloudflare \
use=web, web=ipify-ipv4 \
login=my@email.com \
password='Global API Key' \
test.example.kr,test.example.com

Cloudflare는 Zone을 기준으로 도메인을 구분하고, 서브 도메인의 레코드를 수정할 수 있다. 따라서 Zone을 구분하여 파일 내용을 아래와 같이 수정한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
protocol=cloudflare \
use=web, web=http://checkip.dyndns.org/ \
zone=example.kr \
login=my@email.com \
password='Global API Key' \
test.example.kr \

protocol=cloudflare \
use=web, web=http://checkip.dyndns.org/ \
zone=example.com \
login=my@email.com \
password='Global API Key' \
test.example.com

마무리

1
2
3
$ sudo ddclient -daemon=0 -debug -verbose -noquiet
$ sudo service ddclient restart
$ sudo service ddclient status

먼저 디버그 모드로 테스트 한 후, 잘 작동한다면 ddclient를 재시작해서 설정을 반영하자.

HTTPS 적용을 위한 인증서 발급 - certbot

HTTPS는 HTTP에 SSL/TLS 암호화 프로토콜을 추가한 것으로, 클라이언트와 서버 간의 데이터가 암호화되어 도청과 데이터 탈취를 방지할 수 있다.

요즘 브라우저에서는 HTTP를 사용하면 경고를 띄워서 보기 안 좋을 뿐더러, 내가 올릴 서비스 대부분이 HTTP 환경에서는 작동이 어렵다. 따라서 certbot을 통해 무료로 발급 가능한 Let’s Encrypt의 인증서를 통해 HTTPS를 적용하고자 한다.

발급 방식에 여러가지가 있는데, 수동 발급이나 웹서버 등을 이용한 발급은 자동 갱신이 귀찮기 때문에, Cloudflare의 API를 이용해 ACME Challenge 방식으로 발급 및 자동갱신을 하고자 한다. DNS Challenge를 이용한 인증서 발급은 해당 문서를 참고했다.

certbot 설치 및 발급 진행

certbot 홈페이지에선 운영체제가 제공하는 저장소(우분투의 경우 apt) 대신, 컨테이너로 격리된 패키지 형식을 snap를 사용해 설치할 것을 권장한다. snap 사용법은 나중에 찾아서 정리해보겠다.

  1. certbot과 Cloudflare 애드온 설치
    1
    2
    3
    
     $ sudo snap install --classic certbot
     $ sudo snap set certbot trust-plugin-with-root=ok
     $ sudo snap install certbot-dns-cloudflare
    
  2. Global API Key를 원하는 경로에 저장하기
    1
    2
    
     dns_cloudflare_email = my@email.com
     dns_cloudflare_api_key = 'Global API Key'
    

    API 키를 위의 형식대로 입력해준다.


    1
    
     $ sudo chmod 600 /etc/letsencrypt/cloudflare.ini
    

    그리고 작성자 외에 내용을 볼 수 없도록 권한을 설정한다.

  3. 인증서 발급하기
    1
    2
    3
    4
    5
    6
    7
    
     $ sudo certbot certonly \
         --cert-name my-cert
         --dns-cloudflare \
         --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
         --dns-cloudflare-propagation-seconds 15 \
         -d *.example.com \
         -d example.com
    
    • --cert-name my-cert: 인증서 이름
    • --dns-cloudflare-credentials <경로>: API 키 경로
    • --dns-cloudflare-propagation-seconds <초>: DNS 레코드 업데이트까지 기다릴 시간
    • -d <도메인>: 인증서를 발급받을 도메인

    이 방식의 경우 와일드카드 인증서도 발급이 가능하다. 발급하고 싶다면 -d *.example.com와 같이 명령어를 추가입력하면 된다.


    1
    2
    3
    
     Saving debug log to /var/log/letsencrypt/letsencrypt.log
     Enter email address (used for urgent renewal and security notices)
     (Enter 'c' to cancel): my@email.com
    

    인증서 만료와 보안 공지를 받을 이메일을 입력한다.


    1
    2
    3
    4
    5
    6
    
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     Please read the Terms of Service at
     https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf.
     You must agree in order to register with the ACME server. Do you agree?
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     (Y)es/(N)o: 
    

    Let’s Encrypt 약관을 확인하고 동의 여부를 선택한다. 거절하면 당연히 발급이 안 된다…


    1
    2
    3
    4
    5
    
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     Would you be willing, once your first certificate is successfully issued, to share your email address with the Electronic Frontier Foundation, a founding partner of the Let's Encrypt project and the non-profit organization that develops Certbot?
     We'd like to send you email about our work encrypting the web, EFF news, campaigns, and ways to support digital freedom.
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     (Y)es/(N)o: 
    

    Let’s Encrypt를 관리하는 재단인 Electronic Frontier Foundation의 이메일을 받을지 선택한다.

  4. 인증서 발급 완료
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
     Account registered.
     Requesting a certificate for example.com and 1 more domains
     Waiting 15 seconds for DNS changes to propagate
    
     Successfully received certificate.
     Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
     Key is saved at:         /etc/letsencrypt/live/example.com/privkey.pem
     This certificate expires on 2025-04-23.
     These files will be updated when the certificate renews.
     Certbot has set up a scheduled task to automatically renew this certificate in the background.
    
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     If you like Certbot, please consider supporting our work by:
     * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
     * Donating to EFF:                    https://eff.org/donate-le
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    

    인증서가 /etc/letsencrypt/live/example.com에 발급되었다.

  5. 자동 갱신 테스트
    1
    
     $ sudo certbot renew --dry-run
    

    자동 갱신에 문제가 생기지는 않는지 명령어를 실행해서 확인해보자. 문제가 있다면 /etc/letsencrypt/renewal/example.com.conf에서 설정을 수정할 수 있다.

인증서 삭제

적용할 도메인에 오타가 났거나, 마음에 안 드는 부분이 있을 경우 삭제 후 다시 발급을 해야한다. 인증서는 아래 절차에 따라 삭제가 가능하다.

  1. 인증서 리스트 확인
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
     $ sudo certbot certificates
     Saving debug log to /var/log/letsencrypt/letsencrypt.log
    
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     Found the following certs:
         Certificate Name: example.com
         Serial Number: ***********************************
         Key Type: ECDSA
         Domains: example.com
         Expiry Date: 2025-04-23 15:26:03+00:00 (VALID: 89 days)
         Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
         Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
  2. Certificate Name 확인 후 인증서 삭제
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
     $ sudo certbot delete --cert-name [인증서 이름]
     Saving debug log to /var/log/letsencrypt/letsencrypt.log
    
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     The following certificate(s) are selected for deletion:
    
         * example.com
    
     WARNING: Before continuing, ensure that the listed certificates are not being
     used by any installed server software (e.g. Apache, nginx, mail servers).
     Deleting a certificate that is still being used will cause the server software
     to stop working. See https://certbot.org/deleting-certs for information on
     deleting certificates safely.
    
     Are you sure you want to delete the above certificate(s)?
     - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     (Y)es/(N)o: y
     Deleted all files relating to certificate example.com.
    

삭제 후 인증서 리스트를 다시 확인하고, 인증서를 사용하는 웹서버가 있는지 점검해본다.

이 글은 저작권자의 CC BY 4.0 라이센스를 따릅니다.