AWS Route 53을 이용해 도메인을 사용하고 있다.
문제는 ACM으로 AWS외부의 자체 구축 서버에 인증서 적용이 안된다는 것이다.
(혹시 방법이 있다면 알려주세요 ㅠㅠ)
그래서 결국 자체적으로 SSL 인증을 하는 형태로 구축해야 되기에 Mac에서 SSL 인증하는 방법을 포스팅 해볼까 한다.
외부아이피 확인
우선 https가 아닌 http로 서버에 접근을 가능하게 먼저 설정을 하고 SSL를 적용할 것이다.
Route 53 레코드 생성
이제 AWS Route 53 > 도메인 선택 > 레코드 생성을 누른다.
(다른 도메인 서비스에서도 마찬가지로 A 타입 레코드를 생성하면 된다.)
레코드 이름과 값에 외부 아이피를 설정하고 레코드를 생성하면 된다.
Nginx 설치
공유기 설정 전, Nginx를 설치하려고 한다.
이유는 외부에서 요청을 보냈을 때 연결이 잘 되었는지 쉽게 확인할 방법이 없기 때문...
그래서 특정 요청에서 특정 값을 반환하여 요청과 응답이 잘 오고가는지 확인하게 만들것이다.
일단 nginx를 편히 설치하기 위해서 Homebrew가 필요하다.
아래 명령어를 터미널에 작성하여 설치하면 된다.
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
이미 설치되신 분들은 Homebrew를 한번 업데이트 하시는 것을 추천드립니다.
brew update
이제 아래 명령어로 Nginx를 설치해줍니다.
brew install nginx
설치가 완료 되면 아래 명령어로 Nginx를 실행시킵니다.
sudo nginx
설치 결과에 nginx가 8080포트에 할당되었기 때문에 localhost:8080 으로 접근하여 확인해봅니다.
이런 페이지가 확인된다면 nginx는 성공적으로 적용된것이다.
Nginx 테스트 설정
위 설치 결과에 나왔던 nginx.conf 디렉토리 위치까지 이동하고 안에 내용물을 확인해보자.
cd /opt/homebrew/etc/nginx
ls
nginx.conf에 대해 약간의 수정을 할 것이다.
편하신 방법으로 수정을 진행하시면 될 것 같습니다.
텍스트 편집기로 수정
open .
해당 폴더가 열리면 nginx.conf 파일을 텍스트 편집기나 vscode와 같은 편하신 편집기로 열어주시면 됩니다.
vim으로 수정
sudo vim nginx.conf
위 명령어로 vim으로 파일을 여시고 키보드 "i" 버튼을 눌러 INSERT모드로 변경하시면 수정이 가능합니다.
(저장은 ESC(모드종료) > :wq 입력 (write & quit) > Enter)
수정내용
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 8080;
server_name localhost;
location / {
return 200 "Hello World!";
}
}
include servers/*;
}
http에서 부분에서 필요없는 곳을 다 날리고 해당 내용만 남겼다.
location / { return 200 "Hello World!"};
여기서 주요 포인트는 위 코드로 진입 성공 여부를 확인할 것이다.
이와 같이 변경 되었다면 nginx에 변경된 사항을 반영한다.
# nginx 문법 검사
sudo nginx -t
# nginx Reload
sudo nginx -s reload
# [Reload 실패 시]
sudo nginx -s stop
sudo nginx
이유는 모르겠지만 reload가 제대로 적용이 안될 때가 많다.
회사에서 ubuntu 환경의 nginx를 다룰 때도 reload가 안먹어서 항상 systemctl restart를 진행한다... ㅠㅠ
때문에 reload를 했는데 뭔가 적용이 안되는 것 같다면 수동으로 껏다 켜보자.
이제 localhost:8080으로 진입하면 "Hello World!"가 적힌 파일이 다운로드 되어질 것이다.
공유기 포트 포워딩
개개인의 사용하는 공유기가 다르기 때문에 방법적인 이야기만 하려고 한다.
간단하게 포트포워딩 설정에서 80포트(외부)로 들어오는 데이터를 8080포트(내부)으로 전달해주기만 하면 된다.
외부 접근 확인
이제 본인이 설정했던 주소로 접근을 시도해본다.
localhost:8080으로 요청했을 때와 동일하게 api.limseonghyeon.com으로 접근했을 때 Hello World! 파일을 응답한다.
SSL 인증서 발급
Let's Encrypt라는 TLS 인증서를 제공하는 비영리 인증 기관을 이용할 것이다.
우선 Homebrew를 통해 certbot을 설치한다.
brew install certbot
그리고 설치된 certbot으로 인증서를 발급받는다.
sudo certbot certonly --manual
이후 설치 중 이메일을 입력하라고 한다. 입력해주자.
약관에 동의하는지 물어본다. y를 눌러 수락하자.
다른 약관에 동의하는지 물어본다. y를 눌러 수락하자.
도메인 이름을 물어본다. 적어주도록 하자. ( >> api.limseonghyeon.com)
[중요]
인증서를 발급받기 위해 진짜 이 도메인의 소유주인지 확인하기 위한 첼린지를 진행해야 한다.
그래서 아래쪽에 제시해준 URL에 접근했을 때, 위에 제시해준 값이 return 되어야 한다.
많은 방법이 있겠지만 나는 nginx에서 고정된 경로에 접근할 때, 해당 값을 반환해주는 형식으로 적용하고자 한다.
다시 한번 nginx.conf를 편집한다.
server {
listen 8080;
server_name localhost;
location / {
return 200 "Hello World!";
}
location = /.well-known/acme-challenge/[CHALLENGE KEY] {
return 200 "[CHALLENGE VALUE]";
}
}
위에서 제시해준 경로에 값을 반환하게 설정해주면 된다.
sudo nginx -s stop
sudo nginx
nginx를 재시작하여 변경사항을 적용해주자.
그리고 해당 경로에 접속하여 값을 올바르게 전달하는지 확인한다.
전달 되는지 확인이 되었다면 엔터를 눌러 첼린지 설정이 완료되었다고 알리자.
그러면 자동 갱신에 대한 가이드 라인과 함께 완료 메세지가 나온다.
이때 인증서의 위치를 반드시 확인하자.
다시 한번 nginx를 수정한다.
server {
listen 80;
server_name [DOMAIN];
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name [DOMAIN];
ssl_certificate /etc/letsencrypt/live/[DOMAIN]/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/[DOMAIN]/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
return 200 "Hello World!";
}
location = /.well-known/acme-challenge/[CHALLENGE KEY] {
return 200 "[CHALENGE VALUE]";
}
}
80으로 들어오는 요청은 https를 붙여서 redirection시키고, 443요청에 대한 SSL 정보들을 추가한다.
그리고 다시 nginx에 변경사항을 적용해주자.
sudo nginx -t
sudo nginx -s stop
sudo nginx
[오류 발생시]
1. options-ssl-nginx.conf에 대한 오류가 발생하면 해당 명령어로 options-ssl-nginx.conf를 설치한다.
# 오류 메세지
nginx: [emerg] open() "/etc/letsencrypt/options-ssl-nginx.conf" failed (2: No such file or directory) in /opt/homebrew/etc/nginx/nginx.conf:50
sudo curl -o /etc/letsencrypt/options-ssl-nginx.conf https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/nginx/options-ssl-nginx.conf
2. ssl-dhparams.pem에 대한 오류가 발생하면 해당 명령어로 ssl-dhparams.pem를 생성한다.
sudo openssl dhparam -out /etc/letsencrypt/ssl-dhparams.pem 2048
3. /opt/homebrew/var/~에 대한 Permission denied가 일어나면 아래 명령어로 권한을 습득한다.
# 오류 내용
>> nginx -t
nginx: [alert] could not open error log file: open() "/opt/homebrew/var/log/nginx/error.log" failed (13: Permission denied)
nginx: the configuration file /opt/homebrew/etc/nginx/nginx.conf syntax is ok
2024/10/20 20:48:43 [emerg] 14879#0: open() "/opt/homebrew/var/run/nginx.pid" failed (13: Permission denied)
nginx: configuration file /opt/homebrew/etc/nginx/nginx.conf test failed
sudo chown -R $(whoami):wheel /opt/homebrew/var/log/nginx
sudo chmod -R 755 /opt/homebrew/var/log/nginx
sudo chown -R $(whoami):wheel /opt/homebrew/var/run/nginx.pid
sudo chmod -R 755 /opt/homebrew/var/run/
다시 공유기 포트포워딩 설정으로 돌아가 80포트 → 8080포트로 포트포워딩 해줬던 것을
80 포트 → 80포트
443 포트 → 443포트로 두가지를 설정해주자.
여기가지 성공했다면 SSL에 대한 적용은 끝이다.
인증서 자동 갱신
MacOS에서는 crontab을 이용해서 특정 주기마다 특정 스크립트를 실행할 수 있다.
sudo crontab -e
위 명령어를 터미널에 작성하고 crontab을 등록해보자.
i를 눌러서 INSERT MODE로 진입하여 아래와 같이 작성한다.
* * * * * /bin/echo "Cron job is running" >> /Users/mac/cron_test.log
esc > :wq > enter로 작성 내용을 저장한다.
ps aux | grep cron
위 명령어를 입력해서 실행중인 cron이 있는지 확인한다.
cat /Users/mac/cron_test.log
위 명령어를 입력해서 "Cron job is running"이라는 문구가 추가되는지 확인한다.
1분에 한번 작성되기에 2번 작성된게 확인되면 cron도 잘 돌고 있음을 알 수 있다.
자 이제 test 스크립트를 제거하고 인증서를 갱신하는 스크립트로 치환한다.
crontab에 긴 스크립트를 덕지덕지 붙이기엔 무리가 있으므로 sh파일을 하나 만들어서 등록한다.
sudo vim /usr/local/bin/update_api.limseonghyeon.com_SSL.sh
혹시 나중에 추가적으로 인증서를 발급할 수 있으니 도메인으로 구분해두었다.
마찬가지로 i를 눌러 INSERT MODE로 진입한다.
#!/bin.sh
LOG_FILE_PATH="$HOME/log/certbot-renew.log"
timestamp() {
date +"%Y-%m-%d %T"
}
sudo mkdir -p "$HOME/log"
chmod 755 $HOME/log
echo "[$(timestamp)] Starting certificate renewal process..." >> $LOG_FILE_PATH
echo "[$(timestamp)] stop nginx service." >> $LOG_FILE_PATH
sudo nginx -s stop
echo "[$(timestamp)] Request certificate renewal." >> $LOG_FILE_PATH
sudo certbot renew >> $LOG_FILE_PATH
echo "[$(timestamp)] Clean 80 & 443 port." >> $LOG_FILE_PATH
sudo lsof -ti tcp:80 | xargs kill -9
sudo lsof -ti tcp:443 | xargs kill -9
if [ $? -eq 0 ]; then
echo "[$(timestamp)] Certificate renewal succeeded." >> $LOG_FILE_PATH
else
echo "[$(timestamp)] Certificate renewal failed!" >> $LOG_FILE_PATH
fi
echo "[$(timestamp)] Start nginx." >> $LOG_FILE_PATH
sudo nginx
echo "[$(timestamp)] Proccess finished.\n" >> $LOG_FILE_PATH
위 내용을 입력하고 esc > :wq > enter를 눌러 저장한다.
sudo sh update_api.limseonghyeon.com_SSL.sh
위 명령어로 수행이 잘 되는지 확인해봅니다.
이제 크론에 테스트 했던 스크립트를 제거하고 해당 스크립트를 실행해주기만 하면 된다.
sudo crontab -e
마찬가지로 i를 눌러 INSERT MODE에 진입 후
30 4 * * 0 sudo sh /usr/local/bin/update_api.limseonghyeon.com_SSL.sh
위 스크립트로 바꿔 esc > :wq > enter로 저장하면 끝입니다.
후기
매번 AWS Route53 - ELB를 통해서 쉽게 쉽게 연결했었는데 AWS 비용도 비용이고 간단한 프로젝트로 홈서버 구축을 위해 SSL인증을 진행해보니 참 깨달은게 많습니다.
Mac에서 cron설정이 가능하다는 것도 처음 알았구요 ㅋㅋㅋ
정확하게 실행히 되는지는 3달 뒤에 확실히 알거 같아 그때가 되면 한번 로그파일 확인하고 추가로 적도록 하겠습니다.
진행중에 더 좋은 방법이 있거나 수정점이 있다면, 지적 혹은 질문 환영입니다!