웹서버를 구축할 때 직면하는 문제 중 하나가 보안에 관한 것이다.
웹 프로토콜은 HTTP 와 HTTPS 가 있고 이중 HTTPS 가 보안 인증된 프로토콜이다.
번외로, SSL 은 인증 오래된 보안 인증이고 지금은 SSL 3.0을 기반으로 TLS 로 발전해서 현재는 TLS로만 사용되고 있지만 워낙 SSL이름이 유명해서 그냥 SSL이라고 부르기도 하는 거란다.
SSL 보안인증을 수행하기 위해서는 웹서버에서 공인인증된 SSL 인증서를 구축해야 하나 비용과 저사양 서버를 사용할 때의 SSL 보안 동작 부하를 줄이기 위해서 Cloudflare 를 활용 할 수 있다.
1. 웹 데이터 기본 흐름
클라이언트 (웹브라우저) 는 브라우저에 사이트 주소를 입력하여 접속한다. 이때 사이트 이름을 도메인이라고 하고 도메인은 실제 구동하고 있는 웹서버가 있는 IP 주소지로 대신 연결해주는 역할을 한다.
이를 도메인 서버 (DNS)가 수행하는 것이고 우리는 cloudflare를 DNS 로 설정함으로써 SSL 보안 인증으로 웹사이트를 사용할 수 있도록 한다.
2. Cloudflare 역할 및 설정
Cloudflare 는 DNS 서버로 동작하여 클라이언트의 웹사이트 접속 요청을 받는다. 이 때 HTTPS 로 접속할 경우에는 cloudflare 에서 SSL 인증을 수행하여 클라이언트와 Cloudflare 간의 트래픽은 암호화하여 송수신하게 된다.
하지만, cloudflare 와 웹서버간의 트래픽은 웹서버의 SSL 인증을 통해서 보안되어야 한다.
그렇기에 웹서버의 환경에 따라서 cloudflare 의 설정이 중요하게 된다.
2.1 DNS 프록싱 설정
대시보드 -> DNS 관리 에서 Cloudflare 를 통해 프록시 상태를 설정할 수 있다.
- 프록싱 활성화(주황색 구름): 클라이언트가 도메인으로 접속하면, Cloudflare를 통해 요청이 프록싱되어 웹서버로 전달됩니다. Cloudflare의 보안, 캐싱, DDoS 방어 기능을 사용할 수 있습니다.
- 프록싱 비활성화(회색 구름): 클라이언트는 Cloudflare를 거치지 않고 직접 웹서버에 연결됩니다. 이 경우, 웹서버 자체에서 보안을 처리해야 합니다.
Cloudflare 는 루트 도메인 및 서브 도메인별 프록시 설정이 가능하다.
2.2 SSL 암호화 모드 설정
대시보드 -> SSL/TLS -> 구성 에서 SSL 암호화 모드의 설정에 따라서 Cloudflare 와 웹서버 간의 암호화 설정이 달라진다.
*여기에서 전제는 기본적으로 클라이언트와 Cloudflare 간의 SSL 암호화는 이루어진 상태이다.
SSL 암호화 설정에 따라서 웹서버의 암호화 인증 구성이 요구된다.
- 가변(Flxible SSL):
- 클라이언트와 Cloudflare 간의 연결은 HTTPS로 암호화되지만, Cloudflare와 서버 간의 연결은 암호화되지 않은 HTTP로 이루어 진다.
- 웹서버에서의 SSL 암호화 인증이 요구되지 않는다.
- Cloudflare 와 웹서버간의 암호화를 수행하지 않는다.
- 기본 HTTP 통신으로만 연결을 수행한다.
- https(443), http(80) 접속은 모두 웹서버에 http(80) 포트로 접속이 된다.
- 따라서 웹서버의 443 포트에 대한 proxy 가 적용되지 않는다.
- 특정 포트의 접속은 웹서버로 그대로 전달된다. - 전체 (Full SSL / Full SSL(Strict)):
- 클라이언트와 Cloudflare, Cloudflare와 서버 간의 연결이 모두 암호화된다.
- https(443) 접속이 웹서버에 https(443) 포트로 접속이 된다.
- 따라서 웹서버의 443 포트에 대한 proxy 가 적용된다.
- Full SSL : 서버에 SSL 인증서가 필요하지만, 이 인증서가 자체 서명된 것이든 공인된 CA에서 발급된 것이든 상관없다
- Full SSL(Strcit) : 공인된 SSL 인증서를 사용해야 한다. 무료의 경우 Let's Encrypt 인증서를 사용하면 된다.
2.3 Always HTTPS Use
- 클라이언트에서의 모든 HTTP 접속을 HTTPS 으로 리디렉션해서 전달한다.
- http(80) 접속을 https(443) 접속으로 전환한다.
- http 의 특수 포트로의 접속 또한 https(443) 접속으로 전환한다.
- https 의 특수 포트 접속은 그대로 유지하여 전달한다.
2.4 Cloudflare 프록시 허용 포트 목록
각 프로토콜 별 허용된 프로토콜을 제외한 모든 접속은 Cloudflare 에서 차단된다. 만약 이외의 포트를 사용하려면 DNS 관리에서 프록시 설정을 DNS only 로 변경해야 한다.
- HTTP: 80, 8080, 8880
- HTTPS: 443, 2053, 2083, 2087, 2096, 8443
3. 웹서버 SSL 설정하기
웹서버에서 SSL 인증을 하기 위해서는 Let's Encrypt 와 같은 인증기관을 통해서 인증서를 발급해야 한다.
이때 서브 도메인이 있는 경우에는 루트 도메인과 함께 서브 도메인을 포함해서 인증서를 발급해야 한다.
sudo certbot --nginx -d mydomain.com -d www.mydomain.com -d auto.mydomain.com
모든 서브 도메인을 자동 포함 하기 위해서는 Wildcard SSL 인증서를 사용한다.
sudo certbot -d "*.mydomain.com" --manual --preferred-challenges dns certonly
하지만 Wildcard SSL 인증서 발급을 위해서는 DNS-01 인증 방식이 필요해서 그냥 발급 되지 않고 처음에는 에러가 발생한다. 내용을 보면 도메인의 소유를 확인하기 위해서 TXT 레코드를 추가하라고 나온다.
에러 메시지에 나온 문자열을 DNS 네임서버에 TXT 레코드를 적용해야 한다.
그리고 TXT 레코드가 잘 적용되었는지 확인한다.
dig TXT _acme-challenge.mydomain.com
그리고 다시 certbot 을 실행하는데, 처음에 등록하고 나서 인증 DNS 서버에서 이를 갱신하기까지 시간이 필요하다. 몇 분에서 한시간정도라는데 알수 없다.
4. Nginx 프록시 설정
웹서버에서 Next.js 웹서버를 구동한다는 가정을 했을 때 이를 next.js 의 3000포트로 proxy 하는 예제이다.
Cloudflare 에서 Full SSL 암호화 설정이 되어 있다면, http 와 https 접속을 구분해서 proxy 할 수 있다.
만약 Flexible SSL + Always HTTP Use(설정무관) 일 경우에는 80 포트만 적용되고, Always HTTPS Use + Full SSL 를 설정했으면 443 포트로 연결된다.
server {
listen 80; # HTTP를 통한 접속만 허용
server_name mydomain.com www.mydomain.com; # 도메인 이름 설정
location / {
proxy_pass http://localhost:3000; # Next.js가 구동 중인 포트
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
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;
}
}
server {
listen 443 ssl;
server_name mydomain.com www.mydomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://localhost:3000; # Next.js 서버 포트
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
}
}