Ansible을 이용한 보안 설정/모니터링 자동화와 Semaphore
A101 가시다 4주차
이번 시간에는 앤서블을 이용하여 패스워드 규칙 설정과 같은 요구사항을 충족할 수 있는 태스크를 작성하면서 보안 설정과 모니터링 자동화를 할 수 있는 방법에 대해 알아본다.
또한 Ansible을 쉽게 사용할 수 있도록 Web UI인 Semaphore와 기타 Runner에 대해서도 실습을 통해 알아보는 시간을 가지도록 하겠다.
실습 환경 준비
- 실습에서 사용되는 유저를 생성한다.
# 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
EOTcat <<EOT> inventory
tnode1
tnode2
tnode3
EOT# 사용자 계정 정보가 정의된 변수 파일을 생성
mkdir vars
cat <<EOT > vars/users.yml
user_info:
- userid: "ansible"
userpw: "ansiblePw1"
- userid: "stack"
userpw: "stackPw1"
EOT# 사용자 계정을 생성하는 플레이북을 작성
# 모든 호스트에 동일하게 생성하며 vault로 작성된 변수 파일을 읽어서 생성ubuntu@server:~/ansible-project/chapter_09.1$ #
ansible-playbook create_user.yml# 계정 생성 확인
ansible -m shell -a "tail -n 3 /etc/passwd" allPLAY [all] *********************************************************************TASK [Gathering Facts] *********************************************************
ok: [tnode1]
ok: [tnode3]
ok: [tnode2]TASK [Create user] *************************************************************
changed: [tnode1] => (item={'userid': 'ansible', 'userpw': 'ansiblePw1'})
changed: [tnode2] => (item={'userid': 'ansible', 'userpw': 'ansiblePw1'})
changed: [tnode3] => (item={'userid': 'ansible', 'userpw': 'ansiblePw1'})
changed: [tnode1] => (item={'userid': 'stack', 'userpw': 'stackPw1'})
changed: [tnode2] => (item={'userid': 'stack', 'userpw': 'stackPw1'})
changed: [tnode3] => (item={'userid': 'stack', 'userpw': 'stackPw1'})PLAY RECAP *********************************************************************
tnode1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
tnode2 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
tnode3 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0tnode1 | CHANGED | rc=0 >>
statd:x:116:65534::/var/lib/nfs:/usr/sbin/nologin
ansible:x:1001:1001::/home/ansible:/bin/bash
stack:x:1002:1002::/home/stack:/bin/bash
tnode2 | CHANGED | rc=0 >>
mysql:x:117:122:MySQL Server,,,:/nonexistent:/bin/false
ansible:x:1001:1001::/home/ansible:/bin/bash
stack:x:1002:1002::/home/stack:/bin/bash
tnode3 | CHANGED | rc=0 >>
statd:x:116:65534::/var/lib/nfs:/usr/sbin/nologin
ansible:x:1001:1001::/home/ansible:/bin/bash
stack:x:1002:1002::/home/stack:/bin/bash
실습
패스워드 변경주기 설정하자
- 사전 분석: 패스워드 변경 주기를 설정할 대상 호스트는 인벤토리를 통하여 설정한다. 이 때, 변경 주기를 설정할 사용자의 계정 정보와 최대 변경일은 변수를 통하여 별도 파일로 정의한다. 주기를 설정할 때는
ansible.builtin.user
모듈을 사용해야 한다. - 플레이북 설계: 사용자 계정과 최대 변경일을 변수로 설정하기 위해
vars_maxdays.yml
파일을 생성해야 한다. 이 때, 메인 플레이북인set_change_password.yml
파일에는 변경 주기를 설정할 태스크를 포함해야 한다. 이 때,set_change_password.yml
은 default password가 있는데 90일 마다 변경하고 싶다라고 하는 요구사항을 생각해볼 수 있다. - 프로젝트 디렉터리 생성 및 ansible.cfg, inventory 파일 작성
#
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
EOTcat <<EOT> inventory
[tnode]
tnode1
tnode2
tnode3
EOT
- 사전형 변수 파일을 작성한다
# ~/my-ansible/chapter_11.1/**vars_maxdays.yml**
---Userinfo:
- username: ansible
maxdays: 90
- username: stack
maxdays: 90
- 메인 플레이북을 작성한다. 패스워드의 변경 주기는 유저 모듈의
password_expire_max
파라미터로 설정해야 한다.
- 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 }}"
- 어떤 커맨드를 사용하는가?
man chage
CentOS : man chage
1. 용도 및 목적 사용자 비밀번호 만료 정보 변경(계정의 Password expires(패스워드 만료)와 Account expires(계정 만료)를 이해해야함)
-패스워드 만료시는 ssh 접속 시 기존패스워드를 입력 후 신규 패스워드로 변경하면 OS에 접근이 가능하다
-계정 만료시는 single mode 에서 계정 정보를 변경해줘야 접속이 가능하다(재기동 필요)
2. 자주 쓰는 옵션 -사용자 만료 정보 확인
#chage -l test -사용자 계정 만료일 해제 및 패스워드 유지기간 무제한(E : 만료일 설정, M : 만료일 무제한)
#chage -E -1 -M 99999 test 3. 활용 방법 -사용자 min 설정(최초 변경 후 min 설정일이 지나야 변경 가능)
#chage -m 1 test -사용자 max 설정(최초 변경 후 max 설정일이 지나면 만료)
#chage -M 90 test -사용자 계정 만료일 설정(잠금 해제도 가능)
# chage -E 2020–03–25 test
ubuntu@server:~/ansible-project/chapter_11.1$ for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo chage -l ansible; echo; done
>> tnode1 <<
Last password change : Feb 10, 2024
Password expires : never
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 99999
Number of days of warning before password expires : 7
>> tnode2 <<
Last password change : Feb 10, 2024
Password expires : never
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 99999
Number of days of warning before password expires : 7>> tnode3 <<
Last password change : Feb 10, 2024
Password expires : never
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 99999
Number of days of warning before password expires : 7
- 패스워드 변경주기를 설정하는 플레이북 실행
ubuntu@server:~/ansible-project/chapter_11.1$ ansible-playbook set_change_password.yml
PLAY [tnode] *******************************************************************TASK [Gathering Facts] *********************************************************
ok: [tnode3]
ok: [tnode1]
ok: [tnode2]TASK [Change Password Maxdays] *************************************************
changed: [tnode1] => (item={'username': 'ansible', 'maxdays': 90})
changed: [tnode3] => (item={'username': 'ansible', 'maxdays': 90})
changed: [tnode2] => (item={'username': 'ansible', 'maxdays': 90})
changed: [tnode1] => (item={'username': 'stack', 'maxdays': 90})
changed: [tnode3] => (item={'username': 'stack', 'maxdays': 90})
changed: [tnode2] => (item={'username': 'stack', 'maxdays': 90})PLAY RECAP *********************************************************************
tnode1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
tnode2 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
tnode3 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
- 실행 이후에 90일로 변경된 것을 알 수 있다
ubuntu@server:~/ansible-project/chapter_11.1$ for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo chage -l ansible; echo; done
>> tnode1 <<
Last password change : Feb 10, 2024
Password expires : May 10, 2024
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 90
Number of days of warning before password expires : 7
>> tnode2 <<
Last password change : Feb 10, 2024
Password expires : May 10, 2024
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 90
Number of days of warning before password expires : 7>> tnode3 <<
Last password change : Feb 10, 2024
Password expires : May 10, 2024
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 90
Number of days of warning before password expires : 7
패스워드 생성 법칙을 적용하자
- 사전 분석: 패스워드 생성 법칙을 적용하기 위해서는 pwquality.conf라고 하는 pam 설정 파일을 이용해야 하며 리눅스 서버에 libpam-pwquality 패키지가 있어야 한다. 패스워드의 변경 주기는 /etc/security/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 : 동일 클래스의 최대 연속 문자 수
- 플레이북 개발 및 실행
- ansible.cfg, inventory file
#
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
EOTcat <<EOT> inventory
[tnode]
tnode1
tnode2
tnode3
EOT
- 변수 값을 설정한다
// ~/**my-ansible/**chapter_11.2/**vars_pw_rule.yml
---
minlen: 8
dcredit: -1
ucredit: -1
lcredit: -1
ocredit: -1
enforce_for_root: false**
- Jinja2 템플릿 파일 pwquality.conf.j2 작성
{% ~ %}
사이에 제어문 구문 위치{% if **minlen** is **defined** %}
구문 : minlen 이라는 변수가 정의되면 아래 문장을 삽입하라’ 는 의미- 아래 템플릿에는
{% if **변수** is **defined** %}
~{% **endif** %}
구문을 사용하여 파라미터와 관련된 변수가 선언되면 해당 파라미터를 삽입
# Created by ansible # ~/**my-ansible/**chapter_11.2/**pwquality.conf.j2** {% 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 %}
- 메인 플레이북을 작성한다. 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
remote_src: yes #** 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'**
- 플레이북이 정상적으로 실행되었는지 확인한다.
# 문법 체크
ansible-playbook --syntax-check set_password_rule.yml
# 시뮬레이션
ansible-playbook --check set_password_rule.yml# 실행 전 확인
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo cat /etc/security/pwquality.conf; echo; done
# 아예 파일도 존재하지 않아야 정상이다# 실행
ubuntu@server:~/ansible-project/chapter_11.2$ ansible-playbook set_password_rule.ymlPLAY [tnode] *******************************************************************TASK [Gathering Facts] *********************************************************
ok: [tnode3]
ok: [tnode1]
ok: [tnode2]TASK [Install libpam-pwquality] ************************************************
changed: [tnode3]
changed: [tnode2]
changed: [tnode1]TASK [Backup pwquality.conf] ***************************************************
changed: [tnode3]
changed: [tnode1]
changed: [tnode2]TASK [Copy pwquality.conf.j2 at /etc/security] *********************************
changed: [tnode1]
changed: [tnode3]
changed: [tnode2]PLAY RECAP *********************************************************************
tnode1 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
tnode2 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
tnode3 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0# 확인
ubuntu@server:~/ansible-project/chapter_11.2$ for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo ls -l /etc/security; echo; done
>> tnode1 <<
total 60
-rw-r--r-- 1 root root 4564 Mar 24 2022 access.conf
-rw-r--r-- 1 root root 1793 Jul 1 2020 capability.conf
-rw-r--r-- 1 root root 2234 Mar 24 2022 faillock.conf
-rw-r--r-- 1 root root 3635 Mar 24 2022 group.conf
-rw-r--r-- 1 root root 2161 Mar 24 2022 limits.conf
drwxr-xr-x 2 root root 4096 Mar 24 2022 limits.d
-rw-r--r-- 1 root root 1637 Mar 24 2022 namespace.conf
drwxr-xr-x 2 root root 4096 Mar 24 2022 namespace.d
-rwxr-xr-x 1 root root 1016 Mar 24 2022 namespace.init
-rw------- 1 root root 0 Jan 26 11:09 opasswd
-rw-r--r-- 1 root root 2971 Mar 24 2022 pam_env.conf
-rw-r--r-- 1 root root 487 Feb 10 09:42 pwquality.conf
-rw-r--r-- 1 root root 2674 Jan 14 2022 pwquality.conf.bak
-rw-r--r-- 1 root root 419 Mar 24 2022 sepermit.conf
-rw-r--r-- 1 root root 2179 Mar 24 2022 time.conf>> tnode2 <<
total 60
-rw-r--r-- 1 root root 4564 Mar 24 2022 access.conf
-rw-r--r-- 1 root root 1793 Jul 1 2020 capability.conf
-rw-r--r-- 1 root root 2234 Mar 24 2022 faillock.conf
-rw-r--r-- 1 root root 3635 Mar 24 2022 group.conf
-rw-r--r-- 1 root root 2161 Mar 24 2022 limits.conf
drwxr-xr-x 2 root root 4096 Mar 24 2022 limits.d
-rw-r--r-- 1 root root 1637 Mar 24 2022 namespace.conf
drwxr-xr-x 2 root root 4096 Mar 24 2022 namespace.d
-rwxr-xr-x 1 root root 1016 Mar 24 2022 namespace.init
-rw------- 1 root root 0 Jan 26 11:09 opasswd
-rw-r--r-- 1 root root 2971 Mar 24 2022 pam_env.conf
-rw-r--r-- 1 root root 487 Feb 10 09:42 pwquality.conf
-rw-r--r-- 1 root root 2674 Jan 14 2022 pwquality.conf.bak # bak은 백업 파일
-rw-r--r-- 1 root root 419 Mar 24 2022 sepermit.conf
-rw-r--r-- 1 root root 2179 Mar 24 2022 time.conf>> tnode3 <<
total 60
-rw-r--r-- 1 root root 4564 Mar 24 2022 access.conf
-rw-r--r-- 1 root root 1793 Jul 1 2020 capability.conf
-rw-r--r-- 1 root root 2234 Mar 24 2022 faillock.conf
-rw-r--r-- 1 root root 3635 Mar 24 2022 group.conf
-rw-r--r-- 1 root root 2161 Mar 24 2022 limits.conf
drwxr-xr-x 2 root root 4096 Mar 24 2022 limits.d
-rw-r--r-- 1 root root 1637 Mar 24 2022 namespace.conf
drwxr-xr-x 2 root root 4096 Mar 24 2022 namespace.d
-rwxr-xr-x 1 root root 1016 Mar 24 2022 namespace.init
-rw------- 1 root root 0 Jan 26 11:09 opasswd
-rw-r--r-- 1 root root 2971 Mar 24 2022 pam_env.conf
-rw-r--r-- 1 root root 487 Feb 10 09:42 pwquality.conf
-rw-r--r-- 1 root root 2674 Jan 14 2022 pwquality.conf.bak
-rw-r--r-- 1 root root 419 Mar 24 2022 sepermit.conf
-rw-r--r-- 1 root root 2179 Mar 24 2022 time.confubuntu@server:~/ansible-project/chapter_11.2$ for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo cat /etc/security/pwquality.conf; echo; done
>> tnode1 <<
# Created by ansible# Minimum acceptable size for the new password
minlen = 8# The maximum credit for having digits in the new password
dcredit = -1# The maximum credit for having uppercase characters in the new password
ucredit = -1# The maximum credit for having lowercase characters in the new password
lcredit = -1# The maximum credit for having other characters in the new password
ocredit = -1# Enforces pwquality checks on the root user password.
enforce_for_root>> tnode2 <<
# Created by ansible# Minimum acceptable size for the new password
minlen = 8# The maximum credit for having digits in the new password
dcredit = -1# The maximum credit for having uppercase characters in the new password
ucredit = -1# The maximum credit for having lowercase characters in the new password
lcredit = -1# The maximum credit for having other characters in the new password
ocredit = -1# Enforces pwquality checks on the root user password.
enforce_for_root>> tnode3 <<
# Created by ansible# Minimum acceptable size for the new password
minlen = 8# The maximum credit for having digits in the new password
dcredit = -1# The maximum credit for having uppercase characters in the new password
ucredit = -1# The maximum credit for having lowercase characters in the new password
lcredit = -1# The maximum credit for having other characters in the new password
ocredit = -1# Enforces pwquality checks on the root user password.
enforce_for_root# 확인 : ansible 계정에 패스워드를 조건이 불충분하도록 입력 시도
ssh tnode2
-----------------
sudo su - ansible
whoami
pwd
ansible@tnode2:~$ passwd ansible
Changing password for ansible.
Current password:
New password:
BAD PASSWORD: The password contains less than 1 digits
New password:
BAD PASSWORD: The password is a palindrome
New password:
BAD PASSWORD: The password is a palindrome
a
passwd: Have exhausted maximum number of retries for service
passwd: password unchangedexit
exit
-----------------
디렉터리 및 파일 접근 권한을 변경해보자
- 사용하는 패키지는 SUID, SGID, Sticky bit, World Writeable 등이 있다.
- 상황: 리눅스 보안에서 신경써야 한 것이 Sticky Bit 설정 파일과 World Writeable 설정 파일이다. Sticky bit 설정 파일은 리눅스에서 파일 소유자나 그룹 소유자만 해당 파일을 읽고 쓰고 삭제할 수 있도록 권한을 부여한 것을 의미한다. 파일 소유자에게 권한을 부여하면 SUID, 파일 그룹에게 권한을 부여하면 SGID, 다른 사람에게 권한을 부여하면 Sticky bit 라고 한다.
- Sticky bit가 적용된 파일 목록 중 보안을 위해 이를 적용하면 안 되는 파일 목록들이 있다. Sticky bit가 적용된 파일의 권한을 수정할 때는 적용되면 안 되는 파일인지 반드시 먼저 확인해야 하는데, 이는 보안 관련 가이드를 참고해야 한다. 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(sticky bit를 뺀다), g-s, o-s 로 설정하고, World Writable 파일은 o-w(쓰기 권한을 빼버린다) 로 설정한다.
- Task 파일을 작성
- hosts: tnode tasks: # 검색 시 grep 으로 필터링(-e 정규식 패턴, -v 매칭되지 않는 경우)
- 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}' # directory 빼고 출력
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" # sticky bit 제거
loop: "{{ sfile_list.stdout_lines }}" - name: Set World Writable files
ansible.builtin.file:
path: "{{ item }}"
mode: "o-w" # others 쪽에 write 권한을 빼버린다
loop: "{{ wfile_list.stdout_lines }}"
- 플레이북을 실행해본다
- 만약 리눅스의 permission에 대해서 잘 모른다면 해당 지식에 대한 이해는 필수적이므로 다음 링크 https://bing-su-b.tistory.com/7 학습해보자.
# 문법 체크
ansible-playbook --syntax-check set_sticky_writable_files.yml
# 시뮬레이션
ansible-playbook --check set_sticky_writable_files.yml# (예시) 확인
ubuntu@server:~/ansible-project/chapter_11.2/chapter_11.3$ for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo ls -al /usr/bin/newgrp; echo; done # -rwsr-xr-x
>> tnode1 <<
-rw**s**r-xr-x 1 root root 40496 Nov 24 2022 /usr/bin/newgrp>> tnode2 <<
-rwsr-xr-x 1 root root 40496 Nov 24 2022 /usr/bin/newgrp>> tnode3 <<
-rwsr-xr-x 1 root root 40496 Nov 24 2022 /usr/bin/newgrpubuntu@server:~/ansible-project/chapter_11.2/chapter_11.3$ for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo ls -al /var/crash; echo; done # drwxrwxrwt
>> tnode1 <<
total 8
drwxrwxr**w**t 2 root root 4096 Jan 26 11:13 .
drwxr-xr-x 13 root root 4096 Jan 26 11:13 ..>> tnode2 <<
total 8
drwxrwxr**w**t 2 root root 4096 Jan 26 11:13 .
drwxr-xr-x 13 root root 4096 Jan 26 11:13 ..>> tnode3 <<
total 8
drwxrwxr**w**t 2 root root 4096 Jan 26 11:13 .
drwxr-xr-x 13 root root 4096 Jan 26 11:13 ..# 실행
ubuntu@server:~/ansible-project/chapter_11.2/chapter_11.3$ ansible-playbook set_sticky_writable_files.ymlPLAY [tnode] *******************************************************************TASK [Gathering Facts] *********************************************************
ok: [tnode2]
ok: [tnode1]
ok: [tnode3]TASK [Find Sticky bit files] ***************************************************
changed: [tnode2]
changed: [tnode3]
changed: [tnode1]TASK [Find World Writable files] ***********************************************
changed: [tnode2]
changed: [tnode3]
changed: [tnode1]TASK [Print Sticky bit files] **************************************************
ok: [tnode1] => {
"msg": [
"/usr/bin/newgrp"
]
}
ok: [tnode2] => {
"msg": [
"/usr/bin/newgrp"
]
}
ok: [tnode3] => {
"msg": [
"/usr/bin/newgrp"
]
}TASK [Print World Writable files] **********************************************
ok: [tnode1] => {
"msg": [
"/tmp",
"/tmp/systemd-private-6ddf0018dce84db0ba1d65da85c5cd86-chrony.service-a3UJmi/tmp",
"/tmp/.ICE-unix",
"/tmp/.XIM-unix",
"/tmp/.X11-unix",
"/tmp/snap-private-tmp/snap.lxd/tmp",
"/tmp/.Test-unix",
"/tmp/.font-unix",
"/var/tmp",
"/var/tmp/cloud-init",
"/var/tmp/systemd-private-6ddf0018dce84db0ba1d65da85c5cd86-chrony.service-5sPYo0/tmp",
"/var/crash"
]
}
ok: [tnode2] => {
"msg": [
"/tmp",
"/tmp/systemd-private-544272086c0d4f6e9dfe8760fad8cda1-chrony.service-e0BiDE/tmp",
"/tmp/.ICE-unix",
"/tmp/.XIM-unix",
"/tmp/.X11-unix",
"/tmp/snap-private-tmp/snap.lxd/tmp",
"/tmp/.Test-unix",
"/tmp/.font-unix",
"/var/tmp",
"/var/tmp/systemd-private-544272086c0d4f6e9dfe8760fad8cda1-chrony.service-Md9pIp/tmp",
"/var/tmp/cloud-init",
"/var/crash"
]
}
ok: [tnode3] => {
"msg": [
"/tmp",
"/tmp/systemd-private-32bfce25d99140248046c0f1de3af111-chrony.service-GPQDYr/tmp",
"/tmp/.ICE-unix",
"/tmp/.XIM-unix",
"/tmp/.X11-unix",
"/tmp/snap-private-tmp/snap.lxd/tmp",
"/tmp/.Test-unix",
"/tmp/.font-unix",
"/var/tmp",
"/var/tmp/systemd-private-32bfce25d99140248046c0f1de3af111-chrony.service-0fOU2B/tmp",
"/var/tmp/cloud-init",
"/var/crash"
]
}TASK [Set Sticky bit files] ****************************************************
changed: [tnode3] => (item=/usr/bin/newgrp)
changed: [tnode1] => (item=/usr/bin/newgrp)
changed: [tnode2] => (item=/usr/bin/newgrp)TASK [Set World Writable files] ************************************************
changed: [tnode1] => (item=/tmp)
changed: [tnode3] => (item=/tmp)
changed: [tnode2] => (item=/tmp)
changed: [tnode3] => (item=/tmp/systemd-private-32bfce25d99140248046c0f1de3af111-chrony.service-GPQDYr/tmp)
changed: [tnode2] => (item=/tmp/systemd-private-544272086c0d4f6e9dfe8760fad8cda1-chrony.service-e0BiDE/tmp)
changed: [tnode1] => (item=/tmp/systemd-private-6ddf0018dce84db0ba1d65da85c5cd86-chrony.service-a3UJmi/tmp)
changed: [tnode3] => (item=/tmp/.ICE-unix)
changed: [tnode1] => (item=/tmp/.ICE-unix)
changed: [tnode2] => (item=/tmp/.ICE-unix)
changed: [tnode3] => (item=/tmp/.XIM-unix)
changed: [tnode1] => (item=/tmp/.XIM-unix)
changed: [tnode2] => (item=/tmp/.XIM-unix)
changed: [tnode3] => (item=/tmp/.X11-unix)
changed: [tnode1] => (item=/tmp/.X11-unix)
changed: [tnode2] => (item=/tmp/.X11-unix)
changed: [tnode3] => (item=/tmp/snap-private-tmp/snap.lxd/tmp)
changed: [tnode2] => (item=/tmp/snap-private-tmp/snap.lxd/tmp)
changed: [tnode1] => (item=/tmp/snap-private-tmp/snap.lxd/tmp)
changed: [tnode3] => (item=/tmp/.Test-unix)
changed: [tnode2] => (item=/tmp/.Test-unix)
changed: [tnode1] => (item=/tmp/.Test-unix)
changed: [tnode3] => (item=/tmp/.font-unix)
changed: [tnode2] => (item=/tmp/.font-unix)
changed: [tnode1] => (item=/tmp/.font-unix)
changed: [tnode3] => (item=/var/tmp)
changed: [tnode2] => (item=/var/tmp)
changed: [tnode1] => (item=/var/tmp)
changed: [tnode3] => (item=/var/tmp/systemd-private-32bfce25d99140248046c0f1de3af111-chrony.service-0fOU2B/tmp)
changed: [tnode2] => (item=/var/tmp/systemd-private-544272086c0d4f6e9dfe8760fad8cda1-chrony.service-Md9pIp/tmp)
changed: [tnode1] => (item=/var/tmp/cloud-init)
changed: [tnode3] => (item=/var/tmp/cloud-init)
changed: [tnode2] => (item=/var/tmp/cloud-init)
changed: [tnode1] => (item=/var/tmp/systemd-private-6ddf0018dce84db0ba1d65da85c5cd86-chrony.service-5sPYo0/tmp)
changed: [tnode3] => (item=/var/crash)
changed: [tnode2] => (item=/var/crash)
changed: [tnode1] => (item=/var/crash)PLAY RECAP *********************************************************************
tnode1 : ok=7 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
tnode2 : ok=7 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
tnode3 : ok=7 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0# (예시) 변경 확인
ubuntu@server:~/ansible-project/chapter_11.2/chapter_11.3$ for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo ls -al /usr/bin/newgrp; echo; done # -rwxr-xr-x
>> tnode1 <<
-rwxr-xr-x 1 root root 40496 Nov 24 2022 /usr/bin/newgrp>> tnode2 <<
-rwxr-xr-x 1 root root 40496 Nov 24 2022 /usr/bin/newgrp>> tnode3 <<
-rwxr-xr-x 1 root root 40496 Nov 24 2022 /usr/bin/newgrpubuntu@server:~/ansible-project/chapter_11.2/chapter_11.3$ for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo ls -al /var/crash; echo; done # drwxrwxr-t
>> tnode1 <<
total 8
drwxrwxr-t 2 root root 4096 Jan 26 11:13 .
drwxr-xr-x 13 root root 4096 Jan 26 11:13 ..>> tnode2 <<
total 8
drwxrwxr-t 2 root root 4096 Jan 26 11:13 .
drwxr-xr-x 13 root root 4096 Jan 26 11:13 ..>> tnode3 <<
total 8
drwxrwxr-t 2 root root 4096 Jan 26 11:13 .
drwxr-xr-x 13 root root 4096 Jan 26 11:13 ..# 한번 더 실행
ubuntu@server:~/ansible-project/chapter_11.2/chapter_11.3$ ansible-playbook set_sticky_writable_files.ymlPLAY [tnode] *******************************************************************TASK [Gathering Facts] *********************************************************
ok: [tnode2]
ok: [tnode3]
ok: [tnode1]TASK [Find Sticky bit files] ***************************************************
changed: [tnode3]
changed: [tnode1]
changed: [tnode2]TASK [Find World Writable files] ***********************************************
changed: [tnode3]
changed: [tnode2]
changed: [tnode1]TASK [Print Sticky bit files] **************************************************
ok: [tnode1] => {
"msg": []
}
ok: [tnode2] => {
"msg": [
"ansible.cfg",
"inventory"
]
}
ok: [tnode3] => {
"msg": []
}TASK [Print World Writable files] **********************************************
ok: [tnode1] => {
"msg": []
}
ok: [tnode2] => {
"msg": []
}
ok: [tnode3] => {
"msg": []
}TASK [Set Sticky bit files] ****************************************************
ok: [tnode2] => (item=ansible.cfg)
ok: [tnode2] => (item=inventory)TASK [Set World Writable files] ************************************************PLAY RECAP *********************************************************************
tnode1 : ok=5 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
tnode2 : ok=6 changed=2 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
tnode3 : ok=5 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
사설 인증서 생성하기
사전 분석
- 사설 인증서에는 자체 서명된 인증 기관용 인증서와 해당 인증서를 이용해 만든 클라이언트 인증 키가 있다. 인증서를 만들 때는 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로부터 발급받아 생성하는 방식을 사용한다
플레이북 개발하기
- ansible, inventory
#
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
EOTcat <<EOT> inventory
[rootCA]
tnode1[tnode]
tnode2
tnode3
EOT
- 롤을 2개 만들어보자
ubuntu@server:~/ansible-project/chapter_11.4$ #
ansible-galaxy role init --init-path ./roles myrole.rootCA
ansible-galaxy role init --init-path ./roles myrole.serverKey
# 확인
ansible-galaxy role list
tree roles -L 2
- Role myrole.rootCA was created successfully
- Role myrole.serverKey was created successfully
# /home/ubuntu/ansible-project/chapter_11.4/roles
- myrole.serverKey, (unknown version)
- myrole.rootCA, (unknown version)
roles
├── myrole.rootCA
│ ├── README.md
│ ├── defaults
│ ├── files
│ ├── handlers
│ ├── meta
│ ├── tasks
│ ├── templates
│ ├── tests
│ └── vars
└── myrole.serverKey
├── README.md
├── defaults
├── files
├── handlers
├── meta
├── tasks
├── templates
├── tests
└── vars18 directories, 2 files
- 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
- myrole.serverKey 에 태스크 파일 작성
- myrole.rootCA 태스크와 비슷하지만, rootCA로부터 발급받는 인증서를 생성할 때 provider가 사전에 생성된 CA 파일을 이용(ownca)를 사용.
- 그리고 사전에 생성된 rootC의 개인 키와 인증서는 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
- 변수 파일을 생성한다
# touch ~/**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**
2. 필요한 모듈을 설치한다
ubuntu@server:~/ansible-project/chapter_11.4$ ansible-galaxy collection install community.crypto
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading <https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/community-crypto-2.17.1.tar.gz> to /home/ubuntu/.ansible/tmp/ansible-local-22927_ea5w6as/tmp3_c09pb0/community-crypto-2.17.1-kz41qipt
Installing 'community.crypto:2.17.1' to '/home/ubuntu/.ansible/collections/ansible_collections/community/crypto'
community.crypto:2.17.1 was installed successfully
3. 플레이북을 작성한다
# ~/**my-ansible/**chapter_11.1/**make_certification.yml**
---- hosts: rootCA # tnode1
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을 만든다
- role: myrole.serverKey
4. 플레이북을 실행한다
ubuntu@server:~/ansible-project/chapter_11.4$ ansible-playbook make_certification.yml
PLAY [rootCA] ******************************************************************TASK [Gathering Facts] *********************************************************
ok: [tnode1]TASK [Make ssl & tls directory] ************************************************
changed: [tnode1]TASK [myrole.rootCA : Create new private key for rootCA] ***********************
changed: [tnode1]TASK [myrole.rootCA : Create CSR for new certificate rootCA] *******************
changed: [tnode1]TASK [myrole.rootCA : Create Self-signed new certificate rootCA] ***************
changed: [tnode1]TASK [myrole.serverKey : Create new private key for server key] ****************
changed: [tnode1]TASK [myrole.serverKey : Create CSR for new server key] ************************
changed: [tnode1]TASK [myrole.serverKey : Create Self-signed server key from rootCA] ************
changed: [tnode1]PLAY RECAP *********************************************************************
tnode1 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0ubuntu@server:~/ansible-project/chapter_11.4$ for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i ls -l ~/tls; echo; done
>> tnode1 <<
total 16
-rw-rw-r-- 1 ubuntu ubuntu 1858 Feb 10 10:07 ca_cert.crt
-rw------- 1 ubuntu ubuntu 3243 Feb 10 10:07 ca_priv.key
-rw-rw-r-- 1 ubuntu ubuntu 1891 Feb 10 10:07 server_cert.crt
-rw------- 1 ubuntu ubuntu 3243 Feb 10 10:07 server_priv.key>> tnode2 <<
ls: cannot access '/home/ubuntu/tls': No such file or directory>> tnode3 <<
ls: cannot access '/home/ubuntu/tls': No such file or directory
팩트를 이용한 시스템 모니터링 자동화
- 사전 분석: 팩트는 관리 노드에서 시스템과 관련된 정보(아래 예시)들을 찾아 변수로 제공 → 인프라 정보 파악 및 로그로 저장
호스트 이름
커널 버전
네트워크 인터페이스 이름
네트워크 인터페이스 IP 주소
운영체제 버전
CPU 개수
사용 가능한 메모리
스토리지 장치의 크기 및 여유 공간
- 추출한 내용은
ansible.builtin.**shell**
모듈을 이용하여 /var/log/daily_check 디렉터리에 저장한다
플레이북 개발
- ansible.cfg, inventory 파일 작성한다
# 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**
- 플레이북 파일을 작성한다. debug, facts를 수집하고 파일에 저장한다
---
- hosts: tnode
vars:
log_directory: /var/log/daily_check tasks:
- name: Print system info
ansible.builtin.debug:
msg:
- "################ Start #####################"
- "Date: {{ ansible_facts.date_time.date }} {{ ansible_facts.date_time.time }}"
- "HostName: {{ ansible_facts.hostname }}"
- "OS: {{ ansible_facts.distribution }}"
- "OS Version: {{ ansible_facts.distribution_version }}"
- "OS Kernel: {{ ansible_facts.kernel }}"
- "CPU Cores: {{ ansible_facts.processor_vcpus }}"
- "Memory: {{ ansible_facts.memory_mb.real }}"
- "Interfaces: {{ ansible_facts.interfaces }}"
- "IPv4: {{ ansible_facts.all_ipv4_addresses }}"
- "Devices: {{ ansible_facts.mounts }}"
- "################# End #######################"
register: result - name: Create log directory
ansible.builtin.file:
path: "{{ log_directory }}"
state: directory - name: Print logs to log file
ansible.builtin.shell: |
echo "{{ item }}" >> "{{ log_directory }}"/system_info.logs
loop: "{{ result.msg }}"
2. 플레이북을 실행한다. 아마 플레이북을 실행해서 날짜와 시간이 포함되게 하고 crontab으로 반복적으로 실행될 수 있도록 할 수 있겠다.
# 실행
ansible-playbook monitoring_facts.yml
# 확인
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i ls -l /var/log/daily_check; echo; done
ssh tnode1 sudo cat /var/log/daily_check/system_info.logsansible-playbook monitoring_facts.yml
ssh tnode1 sudo cat /var/log/daily_check/system_info.logs# Logs
################ Start #####################
Date: 2024-02-10 10:11:25
HostName: tnode1
OS: Ubuntu
OS Version: 22.04
OS Kernel: 6.2.0-1018-aws
CPU Cores: 2
Memory: {'total': 3847, 'used': 1441, 'free': 2406}
Interfaces: ['lo', 'ens5']
IPv4: ['10.10.1.11']
Devices: [{'mount': '/', 'device': '/dev/root', 'fstype': 'ext4', 'options': 'rw,relatime,discard,errors=remount-ro', 'size_total': 31024283648, 'size_available': 28897542144, 'block_size': 4096, 'block_total': 7574288, 'block_available': 7055064, 'block_used': 519224, 'inode_total': 3870720, 'inode_available': 3795753, 'inode_used': 74967, 'uuid': 'e46a3bda-f425-4174-a67f-631861311c42'}, {'mount': '/snap/core18/2812', 'device': '/dev/loop1', 'fstype': 'squashfs', 'options': 'ro,nodev,relatime,errors=continue,threads=single', 'size_total': 58458112, 'size_available': 0, 'block_size': 131072, 'block_total': 446, 'block_available': 0, 'block_used': 446, 'inode_total': 10944, 'inode_available': 0, 'inode_used': 10944, 'uuid': 'N/A'}, {'mount': '/snap/amazon-ssm-agent/7628', 'device': '/dev/loop0', 'fstype': 'squashfs', 'options': 'ro,nodev,relatime,errors=continue,threads=single', 'size_total': 26214400, 'size_available': 0, 'block_size': 131072, 'block_total': 200, 'block_available': 0, 'block_used': 200, 'inode_total': 16, 'inode_available': 0, 'inode_used': 16, 'uuid': 'N/A'}, {'mount': '/snap/core20/2105', 'device': '/dev/loop2', 'fstype': 'squashfs', 'options': 'ro,nodev,relatime,errors=continue,threads=single', 'size_total': 67108864, 'size_available': 0, 'block_size': 131072, 'block_total': 512, 'block_available': 0, 'block_used': 512, 'inode_total': 12041, 'inode_available': 0, 'inode_used': 12041, 'uuid': 'N/A'}, {'mount': '/snap/snapd/20671', 'device': '/dev/loop3', 'fstype': 'squashfs', 'options': 'ro,nodev,relatime,errors=continue,threads=single', 'size_total': 42467328, 'size_available': 0, 'block_size': 131072, 'block_total': 324, 'block_available': 0, 'block_used': 324, 'inode_total': 658, 'inode_available': 0, 'inode_used': 658, 'uuid': 'N/A'}, {'mount': '/boot/efi', 'device': '/dev/nvme0n1p15', 'fstype': 'vfat', 'options': 'rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro', 'size_total': 109395456, 'size_available': 103061504, 'block_size': 512, 'block_total': 213663, 'block_available': 201292, 'block_used': 12371, 'inode_total': 0, 'inode_available': 0, 'inode_used': 0, 'uuid': '6192-2BA4'}, {'mount': '/snap/lxd/26975', 'device': '/dev/loop4', 'fstype': 'squashfs', 'options': 'ro,nodev,relatime,errors=continue,threads=single', 'size_total': 91226112, 'size_available': 0, 'block_size': 131072, 'block_total': 696, 'block_available': 0, 'block_used': 696, 'inode_total': 959, 'inode_available': 0, 'inode_used': 959, 'uuid': 'N/A'}, {'mount': '/snap/lxd/27037', 'device': '/dev/loop5', 'fstype': 'squashfs', 'options': 'ro,nodev,relatime,errors=continue,threads=single', 'size_total': 91226112, 'size_available': 0, 'block_size': 131072, 'block_total': 696, 'block_available': 0, 'block_used': 696, 'inode_total': 959, 'inode_available': 0, 'inode_used': 959, 'uuid': 'N/A'}]
################# End #######################
팩트에서 수집하지 않는 정보를 모니터링하기
- 사전 분석: 팩트에서 제공되지 않은 정보를 모니터링해야되는 상황. 자세한 CPU, 메모리, 디스크 사용률 모니터링을 위해 dstat, iostat, vmstat 명령어 사용 → 툴 설치가 필요하다.
- 각각의 명령어 실행은
ansible.builtin.**shell**
이용하여 실행하고, loop 키워드를 이용하여 모니터링 명령어별로 여러 옵션을 추가하여 명령 실행한다. - 실행된 명령어 결과는 로그 디렉터리에 저장한다.
- ansible.cfg, inventory 파일 작성하기
#
mkdir ~/my-ansible/chapter_12.2
cd ~/my-ansible/chapter_12.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
EOTcat <<EOT> inventory
[tnode]
tnode1
tnode2
tnode3
EOT
2. 로그 디렉터리와 설치할 패키지가 명시된 변수 파일을 작성한다
# ~/my-ansible/chapter_12.2/vars_packages.yml
---log_directory: /home/ubuntu/logs
packages:
- dstat
- sysstat
3. 플레이북 파일을 작성한다. 이 때 debug와 facts를 수집하고 파일에 저장한다
# ~/my-ansible/chapter_12.2/monitoring_system.yml
---- hosts: tnode
vars_files: vars_packages.yml tasks:
- name: Install packages on RedHat
ansible.builtin.dnf:
name: "{{ item }}"
state: present
loop: "{{ packages }}"
when: ansible_facts.os_family == "RedHat" - name: Install packages on Ubuntu
ansible.builtin.apt:
name: "{{ item }}"
state: present
loop: "{{ packages }}"
when: ansible_facts.os_family == "Debian" - name: Create log directory
ansible.builtin.file:
path: "{{ log_directory }}"
state: directory - name: Monitoring dstat
ansible.builtin.shell: |
{{ item }} >> {{ log_directory }}/dstat.log
loop:
- dstat 2 10
- dstat -cmdlt -D vda 2 10 - name: Monitoring iostat
ansible.builtin.shell: |
{{ item }} >> {{ log_directory }}/iostat.log
loop:
- iostat
- echo "==============="
- iostat -t -c -dp vda
- echo "===============" - name: Monitoring vmstat
ansible.builtin.shell: |
{{ item }} >> {{ log_directory }}/vmstat.log
loop:
- vmstat
- echo "==============="
- vmstat -dt
- echo "==============="
- vmstat -D
- echo "===============" - name: Monitoring df
ansible.builtin.shell: |
df -h >> {{ log_directory }}/df.log
4. 플레이북 실행하기
- 도전 과제: 플레이북에 대상 노드에 ifconfig ens5 정보를 ifconfig.log 파일로 수집 할수 있게 태스크를 추가해보자
# (옵션) 툴 기본 설치 확인
vmstat
dstat
iostat
# 실행
ansible-playbook monitoring_system.yml
PLAY [tnode] *******************************************************************TASK [Gathering Facts] *********************************************************
ok: [tnode2]
ok: [tnode3]
ok: [tnode1]TASK [Install packages on RedHat] **********************************************
skipping: [tnode1] => (item=dstat)
skipping: [tnode1] => (item=sysstat)
skipping: [tnode2] => (item=dstat)
skipping: [tnode2] => (item=sysstat)
skipping: [tnode3] => (item=dstat)
skipping: [tnode3] => (item=sysstat)TASK [Install packages on Ubuntu] **********************************************
changed: [tnode1] => (item=dstat)
changed: [tnode2] => (item=dstat)
changed: [tnode3] => (item=dstat)
changed: [tnode1] => (item=sysstat)
changed: [tnode2] => (item=sysstat)
changed: [tnode3] => (item=sysstat)TASK [Create log directory] ****************************************************
changed: [tnode1]
changed: [tnode3]
changed: [tnode2]TASK [Monitoring dstat] ********************************************************
changed: [tnode3] => (item=dstat 2 10)
changed: [tnode1] => (item=dstat 2 10)
changed: [tnode2] => (item=dstat 2 10)
changed: [tnode3] => (item=dstat -cmdlt -D vda 2 10)
changed: [tnode1] => (item=dstat -cmdlt -D vda 2 10)
changed: [tnode2] => (item=dstat -cmdlt -D vda 2 10)TASK [Monitoring iostat] *******************************************************
changed: [tnode1] => (item=iostat)
changed: [tnode2] => (item=iostat)
changed: [tnode3] => (item=iostat)
changed: [tnode1] => (item=echo "===============")
changed: [tnode3] => (item=echo "===============")
changed: [tnode2] => (item=echo "===============")
changed: [tnode1] => (item=iostat -t -c -dp vda)
changed: [tnode3] => (item=iostat -t -c -dp vda)
changed: [tnode2] => (item=iostat -t -c -dp vda)
changed: [tnode1] => (item=echo "===============")
changed: [tnode3] => (item=echo "===============")
changed: [tnode2] => (item=echo "===============")TASK [Monitoring vmstat] *******************************************************
changed: [tnode1] => (item=vmstat)
changed: [tnode3] => (item=vmstat)
changed: [tnode2] => (item=vmstat)
changed: [tnode1] => (item=echo "===============")
changed: [tnode2] => (item=echo "===============")
changed: [tnode3] => (item=echo "===============")
changed: [tnode1] => (item=vmstat -dt)
changed: [tnode2] => (item=vmstat -dt)
changed: [tnode3] => (item=vmstat -dt)
changed: [tnode1] => (item=echo "===============")
changed: [tnode2] => (item=echo "===============")
changed: [tnode3] => (item=echo "===============")
changed: [tnode1] => (item=vmstat -D)
changed: [tnode2] => (item=vmstat -D)
changed: [tnode3] => (item=vmstat -D)
changed: [tnode1] => (item=echo "===============")
changed: [tnode3] => (item=echo "===============")
changed: [tnode2] => (item=echo "===============")TASK [Monitoring df] ***********************************************************
changed: [tnode1]
changed: [tnode3]
changed: [tnode2]PLAY RECAP *********************************************************************
tnode1 : ok=7 changed=6 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
tnode2 : ok=7 changed=6 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
tnode3 : ok=7 changed=6 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0# 확인
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i ls -l ~/logs; echo; done
>> tnode1 <<
total 16
-rw-r--r-- 1 root root 335 Feb 10 10:15 df.log
-rw-r--r-- 1 root root 1759 Feb 10 10:15 dstat.log
-rw-r--r-- 1 root root 2488 Feb 10 10:15 iostat.log
-rw-r--r-- 1 root root 1715 Feb 10 10:15 vmstat.log>> tnode2 <<
total 16
-rw-r--r-- 1 root root 444 Feb 10 10:15 df.log
-rw-r--r-- 1 root root 1759 Feb 10 10:15 dstat.log
-rw-r--r-- 1 root root 2488 Feb 10 10:15 iostat.log
-rw-r--r-- 1 root root 1715 Feb 10 10:15 vmstat.log>> tnode3 <<
total 16
-rw-r--r-- 1 root root 444 Feb 10 10:15 df.log
-rw-r--r-- 1 root root 1759 Feb 10 10:15 dstat.log
-rw-r--r-- 1 root root 2488 Feb 10 10:15 iostat.log
-rw-r--r-- 1 root root 1715 Feb 10 10:15 vmstat.logssh tnode1 cat ~/logs/df.log
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 2456536 116084 1113052 0 0 0 4 9 11 0 0 100 0 0
===============
disk- ------------reads------------ ------------writes----------- -----IO------ -----timestamp-----
total merged sectors ms total merged sectors ms cur sec KST
loop0 150 0 16820 41 0 0 0 0 0 0 2024-02-10 10:15:48
loop1 51 0 9520 16 0 0 0 0 0 0 2024-02-10 10:15:48
loop2 292 0 13882 82 0 0 0 0 0 0 2024-02-10 10:15:48
loop3 634 0 54882 238 0 0 0 0 0 2 2024-02-10 10:15:48
loop4 89 0 9672 30 0 0 0 0 0 0 2024-02-10 10:15:48
loop5 97 0 9446 4 0 0 0 0 0 0 2024-02-10 10:15:48
loop6 26 0 102 0 0 0 0 0 0 0 2024-02-10 10:15:48
loop7 0 0 0 0 0 0 0 0 0 0 2024-02-10 10:15:48
nvme0n1 17092 4884 957746 10893 247824 86796 9034154 237103 0 143 2024-02-10 10:15:48
===============
9 disks
3 partitions
18431 total reads
4884 merged reads
1072070 read sectors
11304 milli reading
247824 writes
86796 merged writes
9034154 written sectors
237103 milli writing
0 inprogress IO
145 milli spent IO
ssh tnode1 cat ~/logs/*.logansible-playbook monitoring_system.yml
ssh tnode1 cat ~/logs/df.log
ssh tnode1 cat ~/logs/*.log
Semaphore를 설정하기
- 패키지 매너지로 설치해보자
cd
pwd
wget <https://github.com/ansible-semaphore/semaphore/releases/download/v2.9.45/semaphore_2.9.45_linux_amd64.deb>
sudo dpkg -i semaphore_2.9.45_linux_amd64.deb
2. semaphore setup
1. Set up configuration for a MySQL/MariaDB database
2. Set up a path for your playbooks (auto-created)
3. Run database Migrations
4. Set up initial semaphore user & passwordWhat database to use:
1 - MySQL
2 - BoltDB
3 - PostgreSQL
(default 1): 2Enable slack alerts? (yes/no) (default no): yesSlack Webhook URL: 슬랙 웹훅을 사용하도록 설정함Enable LDAP authentication? (yes/no) (default no):Config output directory (default /home/ubuntu):Running: mkdir -p /home/ubuntu..
Configuration written to /home/ubuntu/config.json..
Pinging db..
Running db Migrations..
Executing migration v0.0.0 (at 2024-02-10 10:22:59.894762225 +0900 KST m=+58.466267502)...
Executing migration v1.0.0 (at 2024-02-10 10:22:59.898583342 +0900 KST m=+58.470088638)...
Executing migration v1.2.0 (at 2024-02-10 10:22:59.899798701 +0900 KST m=+58.471303960)...
Executing migration v1.3.0 (at 2024-02-10 10:22:59.900699513 +0900 KST m=+58.472204770)...
Executing migration v1.4.0 (at 2024-02-10 10:22:59.901963114 +0900 KST m=+58.473468372)...
Executing migration v1.5.0 (at 2024-02-10 10:22:59.903011731 +0900 KST m=+58.474516998)...
Executing migration v1.6.0 (at 2024-02-10 10:22:59.904425048 +0900 KST m=+58.475930315)...
Executing migration v1.7.0 (at 2024-02-10 10:22:59.90556446 +0900 KST m=+58.477069713)...
Executing migration v1.8.0 (at 2024-02-10 10:22:59.906816374 +0900 KST m=+58.478321644)...
Executing migration v1.9.0 (at 2024-02-10 10:22:59.908208493 +0900 KST m=+58.479713746)...
Executing migration v2.2.1 (at 2024-02-10 10:22:59.909453676 +0900 KST m=+58.480958929)...
Executing migration v2.3.0 (at 2024-02-10 10:22:59.910460991 +0900 KST m=+58.481966244)...
Executing migration v2.3.1 (at 2024-02-10 10:22:59.911904966 +0900 KST m=+58.483410218)...
Executing migration v2.3.2 (at 2024-02-10 10:22:59.912923838 +0900 KST m=+58.484429182)...
Executing migration v2.4.0 (at 2024-02-10 10:22:59.915305163 +0900 KST m=+58.486810421)...
Executing migration v2.5.0 (at 2024-02-10 10:22:59.918768996 +0900 KST m=+58.490274254)...
Executing migration v2.5.2 (at 2024-02-10 10:22:59.919907401 +0900 KST m=+58.491412659)...
Executing migration v2.7.1 (at 2024-02-10 10:22:59.921143286 +0900 KST m=+58.492648554)...
Executing migration v2.7.4 (at 2024-02-10 10:22:59.923543363 +0900 KST m=+58.495048635)...
Executing migration v2.7.6 (at 2024-02-10 10:22:59.924924188 +0900 KST m=+58.496429442)...
Executing migration v2.7.8 (at 2024-02-10 10:22:59.926473538 +0900 KST m=+58.497978805)...
Executing migration v2.7.9 (at 2024-02-10 10:22:59.927871369 +0900 KST m=+58.499376645)...
Executing migration v2.7.10 (at 2024-02-10 10:22:59.92905554 +0900 KST m=+58.500560808)...
Executing migration v2.7.12 (at 2024-02-10 10:22:59.93038577 +0900 KST m=+58.501891037)...
Executing migration v2.7.13 (at 2024-02-10 10:22:59.931666361 +0900 KST m=+58.503171623)...
Executing migration v2.8.0 (at 2024-02-10 10:22:59.933243917 +0900 KST m=+58.504749184)...
Executing migration v2.8.1 (at 2024-02-10 10:22:59.935054445 +0900 KST m=+58.506559711)...
Executing migration v2.8.7 (at 2024-02-10 10:22:59.936213229 +0900 KST m=+58.507718490)...
Executing migration v2.8.8 (at 2024-02-10 10:22:59.937429837 +0900 KST m=+58.508935104)...
Executing migration v2.8.20 (at 2024-02-10 10:22:59.938625676 +0900 KST m=+58.510130929)...
Executing migration v2.8.25 (at 2024-02-10 10:22:59.940089794 +0900 KST m=+58.511595060)...
Executing migration v2.8.26 (at 2024-02-10 10:22:59.941186863 +0900 KST m=+58.512692116)...
Executing migration v2.8.36 (at 2024-02-10 10:22:59.942265291 +0900 KST m=+58.513770543)...
Executing migration v2.8.38 (at 2024-02-10 10:22:59.943964161 +0900 KST m=+58.515469429)...
Executing migration v2.8.39 (at 2024-02-10 10:22:59.945733052 +0900 KST m=+58.517238319)...
Executing migration v2.8.40 (at 2024-02-10 10:22:59.947060891 +0900 KST m=+58.518566157)...
Executing migration v2.8.42 (at 2024-02-10 10:22:59.948539647 +0900 KST m=+58.520044912)...
Executing migration v2.8.51 (at 2024-02-10 10:22:59.949867487 +0900 KST m=+58.521372756)...
Executing migration v2.8.57 (at 2024-02-10 10:22:59.951425384 +0900 KST m=+58.522930653)...
Executing migration v2.8.58 (at 2024-02-10 10:22:59.95272638 +0900 KST m=+58.524231646)...
Executing migration v2.8.91 (at 2024-02-10 10:22:59.954079473 +0900 KST m=+58.525584747)...
Executing migration v2.9.6 (at 2024-02-10 10:22:59.955464009 +0900 KST m=+58.526969283)...
Migrations Finished> Username: sigrid
> Email: sigrid@localhost
WARN[0063] no rows in result set level=Warn
> Your name: sigrid
> Password: qwe123 You are all setup sigrid!
Re-launch this program pointing to the configuration file./semaphore server --config /home/ubuntu/config.json To run as daemon:nohup ./semaphore server --config /home/ubuntu/config.json & You can login with sigrid@localhost or sigrid.
- 새로운 터미널 탭에는 태스크 실행 시 로그 확인 용도로 계쏙 터미널을 띄워둔다
- 데몬으로 띄워볼 수도 있다.
# Replace /path/to/semaphore and /path/to/config.json to your semaphore and config file path
sudo cat > /etc/systemd/system/semaphore.service <<EOF
[Unit]
Description=Semaphore Ansible
Documentation=https://github.com/ansible-semaphore/semaphore
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
ExecReload=/bin/kill -HUP $MAINPID
ExecStart=/path/to/semaphore service --config=/path/to/config.json
SyslogIdentifier=semaphore
Restart=always
RestartSec=10s[Install]
WantedBy=multi-user.target
EOF# Start the Semaphore service
sudo systemctl daemon-reload
sudo systemctl start semaphore# Check the Semaphore service status
sudo systemctl status semaphore# To make the Semaphore service auto start
sudo systemctl enable semaphore
Key Store에서 암호를 생성해보자
- ssh private key 복사하기
ubuntu@server:~$ cat /home/ubuntu/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
- Key Store에서 SSH Key를 생성한다
- root 계정의 login password도 가능하다
- None도 있는데, local filesystem을 repository로 등록할 때나 public repository의 경우에도 내가 키를 넣을 수 없다면 None을 설정할 수 있다. 기본적으로 Repository는 playbook과 role이 위치한 곳인데 보통 3가지의 저장소를 지원한다 (Git Repo, Local Filesystem, Remote Git Repo)
위와 같이 인증이 없을 경우 None 암호를 입력하게 된다. 추후 리모트 Repo를 통해 SSH Key가 필요한 경우를 연습해볼 수도 있겠다.
- Environment: Inventory에 추가 변수를 저장하는 데 JSON 포맷으로 작성해보자.
- Inventory: 플레이북이 실행될 호스트 목록과 변수를 사용할 수 있다.
- inventory에 있는 파일 변수 vs env에 있는 파일 변수 중 무엇이 우선할까? env에 있는 파일 변수가 우선한다.
- Task Template은 플레이북의 실행을 정의하는 템플릿이 있다. 여기서는 Task만 사용한다. 테스트용 플레이북을 만들어둔다.
cat << EOT > /tmp/semaphore/fact.yml
---
- hosts: all
tasks:
- name: Print all facts
ansible.builtin.debug:
msg: >
The default IPv4 address of {{ ansible_facts.fqdn }}
is {{ ansible_facts.default_ipv4.address }}
EOT
cat << EOT > /tmp/semaphore/user.yml
---
- hosts: web
tasks:
- name: Create User {{ user }}
ansible.builtin.user:
name: "{{ user }}"
state: present
EOT
다음과 같이 등록할 수 있다.
- Task를 실행해본다 (facts.yml)
Task 실행 성공 및 실패 여부가 슬랙 웹훅으로 고지된다
만약 마지막에 출력되는 \\n
을 바꾸고 싶다면 msg ->
로 수정해야 한다
- hosts: all
tasks:
- name: Print all facts
ansible.builtin.debug:
msg: -> # 여기를 바꾸면 된다.
The default IPv4 address of {{ ansible_facts.fqdn }}
is {{ ansible_facts.default_ipv4.address }}
태스크 템플릿이 실행될 때 inventory를 각각 만든다. 반복 작업이 있을 때마다 inventory의 별도 파일로 만든다.
ubuntu@server:/tmp/semaphore$ ls -l
total 44
-rw-rw-r-- 1 ubuntu ubuntu 207 Feb 10 10:53 fact.yml
-rw-rw-r-- 1 ubuntu ubuntu 78 Feb 10 11:08 inventory_2147483637
-rw-rw-r-- 1 ubuntu ubuntu 78 Feb 10 11:08 inventory_2147483638
-rw-rw-r-- 1 ubuntu ubuntu 32 Feb 10 11:07 inventory_2147483639
-rw-rw-r-- 1 ubuntu ubuntu 32 Feb 10 11:05 inventory_2147483640
-rw-rw-r-- 1 ubuntu ubuntu 32 Feb 10 11:04 inventory_2147483641
-rw-rw-r-- 1 ubuntu ubuntu 78 Feb 10 11:03 inventory_2147483642
-rw-rw-r-- 1 ubuntu ubuntu 78 Feb 10 11:02 inventory_2147483643
-rw-rw-r-- 1 ubuntu ubuntu 78 Feb 10 11:01 inventory_2147483644
-rw-rw-r-- 1 ubuntu ubuntu 78 Feb 10 11:00 inventory_2147483645
-rw-rw-r-- 1 ubuntu ubuntu 132 Feb 10 10:53 user.yml