이채강

읽는 데 17분조회 18

무료 인증서 발급 가이드

버전 1.0.0

DuckDNS · Let’s Encrypt · Certbot · Caddy · acme.sh

예제 도메인: leechis.duckdns.org

1. 문서 목적과 권장 구성

이 문서는 DuckDNS에서 도메인을 등록하고 서버 공인 IP에 연결한 뒤, Let’s Encrypt 인증서를 발급해 HTTPS 서비스를 구성하는 전체 흐름을 설명합니다.

예제 환경:
• 기본 도메인: leechis.duckdns.org
• Calibre: books.leechis.duckdns.org → 127.0.0.1:40001
• Grafana: grafana.leechis.duckdns.org → 127.0.0.1:53000
• 공개 진입점: Caddy가 TCP 80·443 수신
• 운영체제: Ubuntu 계열 Linux

권장 선택:
• 웹서비스 리버스 프록시: Caddy 자동 HTTPS가 가장 간단합니다.
• 인증서와 개인키 파일이 직접 필요한 서비스: Certbot DNS-01을 사용합니다.
• Nginx·Apache를 이미 운영 중: 해당 웹서버용 Certbot 플러그인 또는 webroot 방식을 사용합니다.
• 와일드카드 인증서: DNS-01 방식이 필요합니다.

주의: Caddy와 Certbot standalone은 모두 80 또는 443 포트를 사용할 수 있습니다. 같은 서버에서 동시에 실행하면 포트 충돌이 발생하므로 목적에 맞는 한 방식을 선택하거나 발급 중 Caddy를 잠시 중지해야 합니다.

2. 전체 처리 흐름

DuckDNS 계정 로그인
→ leechis.duckdns.org 등록
→ DuckDNS A 레코드를 서버 공인 IP로 갱신
→ wildcard=YES로 하위 호스트도 같은 서버로 연결
→ DNS 조회로 leechis·books·grafana 확인
→ 클라우드 방화벽과 서버 방화벽에서 80·443 허용
→ 서비스는 127.0.0.1의 내부 포트에서 실행
→ Caddy가 도메인별로 내부 서비스에 reverse_proxy
→ Caddy 또는 Certbot이 Let’s Encrypt에 인증서 요청
→ Let’s Encrypt가 도메인 제어권 검증
→ HTTPS 서비스 시작 및 자동 갱신

요청 흐름:
인터넷 사용자
→ books.leechis.duckdns.org:443
→ DNS가 서버 공인 IP 반환
→ Caddy가 TLS 종료
→ 127.0.0.1:40001 Calibre

인터넷 사용자
→ grafana.leechis.duckdns.org:443
→ DNS가 서버 공인 IP 반환
→ Caddy가 TLS 종료
→ 127.0.0.1:53000 Grafana

3. DuckDNS 도메인 등록

3.1 계정 로그인과 도메인 추가

1) https://www.duckdns.org/ 에 접속합니다.
2) Google, GitHub 등 제공되는 방식으로 로그인합니다.
3) domains 입력란에 leechis를 입력합니다.
4) add domain을 눌러 leechis.duckdns.org를 등록합니다.
5) 화면에 표시되는 token을 안전한 장소에 보관합니다.

DuckDNS에서 입력하는 값은 leechis이며 .duckdns.org는 붙이지 않습니다. token은 DNS 변경 권한을 가진 비밀정보이므로 채팅, Git, 문서, 공개 스크립트에 실제 값을 기록하지 않습니다.

3.2 기본 도메인과 서비스 하위 도메인

• 등록 도메인: leechis.duckdns.org
• 서비스 호스트: books.leechis.duckdns.org
• 서비스 호스트: grafana.leechis.duckdns.org

DuckDNS의 wildcard 기능을 켜면 books와 grafana 같은 한 단계 하위 이름도 leechis.duckdns.org와 같은 공인 IP를 가리킵니다. DNS는 서버까지 연결할 IP만 결정하고, 어떤 내부 서비스로 보낼지는 Caddy가 Host 이름을 보고 결정합니다.

4. DuckDNS IP 갱신과 DNS 확인

4.1 공인 IPv4와 wildcard 갱신

현재 접속한 서버의 공인 IPv4를 자동 감지해 등록하는 예입니다.

export DUCKDNS_TOKEN='YOUR_DUCKDNS_TOKEN'
curl -fsS "https://www.duckdns.org/update?domains=leechis&token=$DUCKDNS_TOKEN&ip=&wildcard=YES&verbose=true"
unset DUCKDNS_TOKEN

정상 응답은 OK입니다.

• domains에는 leechis만 사용합니다.
• ip=를 비우면 요청을 보낸 쪽의 공인 IPv4를 감지합니다.
• 고정 공인 IP라면 ip=<공인IPv4>로 명시할 수 있습니다.
• IPv6는 DuckDNS API의 ipv6 매개변수로 별도 지정합니다.
• wildcard=YES는 books.leechis.duckdns.org 같은 하위 이름을 같은 IP로 연결할 때 사용합니다.

4.2 동적 IP 자동 갱신

공인 IP가 바뀌는 환경에서는 토큰을 권한 600 파일에 보관하고 5분 주기 cron 또는 systemd timer로 갱신합니다. 토큰을 crontab 명령행에 직접 기록하면 다른 사용자나 백업에서 노출될 수 있으므로 전용 환경 파일 또는 root 전용 스크립트를 권장합니다.

API 호출 형식:
https://www.duckdns.org/update?domains=leechis&token=YOUR_DUCKDNS_TOKEN&ip=&wildcard=YES

IP 갱신과 Let’s Encrypt DNS-01 검증용 TXT 레코드 갱신은 서로 다른 작업입니다.

4.3 DNS 확인

dig +short leechis.duckdns.org A
dig +short books.leechis.duckdns.org A
dig +short grafana.leechis.duckdns.org A

세 이름이 서버의 공인 IP를 반환해야 합니다. 다른 IP가 보이거나 응답이 없으면 인증서 발급보다 먼저 DuckDNS 갱신과 DNS 전파를 해결합니다.

5. 서버 도메인 적용 구조

5.1 실제 예제 매핑

• leechis.duckdns.org → 서버 공인 IP
• books.leechis.duckdns.org → 서버 공인 IP → Caddy → 127.0.0.1:40001
• grafana.leechis.duckdns.org → 서버 공인 IP → Caddy → 127.0.0.1:53000

leechis.duckdns.org 자체에 웹서비스를 제공하려면 Caddyfile에 별도 사이트 블록을 추가해야 합니다. 현재 실제 Caddy 설정은 books와 grafana 두 호스트만 처리합니다.

5.2 백엔드 서비스 바인딩

백엔드 서비스는 가능하면 모든 인터페이스의 0.0.0.0이 아니라 127.0.0.1에 바인딩합니다. 외부 사용자는 Caddy의 443 포트로만 접근하고 Calibre 40001, Grafana 53000 같은 내부 포트는 직접 공개하지 않는 구성이 안전합니다.

확인 예:
ss -lntp
curl -I http://127.0.0.1:40001/
curl -I http://127.0.0.1:53000/

5.3 네트워크 준비

Let’s Encrypt HTTP-01과 Caddy 자동 HTTPS를 사용하려면 다음 두 계층에서 TCP 80·443이 열려 있어야 합니다.

• Oracle Cloud Security List 또는 NSG 인바운드 규칙
• Ubuntu의 iptables, nftables 또는 UFW 규칙

확인 예:
sudo ss -lntp | grep -E ':(80|443)\b'
sudo iptables -S
sudo ufw status

백엔드 포트는 Caddy가 로컬에서 접근할 수 있으면 외부에 열 필요가 없습니다.

6. Let’s Encrypt와 검증 방식

Let’s Encrypt는 무료 공개 신뢰 TLS 인증서를 발급하는 CA이며 ACME 프로토콜로 도메인 제어권을 검증합니다. 인증서는 자동 갱신을 전제로 운영해야 합니다.

6.1 HTTP-01

• TCP 80의 /.well-known/acme-challenge/ 경로로 검증합니다.
• 일반 단일 호스트 인증서에 간단합니다.
• 와일드카드 인증서는 발급할 수 없습니다.
• standalone, webroot, Nginx·Apache 플러그인에서 사용합니다.

6.2 DNS-01

• _acme-challenge.<도메인> TXT 레코드로 검증합니다.
• 외부 80·443 포트가 없어도 발급할 수 있습니다.
• 와일드카드 인증서를 발급할 수 있습니다.
• DNS API 자격증명 보호와 TXT 전파 시간이 중요합니다.

6.3 TLS-ALPN-01

• TCP 443에서 ACME 전용 TLS 응답으로 검증합니다.
• 와일드카드 인증서는 발급할 수 없습니다.
• 일반 사용자가 직접 구성하기보다 Caddy 같은 TLS 프록시의 자동화에 주로 사용됩니다.

7. 기본 Let’s Encrypt 인증서 발급: Certbot

Caddy를 사용하지 않고 인증서와 개인키 파일을 직접 관리해야 할 때 사용하는 장입니다. 현재 Caddy 운영 서버에서는 8장의 Caddy 자동 HTTPS가 기본 선택입니다.

7.1 Certbot 설치

Certbot 공식 안내는 대부분의 사용자에게 Snap 설치를 권장합니다. 기존 apt 또는 pip Certbot과 혼용하지 않습니다.

sudo snap install core
sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/local/bin/certbot

확인:
certbot --version

OS 패키지나 pip 기반으로 설치할 수도 있지만 Certbot 본체와 DNS 플러그인은 반드시 같은 Python·패키징 환경에 있어야 합니다.

7.2 standalone HTTP-01 발급

웹서버 없이 Certbot이 임시로 TCP 80을 열어 검증합니다.

sudo certbot certonly --standalone \
--agree-tos --email YOUR_EMAIL \
-d leechis.duckdns.org

books와 grafana를 한 인증서의 SAN으로 발급하는 예:
sudo certbot certonly --standalone \
--agree-tos --email YOUR_EMAIL \
-d books.leechis.duckdns.org \
-d grafana.leechis.duckdns.org

조건:
• 각 도메인이 이 서버의 공인 IP를 가리켜야 합니다.
• 외부에서 TCP 80 접근이 가능해야 합니다.
• Caddy·Nginx·Apache가 80을 사용 중이면 중지하거나 다른 방식을 사용해야 합니다.

현재 Caddy 서버에서 standalone을 시험한다면 서비스 중단을 고려해 유지보수 시간에 다음 순서로 진행합니다.

sudo systemctl stop caddy
sudo certbot certonly --standalone -d leechis.duckdns.org
sudo systemctl start caddy

운영 중인 Caddy 사이트는 Caddy가 직접 인증서를 관리하므로 위 방식으로 중복 발급할 필요가 없습니다.

7.3 webroot HTTP-01 발급

기존 웹서버를 중지하지 않고 검증 파일을 서비스할 수 있을 때 사용합니다.

sudo certbot certonly --webroot \
-w /var/www/html \
--agree-tos --email YOUR_EMAIL \
-d leechis.duckdns.org

웹서버가 /.well-known/acme-challenge/ 아래 파일을 외부 TCP 80에서 그대로 제공해야 합니다.

7.4 수동 DNS-01 발급

웹서버와 외부 80·443 포트 없이 발급할 수 있지만, 수동 방식은 갱신 때마다 같은 작업을 반복해야 합니다.

sudo certbot certonly --manual \
--preferred-challenges dns \
--agree-tos --email YOUR_EMAIL \
-d leechis.duckdns.org

Certbot이 표시한 TXT_VALUE를 새 터미널에서 DuckDNS에 등록합니다.

export DUCKDNS_TOKEN='YOUR_DUCKDNS_TOKEN'
curl -fsS "https://www.duckdns.org/update?domains=leechis&token=$DUCKDNS_TOKEN&txt=TXT_VALUE&verbose=true"
dig @1.1.1.1 TXT _acme-challenge.leechis.duckdns.org +short

조회 값이 TXT_VALUE와 일치하면 Certbot 터미널로 돌아가 Enter를 누릅니다. 발급 후 TXT 값을 비웁니다.

curl -fsS "https://www.duckdns.org/update?domains=leechis&token=$DUCKDNS_TOKEN&clear=true"
unset DUCKDNS_TOKEN

7.5 certbot-dns-duckdns 자동 DNS-01

certbot-dns-duckdns는 DuckDNS TXT 등록을 자동화하는 커뮤니티 플러그인입니다. Certbot 본체와 같은 설치 환경에 설치해야 합니다.

토큰 파일:
/etc/letsencrypt/duckdns.ini

dns_duckdns_token=YOUR_DUCKDNS_TOKEN

권한 제한:
sudo chmod 600 /etc/letsencrypt/duckdns.ini

단일 호스트 발급:
sudo certbot certonly \
--authenticator dns-duckdns \
--dns-duckdns-credentials /etc/letsencrypt/duckdns.ini \
--dns-duckdns-propagation-seconds 60 \
-d leechis.duckdns.org

와일드카드 발급:
sudo certbot certonly \
--authenticator dns-duckdns \
--dns-duckdns-credentials /etc/letsencrypt/duckdns.ini \
--dns-duckdns-propagation-seconds 60 \
-d '*.leechis.duckdns.org'

*.leechis.duckdns.org는 books와 grafana 같은 한 단계 하위 호스트에 유효하지만 leechis.duckdns.org 자체에는 유효하지 않습니다. DuckDNS의 TXT API 특성상 루트와 와일드카드를 한 번에 요청할 때 같은 TXT 위치에서 충돌할 수 있으므로 이 가이드에서는 별도 인증서 발급을 권장합니다.

7.6 인증서 파일과 갱신

Certbot 기본 경로:
/etc/letsencrypt/live/<인증서이름>/
fullchain.pem
privkey.pem
cert.pem
chain.pem

확인과 갱신 시험:
sudo certbot certificates
sudo certbot renew --dry-run
systemctl list-timers | grep -i certbot

수동 --manual 발급은 인증·정리 hook을 별도로 만들지 않으면 자동 갱신되지 않습니다. 자동 플러그인 또는 HTTP-01 방식을 운영 환경에 권장합니다.

갱신 후 서비스 반영:
sudo certbot renew --deploy-hook 'systemctl reload nginx'

8. Caddy 자동 HTTPS와 실제 서버 적용

8.1 Caddy 방식의 특징

Caddyfile의 사이트 주소에 정상 도메인을 쓰면 Caddy가 인증서 발급, 저장, HTTPS 리다이렉트와 갱신을 자동으로 관리합니다. 웹 리버스 프록시 목적이라면 Certbot을 별도로 실행할 필요가 없습니다.

필수 조건:
• 도메인이 서버 공인 IP를 가리켜야 합니다.
• 외부 TCP 80·443이 서버에 도달해야 합니다.
• Caddy가 80·443을 수신할 수 있어야 합니다.
• 백엔드 서비스가 지정한 로컬 포트에서 응답해야 합니다.

Caddy 저장소의 인증서와 ACME 계정 데이터는 Caddy가 관리합니다. /etc/letsencrypt 경로와 혼동하지 않습니다.

8.2 설치와 서비스 확인

Ubuntu 패키지 설치 예:
sudo apt update
sudo apt install caddy

확인:
caddy version
systemctl is-enabled caddy
systemctl is-active caddy

현재 예제 서버는 Caddy 2.6.2가 active 상태로 동작합니다.

8.3 실제 서버 Caddyfile

파일:
/etc/caddy/Caddyfile

books.leechis.duckdns.org {
encode zstd gzip
reverse_proxy 127.0.0.1:40001
}

grafana.leechis.duckdns.org {
encode zstd gzip
reverse_proxy 127.0.0.1:53000
}

동작:
• books 호스트 요청은 Calibre 40001로 전달됩니다.
• grafana 호스트 요청은 Grafana 53000으로 전달됩니다.
• Caddy가 각 공개 호스트의 TLS 인증서를 자동 발급·갱신합니다.
• 현재 구성은 호스트별 인증서를 사용하므로 와일드카드 인증서가 필요하지 않습니다.

기본 도메인에도 응답하려면 선택적으로 다음 블록을 추가할 수 있습니다.

leechis.duckdns.org {
respond "server is running" 200
}

이는 현재 실제 설정에는 없는 선택 예시입니다.

8.4 설정 검증과 적용

sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

상태와 로그:
systemctl status caddy --no-pager
journalctl -u caddy -n 100 --no-pager

validate가 실패하면 reload하지 말고 Caddyfile 오류를 먼저 수정합니다.

8.5 HTTPS 확인

dig +short books.leechis.duckdns.org A
dig +short grafana.leechis.duckdns.org A
curl -I http://books.leechis.duckdns.org/
curl -I https://books.leechis.duckdns.org/
curl -I https://grafana.leechis.duckdns.org/

인증서:
openssl s_client -connect books.leechis.duckdns.org:443 \
-servername books.leechis.duckdns.org </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates

현재 실제 구성의 정상 응답 예:
• Calibre: HTTPS 200
• Grafana: /login으로 302

9. Nginx·Apache에 Certbot 인증서 적용

9.1 Nginx

server {
listen 443 ssl http2;
server_name books.leechis.duckdns.org;

`ssl_certificate     /etc/letsencrypt/live/<인증서이름>/fullchain.pem;`  
`ssl_certificate_key /etc/letsencrypt/live/<인증서이름>/privkey.pem;`

location / {  
    `proxy_pass http://127.0.0.1:40001;`  
    `proxy_set_header Host $host;`  
    `proxy_set_header X-Real-IP $remote_addr;`  
    `proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;`  
    `proxy_set_header X-Forwarded-Proto $scheme;`  
`}`  

}

검증과 적용:
sudo nginx -t
sudo systemctl reload nginx

9.2 Apache

<VirtualHost *:443>
ServerName books.leechis.duckdns.org
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/<인증서이름>/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/<인증서이름>/privkey.pem
ProxyPass / http://127.0.0.1:40001/
ProxyPassReverse / http://127.0.0.1:40001/
</VirtualHost>

검증과 적용:
sudo apachectl configtest
sudo systemctl reload apache2

10. acme.sh 대안

acme.sh는 Shell 기반 ACME 클라이언트입니다. 인증서 신뢰도는 Certbot과 같으며 경량 장비·컨테이너·스크립트 중심 환경에 적합합니다.

설치:
curl https://get.acme.sh | sh -s email=YOUR_EMAIL

토큰:
export DuckDNS_Token='YOUR_DUCKDNS_TOKEN'

단일 도메인:
~/.acme.sh/acme.sh --issue --dns dns_duckdns \
-d leechis.duckdns.org

와일드카드:
~/.acme.sh/acme.sh --issue --dns dns_duckdns \
-d '*.leechis.duckdns.org'

서비스 경로로 설치:
~/.acme.sh/acme.sh --install-cert -d '*.leechis.duckdns.org' --ecc \
--key-file /etc/myservice/tls/key.pem \
--fullchain-file /etc/myservice/tls/fullchain.pem \
--reloadcmd 'systemctl restart myservice'

acme.sh 내부 저장 경로의 파일을 서비스에서 직접 참조하기보다 --install-cert로 고정 운영 경로에 복사하고 reload 명령을 연결합니다.

11. 장애 점검 순서

1) DNS 확인
dig +short books.leechis.duckdns.org A

2) 서버 공인 IP 확인
curl -4 https://ifconfig.me

3) 백엔드 확인
curl -I http://127.0.0.1:40001/
curl -I http://127.0.0.1:53000/

4) 수신 포트 확인
sudo ss -lntp | grep -E ':(80|443)\b'

5) Caddy 설정과 로그
sudo caddy validate --config /etc/caddy/Caddyfile
journalctl -u caddy -n 100 --no-pager

6) 클라우드·서버 방화벽 확인
• OCI Security List 또는 NSG
• iptables, nftables 또는 UFW

7) 외부 HTTPS 확인
curl -Iv https://books.leechis.duckdns.org/

자주 발생하는 원인:
• DuckDNS가 이전 IP를 가리킴
• wildcard가 비활성화되어 하위 호스트가 해석되지 않음
• OCI와 서버 방화벽 중 한쪽에서 80·443 차단
• Caddy 외 다른 프로세스가 80·443 사용
• 백엔드 서비스가 중지되었거나 잘못된 포트에 바인딩
• DNS-01 TXT 값 오타 또는 전파 대기 부족
• 반복 실패로 인한 Let’s Encrypt 발급 제한

처음 시험할 때는 Certbot의 --dry-run 또는 ACME staging 환경을 사용합니다.

12. 보안·운영 체크리스트

• DuckDNS token과 인증서 개인키는 비밀번호처럼 관리합니다.
• 실제 token을 채팅, Git, 문서, 셸 히스토리에 남기지 않습니다.
• token 파일과 개인키는 최소 권한으로 제한합니다.
• 백엔드 서비스는 가능하면 127.0.0.1에 바인딩합니다.
• 외부에는 필요한 80·443 포트만 공개합니다.
• Caddy 설정, 인증서와 ACME 데이터를 정기 백업합니다.
• Certbot은 renew --dry-run으로 갱신을 시험합니다.
• 갱신 후 서비스 reload 또는 restart가 실행되는지 확인합니다.
• 와일드카드 인증서 개인키 접근을 더 엄격히 제한합니다.
• Caddy와 Certbot으로 같은 도메인을 중복 관리하지 않습니다.

13. 빠른 선택

• 가장 간단한 웹 HTTPS·리버스 프록시: Caddy 자동 HTTPS
• 인증서 파일이 직접 필요한 서비스: Certbot DNS-01
• 공개 포트 80 사용 가능, 단일 호스트: Certbot HTTP-01
• 외부 포트 없이 발급: Certbot DNS-01
• 여러 하위 호스트를 한 인증서로 처리: DNS-01 와일드카드
• 기존 Nginx·Apache 유지: Certbot 플러그인 또는 webroot
• 경량·스크립트 중심: acme.sh

현재 예제 서버의 권장 결론:
Caddy가 books와 grafana의 HTTPS 인증서 발급·갱신과 리버스 프록시를 담당하고, 웹서버와 독립적인 인증서 파일이 필요한 서비스에만 Certbot DNS-01을 별도로 사용합니다.

참고

DuckDNS: https://www.duckdns.org/
DuckDNS Install: https://www.duckdns.org/install.jsp
DuckDNS API Specification: https://www.duckdns.org/spec.jsp
Let’s Encrypt Challenge Types: https://letsencrypt.org/docs/challenge-types/
Let’s Encrypt Rate Limits: https://letsencrypt.org/docs/rate-limits/
Certbot Instructions: https://certbot.eff.org/instructions
Certbot Documentation: https://eff-certbot.readthedocs.io/
Caddy HTTPS Quick Start: https://caddyserver.com/docs/quick-starts/https
Caddy reverse_proxy: https://caddyserver.com/docs/caddyfile/directives/reverse\_proxy
certbot-dns-duckdns: https://pypi.org/project/certbot-dns-duckdns/
acme.sh: https://github.com/acmesh-official/acme.sh

부록 A. OpenSSL로 사설 Root CA 및 서버 인증서 만들기

사설 CA는 내부망·개발·검증 환경에서만 사용합니다. 브라우저와 운영체제는 사설 Root CA를 기본적으로 신뢰하지 않으므로, 접속하는 모든 클라이언트에 Root CA 인증서를 신뢰 저장소로 배포해야 합니다. 인터넷에 공개하는 서비스는 이 문서의 Let’s Encrypt 방식을 사용합니다.

A.1 작업 디렉터리와 Root CA 개인키

Root CA 개인키는 최상위 비밀입니다. 서버에 상시 두지 말고, 암호화된 오프라인 보관소에 보관하는 것을 권장합니다.

mkdir -p ~/private-ca && cd ~/private-ca
umask 077
openssl genrsa -aes256 -out root-ca.key 4096

A.2 자체서명 Root CA 인증서 생성

Common Name은 조직에서 식별할 수 있는 이름으로 바꿉니다. 예시는 10년 유효기간이며, 내부 보안 정책에 맞춰 더 짧게 정할 수 있습니다.

openssl req -x509 -new -sha256 -key root-ca.key \
-days 3650 -out root-ca.crt \
-subj "/C=KR/O=Example Internal/CN=Example Internal Root CA"

openssl x509 -in root-ca.crt -noout -subject -issuer -dates

A.3 서버 개인키와 CSR 생성

서버 개인키는 해당 서버에서 생성·보관하는 것이 원칙입니다. Common Name에는 대표 이름을 넣고, 실제 사용 이름은 다음 SAN 설정에서 모두 지정합니다.

openssl genrsa -out server.key 2048
chmod 600 server.key

openssl req -new -sha256 -key server.key -out server.csr \
-subj "/C=KR/O=Example Internal/CN=dev.leechis.duckdns.org"

A.4 SAN 확장 설정

server-ext.cnf 파일을 만들고, 서비스에 사용할 DNS 이름과 IP를 정확히 넣습니다. 현대 브라우저와 TLS 클라이언트는 Common Name만 보지 않고 SAN을 검사합니다.

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectAltName=@alt_names

[alt_names]
DNS.1=leechis.duckdns.org
DNS.2=dev.leechis.duckdns.org
IP.1=192.168.1.10

IP 주소를 사용하지 않으면 IP.1 줄은 제거합니다. dev·api 등 필요한 모든 호스트명을 DNS.n으로 추가합니다.

A.5 Root CA로 서버 인증서 서명

아래 예시는 서버 인증서를 397일 동안 발급합니다. 내부 정책에 맞춰 기간을 조정하되, 갱신 절차를 운영해야 합니다.

openssl x509 -req -sha256 -in server.csr \
-CA root-ca.crt -CAkey root-ca.key -CAcreateserial \
-out server.crt -days 397 -extfile server-ext.cnf

openssl verify -CAfile root-ca.crt server.crt
openssl x509 -in server.crt -noout -subject -issuer -dates -ext subjectAltName

A.6 서버에 배포하고 Nginx·Apache에 적용

서버에는 server.key와 server.crt를 배포하고, Root CA 개인키(root-ca.key)는 절대 복사하지 않습니다. 중간 CA를 쓰지 않는 단순 구성에서는 server.crt가 서버 인증서 파일입니다.

Nginx:
ssl_certificate /etc/ssl/private/server.crt;
ssl_certificate_key /etc/ssl/private/server.key;

Apache:
SSLCertificateFile /etc/ssl/private/server.crt
SSLCertificateKeyFile /etc/ssl/private/server.key

서버 개인키는 웹서버 실행 계정만 읽을 수 있게 권한을 제한합니다. Nginx/Apache 설정 검증 후 reload합니다.

A.7 클라이언트 신뢰 배포

서버 인증서가 아니라 root-ca.crt만 클라이언트 신뢰 저장소에 배포합니다. Root CA를 신뢰한 조직 장비에서만 이 서버 인증서가 정상으로 표시됩니다.

• Windows: 관리자가 그룹 정책 또는 ‘신뢰할 수 있는 루트 인증 기관’ 저장소에 root-ca.crt를 배포
• Debian/Ubuntu: /usr/local/share/ca-certificates/에 root-ca.crt를 두고 update-ca-certificates 실행
• RHEL 계열: /etc/pki/ca-trust/source/anchors/에 두고 update-ca-trust 실행
• macOS/iOS/Android·브라우저: 각 플랫폼의 프로파일·인증서 정책에 따라 관리형 배포

A.8 운영·보안 주의

• Root CA 개인키가 유출되면 해당 CA가 발급한 모든 인증서를 신뢰할 수 없게 됩니다. 새 CA를 만들고 모든 클라이언트의 신뢰 저장소와 서버 인증서를 교체해야 합니다.
• 사설 Root CA는 공개 브라우저 신뢰를 얻지 못합니다. DuckDNS 주소라도 외부 사용자용 HTTPS에는 Let’s Encrypt를 사용합니다.
• 인증서 폐기(CRL/OCSP)와 중간 CA 운영은 별도 PKI 설계가 필요합니다. 소규모 내부 환경은 짧은 서버 인증서 유효기간과 교체 절차를 문서화하는 방식이 단순합니다.
• Root CA 인증서는 안전하게 백업하되, 개인키 백업은 암호화하고 접근자를 최소화합니다.

댓글

첫 댓글을 남겨보세요.

이름 20자, 댓글 1000자까지

← 글 목록으로