국내의 경우, 서버를 운영 환경에서 관리하다보면 한국인터넷진흥원에서 등에서 제공하는 보안 취약점 항목들을 마주칠 것입니다.
입사 1년차때가 생각이 납니다. 선임을 도와 CSAP 인증을 진행하고 있었습니다.
수십대의 서버를 다루고 항목이 워낙 많다보니 결국 권한을 잘못 부여하였고, 설정파일을(pwquality.conf)을 잘못 수정하여 SSH 접속이 되지 않는 큰 실수를 해버렸습니다.(chown -R 명령어를 / 하위에 해버렸다.. 다행히 Snapshot이 있어, 복구는 했었다.)
그때, 앤서블을 활용하였다면 시간과 실수를 많이 줄일 수 있었는데 말입니다.
이번주차는 앤서블을 통해 보안설정 및 모니터링을 자동화 실습을 해보도록 하겠습니다.
1. 보안설정 자동화
1.1 패스워드 변경 주기 설정하기(chage)
- 계정 별 패스워드의 변경주기를 90일로 변경합니다.
- 기본적으로 패스워드 변경주기는 99999(영구)입니다.
- 패스워드 변경 주기를 설정할 계정 정보와 최대 변경일을 변수를 통해 별도 파일로 정의합니다.
변수파일을 작성
ansible,ubuntu 계정의 패스워드 변경 주기를 90일로 설정합니다.
# vars_maxdays.yml
---
Userinfo:
- username: ansible
maxdays: 90
- username: stack
maxdays: 90
메인 플레이북 작성
- vars_files 필드를 통해서 사전에 정의한 변수 파일을 변수로 사용합니다.
- ansible.builtin.user 모듈로 패스워드 변경주기를 변경할 수 있습니다.
- loop(반복문)을 이용해서 여러 계정들을 한번에 실행합니다.
# 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 }}"
플레이북 실행
# 문법 체크
ansible-playbook --syntax-check set_chage_password.yml
# 시뮬레이션
ansible-playbook --check set_chage_password.yml
# 확인 : chage -l ansible
man chage
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo chage -l ansible; echo; done
Password expires : never
...
Maximum number of days between password change : 99999
# 실행
ansible-playbook set_chage_password.yml
# 확인 : chage -l ansible
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo chage -l ansible; echo; done
>> tnode1 <<
Last password change : Jan 10, 2024
Password expires : Apr 09, 2024
...
Maximum number of days between password change : 90
1.2 패스워드 변경 주기 설정하기(pwquality)
- 패스워드 복잡성을 설정하기 위해서는 libpam-pwquality 패키지가 있어야 합니다.
- 패스워드 복잡도는 /etc/securiy/pwquality.conf 파일로 설정합니다.
- /etc/securiy/pwquality.conf 파일을 설정하기 전에 원본 파일을 백업합니다(중요!)
- Jinja2 템플릿 방식으로 pwquality.conf 파일의 내용을 구성합니다.
사전에 정의할 변수 파일을 작성합니다.
- 최소 패스워드 길이 설정 minlen
- 최소 숫자 개수 설정 dcredit
- 최소 대문자 개수 설정 ucredit
- 최소 소문자 개수 설정 lcredit
- 최소 특수문자 개수 설정 ocredit
- root 계정에서도 해당 패스워드 룰을 설정할 수 있는 enforce_for_root
# /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
{% 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 로 템플릿 파일 복사합니다.
# 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
- 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
# 실행
ansible-playbook set_password_rule.yml
# 확인
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo ls -l /etc/security; echo; done
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo cat /etc/security/pwquality.conf; echo; done
# 확인 : ansible 계정에 패스워드를 조건이 불충분하도록 입력 시도
ssh tnode2
-----------------
sudo su - ansible
whoami
pwd
passwd ansible
Changing password for ansible.
Current password: ansiblePw1
New password: qwer
BAD PASSWORD: The password contains less than 1 digits
New password: qwer1234
BAD PASSWORD: The password contains less than 1 uppercase letters
New password: Qwer1234
BAD PASSWORD: The password contains less than 1 non-alphanumeric characters
passwd: Have exhausted maximum number of retries for service
passwd: password unchanged
exit
exit
-----------------
1.2 디렉터리 및 파일 접근 권한 변경(World Writable)
- write(쓰기)권한은 파일 및 디렉토리를 변경하고 삭제할 수 있기 때문에 보안에서 아주 중요하게 관리되는 항목입니다.
- sticky bit(특수권한),world writable 설정 파일들은 루트이외에 사용자가 쓰기 권한을 갖도록합니다.
- Sticky bit가 설정된 파일은 파일을 생성한 소유자가 쓰기 권한을 부여받습니다.
- World Writable 파일은 Other(모든사용자)에게 쓰기 권한에 부여됩니다.
- 따라서, 해당하는 경우의 파일들을 찾아내고 권한을 수정합니다.
태스크 파일 작성
- Shell 모듈을 이용하여 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.file 모듈을 이용하여 파일의 접속 권한을 설정합니다.
- Sticky bit 파일은 u-s, g-s, o-s 로 설정하고, World Writable 파일은 o-w 로 설정
# 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 }}"
플레이북 실헹
# 문법 체크
ansible-playbook --syntax-check set_sticky_writable_files.yml
# 시뮬레이션
ansible-playbook --check set_sticky_writable_files.yml
# (예시) 확인
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo ls -al /usr/bin/newgrp; echo; done # -rwsr-xr-x
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo ls -al /var/crash; echo; done # drwxrwxrwt
# 실행
ansible-playbook set_sticky_writable_files.yml
# (예시) 변경 확인
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo ls -al /usr/bin/newgrp; echo; done # -rwxr-xr-x
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i sudo ls -al /var/crash; echo; done # drwxrwxr-t
# 한번 더 실행
ansible-playbook set_sticky_writable_files.yml
1.3 사설 인증서 생성(openssl)
- 사설 인증서에는 CSR로 자체 서명된 인증 기관용 인증서, 해당 인증서로 만든 클라이언트 인증 키가 존재한다.
- 각각의 항목들을 openssl에서 제공하는 모듈을 통해 생성한다.
- community.crypto.openssl_privatekey 개인 키 생성
- community.crypto.openssl_csr_pipe CSR 생성
- community.crypto.x509_certificate CSR을 이용한 인증서 생성
변수파일 생성
인증서 생성 시, 사용할 변수들을 정의합니다.
# 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
플레이북 작성
- become를 false로 설정하여 root가 아닌 ubuntu 계정에서 실행.
- pre_tasks 필드를 통해 롤이 실행되기 전에 인증서를 보관할 디렉토리를 먼저 생성
# 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
플레이북 실행
# 문법 체크
ansible-playbook --syntax-check make_certification.yml
# 실행
ansible-playbook make_certification.yml
# 확인
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i ls -l ~/tls; echo; done
>> tnode1 <<
-rw-r--r-- 1 root root 1858 Feb 6 23:18 ca_cert.crt
-rw------- 1 root root 3247 Feb 6 23:18 ca_priv.key
-rw-r--r-- 1 root root 1895 Feb 6 23:18 server_cert.crt
-rw------- 1 root root 3243 Feb 6 23:18 server_priv.key
...
2. 모니터링 자동화
2.1 팩트를 이용한 시스템 모니터링
수집하는 앤서블 팩트 중에서 인프라 정보 및 시스템과 관련된 정보들을 출력하고 로그를 저장합니다.
플레이북 작성
- 팩트 수집후 debug,file 모듈로 출력 및 해당 로그파일 저장
- 수집할 데이터
- 호스트 이름
- 커널 버전
- 네트워크 인터페이스 이름
- 네트워크 인터페이스 IP 주소
- 운영체제 버전
- CPU 개수
- 사용 가능한 메모리
# monitoring_facts.yml
---
- 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 }}"
- "################# 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 }}"
플레이북 실행
# 실행
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.logs
ansible-playbook monitoring_facts.yml
ssh tnode1 sudo cat /var/log/daily_check/system_info.logs
2.2 CPU,메모리,디스크 사용률 모니터링
- 팩트에서 제공되지 않는 정보들을 모니터링합니다.
- 자세한 CPU, 메모리, 디스크 사용률 모니터링을 위해 dstat, iostat, vmstat 명령어 도구를 사용합니다.
변수 파일 작성
저장할 로그 디렉터리와 설치할 패키지를 정의합니다.
# vars_packages.yml
---
log_directory: /home/ubuntu/logs
packages:
- dstat
- sysstat
플레이북 파일 작성
- when 조건절로 OS에 맞는 설치 방법을 실행합니다.
- loop 반복문으로 모니터링을 조회를 위한 패키지들을 설치합니다.
- shell 모듈을 통해 설치한 패키지 명령어로 모니터링 정보를 파일로 저장합니다.
# 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
'Automation > Ansible' 카테고리의 다른 글
앤서블(Ansible) 세마포어(Semaphore UI) - 4주차_2 (3) | 2024.02.08 |
---|---|
앤서블(Ansible) 롤과 인프라 구성 자동화 - 3주차 (0) | 2024.02.04 |
앤서블(Ansible) 반복문, 조건문, 핸들러 및 작업 실패 처리 - 2주차 (0) | 2024.01.21 |
앤서블(Ansible) 개념 및 기본 사용 - 1주차 (0) | 2024.01.12 |