본문 바로가기
ansible

AWS EC2 + nginx + https 환경 구성 자동화 (ansible + certbot)

by tango0415 2020. 10. 5.

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 을 이용하여 진행합니다.

https://certbot.eff.org/docs/using.html

certbot 에 대해서 알아두어야할 내용들을 간략하게 정리해보면..

  • amazon linux 1 과 amazon linux 2 에서의 certbot 설치, 사용 방법이 다릅니다. 저는 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