티스토리 뷰
CloudNet@의 가시다님 Ansible 1기 스터디에 참여하게 되어 배운 내용과 책의 내용을 함께 정리합니다.
📚 앤서블로 시작하는 인프라 자동화
1. 보안 설정 자동화
1.1. 패스워드 변경 주기 설정
사전 분석
- 패스워드 변경 주기를 설정할 대상 호스트는 인벤토리를 통해 설정합니다.
- 패스워드 변경 주기를 설정할 사용자 계정 정보와 최대 변경일은 변수를 통해 별도의 파일로 정의합니다.
- 패스워드 변경 주기 설정은 ansible.builtin.user 모듈을 이용합니다.
플레이북 설계
- 사용자 계정과 최대 변경일을 변수로 설정하기 위해 vars_maxdays.yml 파일을 생성합니다.
- 메인 플레이북 set_chage_password.yml 파일에는 변경 주기를 설정할 태스트가 포함됩니다.
플레이북 개발 및 실행
1. 프로젝트 디렉토리를 생성하고 ansible.cfg, inventory 파일을 작성합니다.
~/my-ansible/chapter_11.1
#
mkdir ~/my-ansible/chapter_11.1
cd ~/my-ansible/chapter_11.1
# ansible.cfg, inventory 파일 작성
cat <<EOT> ansible.cfg
[defaults]
inventory = ./inventory
remote_user = ubuntu
ask_pass = false
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
EOT
cat <<EOT> inventory
[tnode]
tnode1
tnode2
tnode3
EOT
2. 사전형 변수 파일을 작성합니다.
~/my-ansible/chapter_11.1/vars_maxdays.yml
---
Userinfo:
- username: ansible
maxdays: 90
- username: stack
maxdays: 90
3. 메인 플레이북을 작성합니다. (패스워드 변경 주기는 user 모듈의 password_expire_max 파라미터로 설정합니다.)
~/my-ansible/chapter_11.1/set_chage_password.yml
---
- hosts: tnode
vars_files: vars_maxdays.yml
tasks:
- name: Change Password Maxdays
ansible.builtin.user:
name: "{{ item.username }}"
password_expire_max: "{{ item.maxdays }}"
loop: "{{ Userinfo }}"
4. 리눅스에서 명령어의 용도가 궁금할때는 man <COMMAND>를 입력해 확인할 수 있습니다.
- chage는 사용자의 패스워드 만기 정보를 변경 및 설정하는 명령어입니다. (root 권한을 가진 사용자만 사용 가능)
5. 플레이북을 실행합니다.
5. 실행한 결과를 확인해보면 각 노드의 ansible 계정마다 잘 적용된 것을 확인할 수 있습니다.
1.2. 패스워드 생성 법칙 적용
사전 분석
- 패스워드 생성 법칙 적용을 위해서는 pwquality.conf 라는 pam 설정 파일을 이용해야 하며, 리눅스 서버에 libpam-pwquality 패키지가 있어야 합니다.
- 패스워드 변경 주기는 /etc/securiy/pwquality.conf 파일로 설정합니다.
- /etc/securiy/pwquality.conf 파일을 설정하기 전에 원본 파일을 백업받습니다.
- pwquality.conf 파일은 사용자가 커스텀으로 설정하는 파라미터들로 구성되어 있으며, Jinja2 템플릿 방식으로 구현합니다.
- 파라미터들은 별도의 변수 설정 파일에서 정의한 파라미터 값으로 사용하고, 파라미터 정의 여부를 체크하여 pwquality.conf 파일의 내용을 구성합니다.
플레이북 설계
- 변수 vars_pw_rule.yml 에 정의합니다.
- 최소 패스워드 길이 설정 minlen
- 최소 숫자 개수 설정 dcredit
- 최소 대문자 개수 설정 ucredit
- 최소 소문자 개수 설정 lcredit
- 최소 특수문자 개수 설정 ocredit
- root 계정에서도 해당 패스워드 룰을 설정할 수 있는 enforce_for_root
- Jinja2 템플릿 파일 pwqulity.conf.j2 아래 내용 포함
- minlen, dcredit, ucredit, lcredit, orcedit, enforce_for_root
- minclass : 최소 문자클래스 개수
- maxrepeat : 최대 연속 동일 문자 수
- maxclassrepeat : 동일 글래스의 최대 연속 문자 수
플레이북 개발 및 실행
1. 프로젝트 디렉토리를 생성하고 ansible.cfg, inventory 파일을 작성합니다.
~/my-ansible/chapter_11.2
#
mkdir ~/my-ansible/chapter_11.2
cd ~/my-ansible/chapter_11.2
# ansible.cfg, inventory 파일 작성
cat <<EOT> ansible.cfg
[defaults]
inventory = ./inventory
remote_user = ubuntu
ask_pass = false
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
EOT
cat <<EOT> inventory
[tnode]
tnode1
tnode2
tnode3
EOT
2. 변수 값을 설정합니다.
~/my-ansible/chapter_11.2/vars_pw_rule.yml
---
minlen: 8
dcredit: -1
ucredit: -1
lcredit: -1
ocredit: -1
# default 값은 true지만 실습의 편의성을 위해 false로 적용
enforce_for_root: false
3. Jinja2 템플릿 파일 pwquality.conf.j2을 작성합니다.
- {% ~ %} 사이에 제어문 구문이 위치합니다.
- {% if minlen is defined %} 구문 : minlen 이라는 변수가 정의되면 아래 문장을 삽입하라는 의미입니다.
- {% if 변수 is defined %} ~ {% endif %} 구문을 사용하여 파라미터와 관련된 변수가 선언되면 해당 파라미터를 삽입합니다.
~/my-ansible/chapter_11.2/pwquality.conf.j2
# Created by ansible
{% if minlen is defined %}
# Minimum acceptable size for the new password
minlen = {{ minlen }}
{% endif %}
{% if dcredit is defined %}
# The maximum credit for having digits in the new password
dcredit = {{ dcredit }}
{% endif %}
{% if ucredit is defined %}
# The maximum credit for having uppercase characters in the new password
ucredit = {{ ucredit }}
{% endif %}
{% if lcredit is defined %}
# The maximum credit for having lowercase characters in the new password
lcredit = {{ lcredit }}
{% endif %}
{% if ocredit is defined %}
# The maximum credit for having other characters in the new password
ocredit = {{ ocredit }}
{% endif %}
{% if minclass is defined %}
# The minimum number of required classes of characters for the new password
minclass = {{ minclass }}
{% endif %}
{% if maxrepeat is defined %}
# The maximum number of allowed consecutive same characters in the new password
maxrepeat = {{ maxrepeat}}
{% endif %}
{% if maxclassrepeat is defined %}
# The maximum number of allowed consecutive characters of the same class in the new password
maxclassrepeat = {{ maxclassreapt }}
{% endif %}
{% if retry is defined %}
# Prompt user at most N times before returning with error
retry = {{ retry }}
{% endif %}
{% if enforce_for_root is defined %}
# Enforces pwquality checks on the root user password.
enforce_for_root
{% endif %}
4. 메인 플레이북을 작성합니다. when 구문으로 데비안(우분투)의 경우 apt 패키지를 설치합니다. 이후 copy로 백업 후, template으로 템플릿 파일을 복사합니다.
~/my-ansible/chapter_11.2/set_password_rule.yml
---
- hosts: tnode
vars_files: vars_pw_rule.yml
tasks:
- name: Install libpam-pwquality
ansible.builtin.apt:
name: libpam-pwquality
state: present
when: ansible_facts.os_family == "Debian"
- name: Backup pwquality.conf
ansible.builtin.copy:
src: /etc/security/pwquality.conf
dest: /etc/security/pwquality.conf.bak
# default 값은 no지만 그럴 경우 로컬에서 파일 복사
remote_src: yes
- name: Copy pwquality.conf.j2 at /etc/security
ansible.builtin.template:
src: pwquality.conf.j2
dest: /etc/security/pwquality.conf
mode: '0644'
5. 플레이북을 실행하기 전에 패스워드 생성 법칙이 설정되어 있는지 체크해보면 설정되어 있지 않은 것을 확인할 수 있습니다.
6. 플레이북을 실행합니다.
7. 패스워드 생성 법칙이 적용된 것은 파일의 생성 유무에 따라 확인할 수 있습니다.
8. 생성된 conf파일을 확인해보면 변수 파일에 지정해놓은 내용이 적용되어 있는 것을 확인할 수 있습니다.
9. 실제로 적용되어 있는지 노드에 접속해 확인해봅니다.
1.3. 디렉토리 및 파일 접근 권한 변경
상황
- 리눅스 보안 중에 꼭 확인해야 하는 항목이 바로 Sticky bit 설정 파일과 World Writable 설정 파일입니다.
- Sticky bit 설정 파일은 리눅스에서 파일 소유자나 그룹 소유자만 해당 파일을 읽고 쓰고 삭제할 수 있도록 권한을 부여한 것을 의미합니다.
- 파일 소유자에게 권한을 부여하면 SUID, 파일 그룹에게 권한을 부여하면 SGID, 다른 사람에게 권한을 부여하면 Sticky bit 라고 합니다.
- Sticky bit가 적용된 파일 목록 중 보안을 위해 이를 적용하면 안 되는 파일 목록들이 있습니다.
- Sticky bit가 적용된 파일의 권한을 수정할 때는 적용되면 안 되는 파일인지 반드시 먼저 확인합니다. 보안 관련 가이드 문서 참고
- Sticky bit가 설정된 디렉토리 내에서는 (/tmp와 /var/tmp의 기본 퍼미션은 777입니다.)
- 퍼미션이 777인 파일에 대해서 파일의 소유자만이 삭제를 할 수 있습니다. (수정이나 실행, 읽기는 모두 허용됩니다.)
- Sticky bit가 설정된 디렉토리 자체도 소유자만이 삭제할 수 있습니다.
- root 계정은 모두 할 수 있습니다.
- World Writable 파일은 모든 사용자에게 파일을 읽고 쓸 수 있는 권한이 부여된 파일을 의미합니다.
사전 분석
- Sticky bit 파일 검색 명령어: find / -xdev -perm -04000 -o -perm -02000 -o -perm -01000
- World Writable 파일 검색 명령어: find / -xdev -type f -perm -2
- ansible.builtin.shell 모듈을 이용하여 Sticky bit 파일과 World Writable 파일을 찾습니다.
- 찾은 파일 목록은 ansible.builtin.file 모듈을 이용하여 파일의 접속 권한을 설정합니다.
- Sticky bit 파일은 u-s, g-s, o-s 로 설정하고, World Writable 파일은 o-w 로 설정합니다.
플레이북 설계
플레이북 개발 및 실행
1. 프로젝트 디렉토리를 생성하고 ansible.cfg, inventory 파일을 작성합니다.
~/my-ansible/chapter_11.3
#
mkdir ~/my-ansible/chapter_11.3
cd ~/my-ansible/chapter_11.3
# ansible.cfg, inventory 파일 작성
cat <<EOT> ansible.cfg
[defaults]
inventory = ./inventory
remote_user = ubuntu
ask_pass = false
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
EOT
cat <<EOT> inventory
[tnode]
tnode1
tnode2
tnode3
EOT
2. 태스크 파일을 작성합니다. 검색 시 grep 으로 필터링(-e 정규식 패턴, -v 매칭되지 않는 경우)
~/my-ansible/chapter_11.3/set_sticky_writable_files.yml
---
- hosts: tnode
tasks:
- name: Find Sticky bit files
ansible.builtin.shell: |
find / -xdev -perm -04000 -o -perm -02000 -o -perm 01000 \
| grep -e 'dump$' \
-e 'lp*-lpd$' \
-e 'newgrp$' \
-e 'restore$' \
-e 'at$' \
-e 'traceroute$' | xargs ls
register: sfile_list
- name: Find World Writable files
ansible.builtin.shell: |
find / -xdev -perm -2 -ls \
| grep -v 'l..........' | awk '{print $NF}'
register: wfile_list
- name: Print Sticky bit files
ansible.builtin.debug:
msg: "{{ sfile_list.stdout_lines }}"
- name: Print World Writable files
ansible.builtin.debug:
msg: "{{ wfile_list.stdout_lines }}"
- name: Set Sticky bit files
ansible.builtin.file:
path: "{{ item }}"
mode: "u-s,g-s,o-s"
loop: "{{ sfile_list.stdout_lines }}"
- name: Set World Writable files
ansible.builtin.file:
path: "{{ item }}"
mode: "o-w"
loop: "{{ wfile_list.stdout_lines }}"
3. 플레이북 실행 전에 파일들의 권한을 살펴보면 Sticky bit와 World Writable 설정이 되어있는 파일을 확인할 수 있습니다.
4. 플레이북을 실행합니다.
5. 파일을 다시 살펴보면 Sticky bit와 World Writable 설정이 빠진 것을 확인할 수 있습니다.
1.4. 사설 인증서 생성
사전 분석
- 사설 인증서에는 자체 서명된 인증 기관용 인증서와 해당 인증서를 이용해 만든 클라이언트 인증 키가 있습니다.
- 인증서를 만들 때는 CSR(Certificate Signing Request) 이라는 인증 서명 요청을 합니다.
- CSR을 통해 인증 기관용 인증서와 클라이언트 인증 키를 생성합니다.
- 앤서블에서 개인 키를 만들 때는 community.crypto.openssl_privatekey 모듈을 사용합니다. - Link
- CSR을 만들 때는 community.crypto.openssl_csr_pipe 모듈을 사용합니다. - Link
- CSR을 이용하여 인증서를 생성할 때는 community.crypto.x509_certificate 모듈을 사용 - Link
플레이북 설계
- tnode1에 사설 인증서를 생성하기 위해 inventory에 rootCA 호스트 그룹을 만들고 그룹 멤버로 tnode1 노드를 추가합니다.
- 인증서 생성에 필요한 값들은 vars_ssltls.yml 파일을 통해 외부 변수로 정의합니다.
- 메인 플레이북인 make_certification.yml 에는 become을 false로 설정하여 root 계정이 아닌 ubuntu 계정으로 실행되도록 합니다.
- pre_tasks 키워드를 사용하여 롤이 실행되기 전에 먼저 태스크가 실행되도록 하였으며, 인증서 생성을 위한 디렉토리를 생성하는 태스크를 추가합니다.
- 인증서 생성은 rootCA 인증서와 serverKey로 나뉘며, 해당 인증서 생성은 각각 롤을 생성하여 처리하도록 설계합니다.
- rootCA 롤과 serverKey 롤은 각각 ‘개인 키 생성 → CSR (인증 서명 요청) 생성 → CSR을 이용한 인증서 생성’ 태스크로 구성되며, serverKey 롤에서 CSR 인증서를 생성할 때는 이미 사전에 생성된 rootCA로부터 발급받아 생성하는 방식을 사용합니다.
플레이북 개발 및 실행
1. 프로젝트 디렉토리를 생성하고 ansible.cfg, inventory 파일을 작성합니다.
~/my-ansible/chapter_11.4
#
mkdir ~/my-ansible/chapter_11.4
cd ~/my-ansible/chapter_11.4
# ansible.cfg, inventory 파일 작성
cat <<EOT> ansible.cfg
[defaults]
inventory = ./inventory
remote_user = ubuntu
ask_pass = false
roles_path = ./roles
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
EOT
cat <<EOT> inventory
[rootCA]
tnode1
[tnode]
tnode2
tnode3
EOT
2. myrole.rootCA와 myrole.serverKey 롤을 생성합니다.
ansible-galaxy role init --init-path ./roles myrole.rootCA
ansible-galaxy role init --init-path ./roles myrole.serverKey
3. 롤이 생성되었는지 확인합니다.
4. myrole.rootCA 에 태스크 파일을 작성합니다. (플레이북은 community.crypto 라는 컨텐츠 컬렉션을 사용) - Link
- 개인 키를 만들 때는 community.crypto.openssl_privatekey 모듈을 사용합니다. - Link
- CSR을 만들 때는 community.crypto.openssl_csr_pipe 모듈을 사용합니다. - Link
- CSR을 이용하여 인증서를 생성할 때는 community.crypto.x509_certificate 모듈을 사용합니다. - Link
- 여기서는 자체 서명 인증서를 생성하는 것이므로 provider를 selfsigned 로 설정합니다.
~/my-ansible/chapter_11.4/roles/myrole.rootCA/tasks/main.yml
---
# tasks file for myrole.rootCA
- name: Create new private key for rootCA
community.crypto.openssl_privatekey:
path: "{{ ca_privatekey_path }}"
- name: Create CSR for new certificate rootCA
community.crypto.openssl_csr_pipe:
privatekey_path: "{{ ca_privatekey_path }}"
country_name: "{{ country_name }}"
organization_name: "{{ orgarnization_name }}"
common_name: "{{ ca_common_name }}"
register: csr
- name: Create Self-signed new certificate rootCA
community.crypto.x509_certificate:
path: "{{ ca_certificate_path }}"
privatekey_path: "{{ ca_privatekey_path }}"
csr_content: "{{ csr.csr }}"
selfsigned_not_after: "{{ certificate_days }}"
provider: selfsigned
state: present
5. myrole.serverKey에 태스크 파일을 작성합니다.
- myrole.rootCA 태스크와 비슷하지만, rootCA로부터 발급받는 인증서를 생성할 때 provider가 사전에 생성된 CA 파일을 이용합니다.(ownca를 사용합니다.)
- 사전에 생성된 rootCA의 개인 키와 인증서는 ownca로 시작되는 파라미터를 사용합니다.
~/my-ansible/chapter_11.4/roles/myrole.serverKey/tasks/main.yml
---
# tasks file for myrole.serverKey
- name: Create new private key for server key
community.crypto.openssl_privatekey:
path: "{{ server_privatekey_path }}"
- name: Create CSR for new server key
community.crypto.openssl_csr_pipe:
privatekey_path: "{{ server_privatekey_path }}"
country_name: "{{ country_name }}"
organization_name: "{{ orgarnization_name }}"
common_name: "{{ server_common_name }}"
register: csr
- name: Create Self-signed server key from rootCA
community.crypto.x509_certificate:
path: "{{ server_certificate_path }}"
privatekey_path: "{{ server_privatekey_path }}"
csr_content: "{{ csr.csr }}"
ownca_path: "{{ ca_certificate_path }}"
ownca_privatekey_path: "{{ ca_privatekey_path }}"
ownca_not_after: "{{ certificate_days }}"
provider: ownca
state: present
6. 프로젝트 디렉토리에서 변수 파일을 생성합니다.
touch ~/my-ansible/chapter_11.4/vars_ssltls.yml
~/my-ansible/chapter_11.4/vars_ssltls.yml
---
country_name: KR
orgarnization_name: Cloudneta
ca_common_name: Ubuntu-CA
server_common_name: local
certificate_days: "+3650d"
ssl_tls_path: /home/ubuntu/tls
ca_privatekey_path: /home/ubuntu/tls/ca_priv.key
ca_certificate_path: /home/ubuntu/tls/ca_cert.crt
server_privatekey_path: /home/ubuntu/tls/server_priv.key
server_certificate_path: /home/ubuntu/tls/server_cert.crt
7. 플레이북을 작성합니다.
- become를 false로 설정하여 root가 아닌 ubuntu 계정에서 실행합니다.
- pre_tasks 섹션을 통해 롤이 실행되기 전에 인증서를 생성하고 보관할 디렉토리를 먼저 생성합니다.
touch ~/my-ansible/chapter_11.4/make_certification.yml
~/my-ansible/chapter_11.1/make_certification.yml
---
- hosts: rootCA
become: false
vars_files: vars_ssltls.yml
pre_tasks:
- name: Make ssl & tls directory
ansible.builtin.file:
path: "{{ ssl_tls_path }}"
state: directory
roles:
- role: myrole.rootCA
- role: myrole.serverKey
8. 플레이북을 실행합니다.
9. 플레이북의 대상이 rootCA 호스트 그룹이었으므로 tnode1에만 인증관련 파일이 생성된 것을 확인할 수 있습니다.
'DevOps > Ansible' 카테고리의 다른 글
[A101] Ansible로 AWS EC2 생성 (0) | 2024.02.06 |
---|---|
[A101] Ansible - 모니터링 자동화 (1) | 2024.02.06 |
[A101] Ansible - 시스템 구축 및 환경 설정 자동화 (0) | 2024.01.28 |
[A101] Ansible - 앤서블 갤럭시와 콘텐츠 컬렉션 (0) | 2024.01.28 |
[A101] Ansible - 핸들러와 롤 (0) | 2024.01.20 |