git repo: https://github.com/depromeet/8th-final-infra-6team/tree/master/ansible
사이드 프로젝트를 진행하면서 EC2 인스턴스에 nginx + https 구성을 해야할 일이 매번 생기게 되었습니다.
route53, LB 등의 리소스를 사용하면 이런 구성을 수동으로 할 필요는 없지만 비용을 아끼기 위해서 public subnet 의 bastion host 에 nginx 를 같이 구성하여 클라이언트와 https 연동이 가능한 구조를 구성하기로 했습니다.
목표
- EC2 인스턴스 nginx 설치, 실행
- https 연동이 가능한 환경 구성
- domain 무료 발급
- certbot 으로 ssl 인증서 발급
- ansible 코드로 자동화
기술스택
- AWS
- ansible
- certbot
디렉토리 구조
EC2 인스턴스 nginx 설치, 실행
EC2 인스턴스를 생성하는 법에 대해서는 다루지 않겠지만, 저는 terraform 을 이용해서 관련된 리소스들을 생성했습니다.
ansible 코드를 반복 테스트하기 위해서 타겟 EC2 인스턴스에 EIP 를 연결해주시는 것이 좋습니다.
우선 ansible 연동을 위해 아래와 같은 inventory.ini 파일을 작성합니다.
# inventory.ini
[bastion]
bastion ansible_user=ec2-user ansible_host=<<YOUR_EC2_PUBLIC_IP>> ansible_ssh_private_key_file=~/.ssh/bastion
EC2 에 접근가능한 public ip 와 연결시에 사용할 ssh private key 를 적어줍니다.
ssh-keygen -t rsa -b 4096 -C "" -f "$HOME/.ssh/bastion" -N ""
ssh 연동을 위한 key pair 를 생성하고, pub key 를 AWS key_pair 리소스를 생성하면서 등록 후에 EC2 인스턴스를 생성시에 해당 key_pair 를 사용하도록 해줍니다.
$ ansible -m ping all -i inventory.ini -l all
bastion | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
ping 테스트를 통해 ansible 연동이 정상적인지 테스트해봅니다.
# init_nginx_with_https.yml
---
- hosts: all
gather_facts: no
remote_user: ec2-user
roles:
# - { role: certbot }
- { role: nginx }
max_fail_percentage: 0
serial: "{{ deploy_parallelism | default(1) | int }}"
# nginx/tasks/main.yml
---
- name: Install nginx
yum:
name: nginx
state: present
become: yes
- name: Copy configuration files
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: go-wx
become: yes
- name: Start service nginx
service:
name: nginx
state: started
become: yes
nginx 설치는 package manager 를 통해서 간단하게 진행합니다.
해당 코드를 운영 환경에서 사용하기에는 많은 것들을 더 고려해야합니다.
- conf 파일이 변경되었을때만 start, restart 혹은 reload
- offline 환경에서 설치해야 한다면 tar 파일로 전달해야함
- 운영상 필요한 디렉토리(설정, 로그 등) 권한 수정
ansible-playbook init_nginx_with_https.yml -i inventory.ini -l bastion -vv
명령어를 통해 설치를 진행합니다. 이때 nginx.conf.j2 파일에 ssl 설정이 이미 추가되어있다면 cert file 이 지금은 없기에 에러가 발생할 수 있습니다.
https 연동이 가능한 환경 구성
ssl 인증서 발급을 위해서는 실제 도메인이 필요합니다.
https://www.freenom.com/en/index.html?lang=en
저는 oauth 가입 시에 에러가 발생하여 email 계정으로 직접 가입하였습니다.
위와 같은 무료 도메인 발급 서비스를 이용하여 도메인을 준비합니다.
ssl 인증서 발급은 certbot 을 이용하여 진행합니다.
certbot 에 대해서 알아두어야할 내용들을 간략하게 정리해보면..
- amazon linux 1 과 amazon linux 2 에서의 certbot 설치, 사용 방법이 다릅니다. 저는 amazon linux 1 환경에서 진행합니다.
- amazon linux 1
- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/SSL-on-amazon-linux-ami.html#lets-encrypt-alami
- certbot-auto 파일을 인터넷 상에서 다운로드 받아 사용합니다.
- amazon linux 2
- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/SSL-on-amazon-linux-2.html#letsencrypt
- yum 을 통해 certbot, python2-certbot-apache 패키지를 다운로드 받아 사용
- amazon linux 1
- standalone / webroot
- certbot 이 인증서를 발급하면서 도메인을 실제로 소유하고 있는게 맞는지 확인하는 방법에 대한 구분입니다.
- standalone 방식을 사용하게 되면 certbot 프로그램이 해당 도메인의 웹서버를 자체적으로 띄워서 확인합니다.
- 80, 443 포트에 기존 웹서버가 떠있으면 안되기에 기존 웹서버를 종료,시작해줘야 하며 이때 서비스 중단이 일어납니다.
- webroot 방식은 기존에 떠있는 웹서버의 특정 경로에 certbot 이 인증 파일을 배포하게되고 특정 path 로 접근시에 인증파일이 확인 되어야 하는 방식입니다.
# roles/certbot/tasks/main.yml
---
- name: Enable epel
shell: yum-config-manager --enable epel
become: yes
changed_when: no
- name: Download certbot-auto
get_url:
url: https://dl.eff.org/certbot-auto
dest: "{{ certbot_auto_path }}/certbot-auto"
mode: 0776
- name: Check if certificate already exists
stat:
path: /etc/letsencrypt/live/{{ certbot_cert.domains | first | replace('*.', '') }}/cert.pem
become: yes
register: certbot_cert_result
- name: Check if nginx is running
shell: "{{ nginx_process_check_command }}"
register: nginx_process_result
failed_when: nginx_process_result.stderr != ""
changed_when: no
- name: Stop nginx to allow certbot to generate a cert
service:
name: nginx
state: stopped
become: yes
when: not certbot_cert_result.stat.exists and
nginx_process_result.stdout | length == 1
- name: Generate new certificate if one doesn't exist
command: |
./certbot-auto certonly --standalone --noninteractive --agree-tos
--email {{ certbot_cert.email }}
-d {{ certbot_cert.domains | join(',') }}
--debug
args:
chdir: "{{ certbot_auto_path }}"
become: yes
when: not certbot_cert_result.stat.exists
- name: Start services after cert has been generated
service:
name: nginx
state: started
become: yes
when: not certbot_cert_result.stat.exists and
nginx_process_result.stdout | length == 1
- name: Add cron job for certbot renewal (if configured)
cron:
name: Certbot automatic renewal.
job: "{{ certbot_auto_path }}/certbot-auto renew {{ certbot_auto_renew_options }}"
day: "{{ certbot_auto_renew_day }}"
user: "{{ certbot_auto_user }}"
when: certbot_auto_renew == true
AWS의 Amazon linux 1 에서의 설치 가이드를 참고하여 위와 같은 task 들을 작성했습니다.
- certbot-auto 파일 다운로드
- standalone 방식을 위해서 기존 nginx process 확인하고 종료
- noninteractive 방식으로 인증서 발급
- 이때 --debug 옵션, 혹은 --no-bootstrap 옵션을 주지 않으면 실패하게 됩니다.
- 보안적으로 위험하다는 경고 메시지가 출력되는데, 실제 서비스 운영 시에는 어떤 메시지인지 확인이 필요해보입니다.
- nginx process 시작
- 갱신을 위한 cron job 등록
playbook 에 주석처리되었던 certbot 항목을 주석해제하고 다시 명령어를 수행합니다.
ansible-playbook init_nginx_with_https.yml -i inventory.ini -l bastion -vv
도메인 주소로 https 로 접근 시에 인증서가 발급된 것을 확인할 수 있습니다.
'ansible' 카테고리의 다른 글
RedHat - DO407 Automation with Ansible 교육 후기 (2) | 2020.04.23 |
---|