개요(Abstract)
이 글에서는, TLS(SSL) 종점(Termination)를 위한 인증서로 오픈 CA인 Let's Encrypt를 사용한 점
등, 특이 사항과 Haproxy 환경 파일에 대한 주요 사항만을 기술하기로 한다.
Let's Encrypt 오픈 CA 인증서 및 설치
이 새로운 오픈CA 인증에 대한 기본 개요는 이전 글 SSL 인증의 새로운 대한 Let's Encrypt 을 참조한다.
우리는 우분투 플렛폼의 Haproxy에 탑재하고자 하므로, 다음의 과정을 거친다.
[Let's Encrypt 설치 도구인 Certbot 설치]
$ wget https://dl.eff.org/certbot-auto
$ chmod a+x certbot-auto
다음 명령어를 수행하여, 인증서 설치에 도구에 필요한 종속성 패키지등 모든 클라이언트 도구 업데이트 수행
$ ./certbot-auto
Let's Encrypt 인증서 설치
클라이언트 도구 certbot-auto 실행한다.
$ ./certbot-auto certonly --standalone
위 명령어를 실행하면, certbot-auto는 작은 자체 웹 서버를 80 포트로 기동한다.
이를 통해 Let's Encrypt CA로 부터 도메인 인증을 수행한다.
주의)
위 명령어를 실행 하기 전, 서비스 포트 80에 웹 서비스가 사용되고 있는지 점검한다.
만약, 웹 서비스가 실행 중이라면 정지하거나 기존 웹서버를 사용한 webroot plugin 모드를 사용한다.
이 글에서는, 80 포트로 어떠한 서비스도 수행되고 있지 않다고 가정한다.
다음, 몇 가지 정보를 요구하는 프롬프트 창이 뜨며, 관련정보를 입력한다.
먼저, 인증서 갱신 등의 안내 메일을 받을 수 있는 email 등록
다음, Let's Encryp사용 동의서 확인
이 글에서는, TLS(SSL) 종점(Termination)를 위한 인증서로 오픈 CA인 Let's Encrypt를 사용한 점
등, 특이 사항과 Haproxy 환경 파일에 대한 주요 사항만을 기술하기로 한다.
Let's Encrypt 오픈 CA 인증서 및 설치
이 새로운 오픈CA 인증에 대한 기본 개요는 이전 글 SSL 인증의 새로운 대한 Let's Encrypt 을 참조한다.
우리는 우분투 플렛폼의 Haproxy에 탑재하고자 하므로, 다음의 과정을 거친다.
[Let's Encrypt 설치 도구인 Certbot 설치]
$ wget https://dl.eff.org/certbot-auto
$ chmod a+x certbot-auto
다음 명령어를 수행하여, 인증서 설치에 도구에 필요한 종속성 패키지등 모든 클라이언트 도구 업데이트 수행
$ ./certbot-auto
Let's Encrypt 인증서 설치
클라이언트 도구 certbot-auto 실행한다.
$ ./certbot-auto certonly --standalone
위 명령어를 실행하면, certbot-auto는 작은 자체 웹 서버를 80 포트로 기동한다.
이를 통해 Let's Encrypt CA로 부터 도메인 인증을 수행한다.
주의)
위 명령어를 실행 하기 전, 서비스 포트 80에 웹 서비스가 사용되고 있는지 점검한다.
만약, 웹 서비스가 실행 중이라면 정지하거나 기존 웹서버를 사용한 webroot plugin 모드를 사용한다.
이 글에서는, 80 포트로 어떠한 서비스도 수행되고 있지 않다고 가정한다.
다음, 몇 가지 정보를 요구하는 프롬프트 창이 뜨며, 관련정보를 입력한다.
먼저, 인증서 갱신 등의 안내 메일을 받을 수 있는 email 등록
다음, Let's Encryp사용 동의서 확인
그리고, 인증서를 사용할 도메인 지정
모든 작업이 성공적으로 완료되면, /etc/letsencrypt/live/도메인명/ 경로 밑에 다음 4개의
파일이 생성된다.
cert.pem: 해당 도메인에 대한 공개키 인증서
chain.pem: Let's Encrypt chain certificate
fullchain.pem: cert.pem and chain.pem combined
privkey.pem: 공개키 인증서의 private key
Haproxy의 TLS(SSL) Termination 구성을 위해 fullchain.pem 과 privkey.pem 병합한다.
cat 명령을 사용하여 단순히 두 파일을 병합하고 pem 확장자를 지정한다.
$ sudo mkdir -p /etc/haproxy/certs
$ sudo -E bash -c 'cat /etc/letsencrypt/live/$DOMAIN/fullchain.pem /etc/letsencrypt/live/도메인/privkey.pem > /etc/haproxy/certs/ssl.pem'
보안을 강화하기 위해, 해당 파일의 권한 변경
sudo chmod -R go-rwx /etc/haproxy/certs
Let's Encrypt 인증서 갱신
인증서를 갱신하기 위해서는 Haproxy 환경 파일을 약간 수정한다.
이때, ACL(Access control list)를 사용하면 요청 패킷의 url path를 식별하여 let's encrypt 인증서 갱신 요청만을 분리하여 서비스 중단이 발생할 수 있는 80 포트 서비스를 별도로 구성했던 번거로움 없이 인증서 갱신을 처리할 수 있다.
Haproxy를 다음과 같이 변경한다.
frontend https-frontend
#bind *:443 ssl crt /etc/ssl/priavte/ssl.pem
bind *:443 ssl crt /etc/haproxy/certs/ssl.pem
reqadd X-Forwarded-Proto:\ https
acl letsencrypt-acl path_beg /.well-known/acme-challenge/
use_backend letsencrypt-backend if letsencrypt-acl
default_backend wwwbackend
backend letsencrypt-backend
server letsencrypt 127.0.0.1:54321
중요한 부분은 highlight 로 표현하였다.
acl 구문은 다음의 포맷을 가진다.
acl <aclname> <criterion> [flags] [operator] [<value>] ...
자세한 도움말은 공식문서 https://cbonte.github.io/haproxy-dconv/configuration-1.6.html#7 참조하고
여기서는 주요내용만 다루자.
우선 acl 규칙 생성이다.
acl letsencrypt-acl path_beg /.well-known/acme-challenge/
letsencrypt-acl 는 acl 이름이고,
path_beg에서 path 은 url을 의미하며, _는 규칙 구분자 beg은 begin으로 이후 나타나는 문자열이 시작될 경우 true 를 반환한다.
여기서는 url path가 /.well-known/acme-challenge/ 시작하면 let's encrypt 인증서 갱신요청으로 간주한다.
use_backend letsencrypt-backend if letsencrypt-acl
위 구문은 acl 규칙에 대한 사용여부를 판별한다.
즉, letsencrypt-acl 이름 지어진 규칙이 true를 반환하면, use_backend로 패킷을 보낸다.
server letsencrypt 127.0.0.1:54321
let's Encrypt인증서 갱신을 위해 certbot-auto가 자체 웹서버를 54321 포트로 오픈 하고 가정하고
letsencrypt-backend 는 해당 요청을 54321포트로 포워딩한다.
위와 같은 메커니즘을 사용함 으로서, 기존 서비스에 영향을 주지 않고 인증서 갱신이 가능하다.
인증서 갱신
--모든 도메인의 인증서를 renewal
./certbot-auto renew --standalone --agree-tos --renew-by-default --standalone-supported-challenges http-01 --http-01-port 54321
위 방법은, 해당 사이트에 여러 도메인에 해당하는 인증서가 있을 경우 모든 인증서를 갱신하도록 한다.
--특정 도메인의 인증서만 renewal
/home/user/certbot-auto certonly --standalone --agree-tos --renew-by-default --standalone-supported-challenges http-01 --http-01-port 54321 -d mysite.co.kr
위 명령어는 -d 로 지정한 특정 도메인 만을 갱신한다.
--갱신된 fullchain.pem와 privkey.pem 병합 및 /etc/haproxy/certs/ssl.pem 재생성
sudo bash -c "cat /etc/letsencrypt/live/mysite.co.kr/fullchain.pem /etc/letsencrypt/live/mysite.co.kr/privkey.pem > /etc/haproxy/certs/ssl.pem"
변경된 환경을 사용하도록 haproxy 다시 시작한다.
sudo service haproxy reload
crontab 에 job 으로 등록
Job에 등록할 스크립트를 다음과 같이 작성
$ vi le-renew-haproxy
#!/bin/bash
domain='mysite.co.kr'
exp_limit=30;
cert_file="/etc/letsencrypt/live/$domain/fullchain.pem"
key_file="/etc/letsencrypt/live/$domain/privkey.pem"
if [ ! -f $cert_file ]; then
echo "[ERROR] certificate file not found for domain $domain."
fi
exp=$(date -d "`openssl x509 -in $cert_file -text -noout|grep "Not After"|cut -c 25-`" +%s)
datenow=$(date -d "now" +%s)
days_exp=$(echo \( $exp - $datenow \) / 86400 |bc)
echo "checking expiration date for $domain..."
if [ "$days_exp" -gt "$exp_limit" ] ; then
echo "The certificate is up to date, no need for renewal ($days_exp days left)."
exit 0;
else
echo "the certificate for $domain will be updated"
/home/user/certbot-auto certonly --standalone --agree-tos --renew-by-default --standalone-supported-challenges http-01 --http-01-port 54321 -d $domain
echo "creating /etc/haproxy/certs/ssl.pem with latest certs"
sudo bash -c "cat /etc/letsencrypt/live/$domain/fullchain.pem /etc/letsencrypt/live/$domain/privkey.pem > /etc/haproxy/certs/ssl.pem"
echo "reloading haproxy service"
sudo service haproxy reload
fi
실행 권한 지정
$ sudo chmod +x le-renew-haproxy
crontab 등록
$ sudo crontab -e
다음과 같이 매주 월요일 오전 04시에 수행하도록 설정
# m h dom mon dow command
0 4 * * 1 /home/user/le-renew-haproxy >> /var/log/le-renew-proxy.log
구성 파일
global
log /dev/log local0
log /dev/log local1 notice
maxconn 8192
tune.ssl.default-dh-param 2048
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# Default ciphers to use on SSL-enabled listening sockets.
# For more information, see ciphers(1SSL). This list is from:
# https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
ssl-default-bind-options no-sslv3
defaults
log global
mode http
option forwardfor
option http-server-close
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
#HAProxy 상태 모니터링url
stats enable
stats uri /stats #상태모니터링 uri명
stats realm Haproxy\ Statistics
stats auth user:1111 #사용자 아이디/비번
frontend https-frontend
#bind *:443 ssl crt /etc/ssl/priavte/ssl.pem
bind *:443 ssl crt /etc/haproxy/certs/ssl.pem
reqadd X-Forwarded-Proto:\ https
acl letsencrypt-acl path_beg /.well-known/acme-challenge/
use_backend letsencrypt-backend if letsencrypt-acl
default_backend wwwbackend
backend wwwbackendd
redirect scheme https if !{ ssl_fc }
server www-1 127.0.0.1:8080 check
backend letsencrypt-backend
server letsencrypt 127.0.0.1:54321
frontend http-frontend
bind *:80
reqadd X-Forwarded-Proto:\ http
default_backend wwwbackend
Let's Encrypt 갱신 시크립트
#!/bin/bash
domain='mysite.co.kr'
exp_limit=30;
cert_file="/etc/letsencrypt/live/$domain/fullchain.pem"
key_file="/etc/letsencrypt/live/$domain/privkey.pem"
if [ ! -f $cert_file ]; then
echo "[ERROR] certificate file not found for domain $domain."
fi
exp=$(date -d "`openssl x509 -in $cert_file -text -noout|grep "Not After"|cut -c 25-`" +%s)
datenow=$(date -d "now" +%s)
days_exp=$(echo \( $exp - $datenow \) / 86400 |bc)
echo "checking expiration date for $domain..."
if [ "$days_exp" -gt "$exp_limit" ] ; then
echo "The certificate is up to date, no need for renewal ($days_exp days left)."
exit 0;
else
echo "the certificate for $domain will be updated"
/home/user/certbot-auto certonly --standalone --agree-tos --renew-by-default --standalone-supported-challenges http-01 --http-01-port 54321 -d $domain
echo "creating /etc/haproxy/certs/ssl.pem with latest certs"
sudo bash -c "cat /etc/letsencrypt/live/$domain/fullchain.pem /etc/letsencrypt/live/$domain/privkey.pem > /etc/haproxy/certs/ssl.pem"
echo "reloading haproxy service"
sudo service haproxy reload
fi
참조
https://certbot.eff.org/#ubuntutrusty-haproxy
https://certbot.eff.org/docs/using.html#renewal
https://www.digitalocean.com/community/tutorials/how-to-secure-haproxy-with-let-s-encrypt-on-ubuntu-14-04
https://www.haproxy.com/doc/aloha/7.0/haproxy/acls.html#id2
댓글 없음:
댓글 쓰기