웹사이트 검색

Ansible을 사용하여 여러 Ubuntu 22.04 서버의 초기 서버 설정을 자동화하는 방법


저자는 Write for DOnations 프로그램을 선택했습니다.

소개

자동화 플레이북.

Ansible은 에이전트가 없으므로 Ansible을 실행하려는 서버에 Ansible 구성 요소를 설치할 필요가 없습니다. 이러한 서버는 Ansible 호스트이며 WSL(Linux용 Windows 하위 시스템)이 설치되어 실행되어야 합니다.

이 튜토리얼에서는 Ansible을 사용하여 여러 Ubuntu 22.04 서버의 초기 서버 설정을 자동화합니다. 모든 서버에서 다음과 같은 초기 설정 작업을 수행합니다.

  • 설치된 패키지 업데이트
  • 관리자 권한이 있는 루트가 아닌 사용자 추가
  • 루트가 아닌 사용자에 대한 SSH 액세스 활성화
  • 방화벽 활성화
  • SSH 액세스용 포트 변경 및 방화벽을 사용하여 무차별 대입 공격으로부터 보호하고 서버의 전반적인 보안 강화
  • 루트 계정에 대한 원격 로그인 비활성화
  • 중요한 서비스가 활성 상태인지 확인
  • 더 이상 필요하지 않은 패키지 종속성 제거

Ansible을 사용하여 각 작업을 정의하는 포괄적인 플레이북을 실행하므로 해당 작업은 서버에 개별적으로 로그인할 필요 없이 단 하나의 명령으로 완료됩니다. 선택적 보조 플레이북을 실행하여 초기 서버 설정 후 서버 관리를 자동화할 수 있습니다.

전제 조건

이 자습서를 완료하려면 다음이 필요합니다.

  • 로컬 시스템 또는 원격 Linux 서버가 될 수 있는 제어 노드 역할을 할 시스템에 Ansible이 설치됩니다. Ansible을 설치하려면 다른 운영 체제에 필요한 공식 Ansible 설치 가이드의 1단계를 따르십시오.\n
    • 제어 노드가 원격 Ubuntu 22.04 서버인 경우 SSH 키 쌍 생성을 사용하여 설정해야 합니다.
    • 제어 노드에 설치된 Git. 널리 사용되는 Linux 배포판에 대한 Git 설치 방법 자습서를 따르십시오.
    • (선택 사항) EDITOR 환경 셸 변수에 연결된 텍스트 편집기를 변경하려면 Ansible Vault 사용 방법 자습서의 Ansible Vault 편집기 설정에서. 이 튜토리얼에서는 Ansible Vault의 편집기로 nano를 사용합니다.

    두 개 이상의 Ubuntu 22.04 서버 및 각 서버의 공용 IPv4 주소. Ansible을 사용하여 5단계에서 설정을 자동화하므로 사전 설정이 필요하지 않지만 위에서 언급한 Ansible 제어 노드에서 이러한 서버에 대한 SSH 액세스 권한이 있어야 합니다. DigitalOcean Droplets를 사용하는 경우 대시보드의 네트워킹 탭에 있는 각 서버의 공용 네트워크 섹션에서 IPv4 주소를 찾을 수 있습니다.

    • 제어 노드가 원격 Ubuntu 22.04 서버인 경우 ssh-copy-id를 사용하여 키 쌍을 호스트에 연결해야 합니다.

    1단계 - 제어 노드에서 SSH 클라이언트 구성 파일 수정

    이 단계에서는 제어 노드의 SSH 클라이언트 구성 파일에서 지시문을 수정합니다. 이렇게 변경한 후에는 자동으로 수락되므로 더 이상 원격 시스템의 SSH 키 지문을 수락하라는 메시지가 표시되지 않습니다. 각 원격 시스템에 대한 SSH 키 지문을 수동으로 수락하는 것은 번거로울 수 있으므로 이 수정은 Ansible을 사용하여 여러 서버의 초기 설정을 자동화할 때 확장 문제를 해결합니다.

    Ansible의 known_hosts 모듈을 사용하여 단일 호스트에 대한 SSH 키 지문을 자동으로 수락할 수 있지만 이 자습서에서는 여러 호스트를 다루므로 제어 노드에서 SSH 클라이언트 구성 파일을 수정하는 것이 더 효과적입니다( 일반적으로 로컬 컴퓨터).

    시작하려면 제어 노드에서 터미널 애플리케이션을 실행하고 nano 또는 선호하는 텍스트 편집기를 사용하여 SSH 클라이언트 구성 파일을 엽니다.

    1. sudo nano /etc/ssh/ssh_config

    StrictHostKeyChecking 지시문이 포함된 줄을 찾습니다. 주석을 제거하고 다음과 같이 값을 변경합니다.

    ...
       StrictHostKeyChecking accept-new
    ...
    

    파일을 저장하고 닫습니다. SSH 클라이언트 구성 파일만 수정했기 때문에 SSH 데몬을 다시 로드하거나 다시 시작할 필요가 없습니다.

    참고: StrictHostKeyChecking 값을 ask에서 accept-new로 영구적으로 변경하지 않으려면 기본값으로 되돌릴 수 있습니다. 7단계에서 플레이북을 실행한 후. 값을 변경하면 시스템이 SSH 키 지문을 자동으로 수락하지만 지문이 변경되면 동일한 호스트의 후속 연결을 거부합니다. 이 기능은 accept-new 변경이 해당 지시문의 값을 no로 변경하는 것만큼 보안 위험이 크지 않음을 의미합니다.

    이제 SSH 지시문을 업데이트했으므로 다음 단계에서 수행할 Ansible 구성을 시작합니다.

    2단계 - Ansible Hosts 파일 구성

    Ansible hosts 파일(인벤토리 파일이라고도 함)에는 Ansible 호스트에 대한 정보가 포함되어 있습니다. 이 정보에는 그룹 이름, 별칭, 도메인 이름 및 IP 주소가 포함될 수 있습니다. 이 파일은 기본적으로 /etc/ansible 디렉토리에 있습니다. 이 단계에서는 Prerequisites 섹션에서 스핀업한 Ansible 호스트의 IP 주소를 추가하여 이에 대해 Ansible 플레이북을 실행할 수 있습니다.

    시작하려면 nano 또는 선호하는 텍스트 편집기를 사용하여 hosts 파일을 엽니다.

    1. sudo nano /etc/ansible/hosts

    파일의 소개 주석 뒤에 다음 줄을 추가합니다.

    ...
    
    host1 ansible_host=host1-public-ip-address
    host2 ansible_host=host2-public-ip-address
    host3 ansible_host=host3-public-ip-address
    
    [initial]
    host1
    host2
    host3
    
    [ongoing]
    host1
    host2
    host3
    

    host1, host2host3는 별칭입니다. 초기 서버 설정을 자동화하려는 각 호스트에 대해. 별칭을 사용하면 다른 곳에서 호스트를 더 쉽게 참조할 수 있습니다. ansible_host는 Ansible 연결 변수이며 이 경우 대상 호스트의 IP 주소를 가리킵니다.

    initialongoing은 Ansible 호스트의 샘플 그룹 이름입니다. 호스트의 용도를 쉽게 알 수 있는 그룹 이름을 선택하십시오. 이러한 방식으로 호스트를 그룹화하면 호스트를 하나의 단위로 처리할 수 있습니다. 호스트는 둘 이상의 그룹에 속할 수 있습니다. 이 자습서의 호스트는 8단계의 초기 서버 설정을 위한 initial의 두 가지 다른 플레이북에서 사용되기 때문에 두 개의 다른 그룹에 할당되었습니다.

    hostN-public-ip-address는 각 Ansible 호스트의 IP 주소입니다. host1-public-ip-address와 후속 행을 자동화의 일부가 될 서버의 IP 주소로 바꾸십시오.

    파일 수정이 완료되면 저장하고 닫습니다.

    인벤토리 파일에서 호스트를 정의하면 Ansible 자동화로 설정할 호스트를 지정하는 데 도움이 됩니다. 다음 단계에서는 샘플 플레이북으로 리포지토리를 복제하여 다중 서버 설정을 자동화합니다.

    3단계 - Ansible Ubuntu 초기 서버 설정 리포지토리 복제

    이 단계에서는 이 자동화에 필요한 파일이 포함된 GitHub의 샘플 리포지토리를 복제합니다.

    이 리포지토리에는 샘플 다중 서버 자동화를 위한 세 개의 파일(initial.yml, ongoing.ymlvars/default.yml)이 포함되어 있습니다. initial.yml 파일은 초기 설정을 위해 Ansible 호스트에 대해 실행할 플레이와 작업이 포함된 기본 플레이북입니다. ongoing.yml 파일에는 초기 서버 설정 후 지속적인 유지 관리를 위해 호스트에 대해 실행할 작업이 포함되어 있습니다. vars/default.yml 파일에는 8단계에서 두 플레이북 모두에서 호출될 변수가 포함되어 있습니다.

    저장소를 복제하려면 다음 명령을 입력하십시오.

    1. git clone https://github.com/do-community/ansible-ubuntu.git

    또는 GitHub 계정에 SSH 키를 추가한 경우 다음을 사용하여 저장소를 복제할 수 있습니다.

    1. git@github.com:do-community/ansible-ubuntu.git

    이제 작업 디렉토리에 ansible-ubuntu라는 폴더가 생겼습니다. 그것으로 변경:

    1. cd ansible-ubuntu

    이 튜토리얼의 나머지 부분을 위한 작업 디렉토리가 됩니다.

    이 단계에서는 Ansible을 사용하여 여러 Ubuntu 22.04 서버를 자동화하기 위한 샘플 파일을 얻었습니다. 호스트에 특정한 정보가 포함된 파일을 준비하려면 시스템에서 작동하도록 vars/default.yml 파일을 업데이트합니다.

    4단계 - Ansible 변수 수정

    이 플레이북은 시간이 지남에 따라 업데이트해야 할 수 있는 자동화에 대한 일부 정보를 참조합니다. 해당 정보를 하나의 변수 파일에 배치하고 플레이북에서 변수를 호출하는 것이 플레이북 내에서 변수를 하드 코딩하는 것보다 더 효율적이므로 vars/default.yml 파일에서 변수를 수정하여 이 단계에서 기본 설정 및 설정이 필요합니다.

    시작하려면 nano 또는 선호하는 텍스트 편집기로 파일을 엽니다.

    1. nano vars/default.yml

    다음 변수를 포함하는 파일의 내용을 검토합니다.

    create_user: sammy
    
    ssh_port: 5995
    
    copy_local_key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}"
    

    create_user 변수의 값은 각 호스트에서 생성될 sudo 사용자의 이름이어야 합니다. 이 경우 sammy이지만 원하는 대로 사용자 이름을 지정할 수 있습니다.

    ssh_port 변수는 설정 후 Ansible 호스트에 연결하는 데 사용할 SSH 포트를 보유합니다. SSH의 기본 포트는 22이지만 포트를 변경하면 서버에 대한 자동 공격의 수가 크게 줄어듭니다. 이 변경은 선택 사항이지만 호스트의 보안 태세를 강화합니다. 102465535 사이에 있고 Ansible 호스트의 다른 애플리케이션에서도 사용하지 않는 잘 알려지지 않은 포트를 선택해야 합니다. 이 예에서는 포트 5995를 사용하고 있습니다.

    참고: 제어 노드가 Linux 배포판을 실행 중인 경우 /etc/services에서 1023grep보다 높은 숫자를 선택하십시오. 예를 들어 grep 5995 /etc/services를 실행하여 5995가 사용되고 있는지 확인합니다. 출력이 없으면 해당 파일에 포트가 없는 것이므로 변수에 할당할 수 있습니다. 제어 노드가 Linux 배포판이 아니고 시스템에서 해당 항목을 찾을 수 없는 경우 서비스 이름 및 전송 프로토콜 포트 번호 레지스트리를 참조할 수 있습니다.

    copy_local_key 변수는 제어 노드의 SSH 공개 키 파일을 참조합니다. 해당 파일의 이름이 id_rsa.pub인 경우 해당 줄을 변경할 필요가 없습니다. 그렇지 않으면 제어 노드의 SSH 공개 키 파일과 일치하도록 변경하십시오. 제어 노드의 ~/.ssh 디렉터리에서 파일을 찾을 수 있습니다. 5단계에서 기본 플레이북을 실행하고 sudo 권한이 있는 사용자가 생성된 후 Ansible 컨트롤러는 공개 키 파일을 사용자의 홈 디렉터리에 복사하므로 해당 사용자로 로그인할 수 있습니다. 초기 서버 설정 후 SSH를 통해.

    파일 수정이 완료되면 저장하고 닫습니다.

    이제 vars/default.yml의 변수에 값을 할당했으므로 Ansible은 8단계에서 플레이북을 실행하는 동안 해당 변수를 호출할 수 있습니다. 다음 단계에서는 Ansible을 사용합니다. 각 호스트에서 생성될 사용자의 암호를 생성하고 보호하기 위한 볼트.

    5단계 - Ansible Vault를 사용하여 암호화된 암호 파일 생성

    Ansible Vault는 플레이북에서 참조할 수 있는 파일 및 변수를 생성하고 암호화하는 데 사용됩니다. Ansible Vault를 사용하면 플레이북을 실행하는 동안 민감한 정보가 일반 텍스트로 전송되지 않습니다. 이 단계에서는 각 호스트에서 sudo 사용자의 암호를 만드는 데 사용할 값을 가진 변수가 포함된 파일을 만들고 암호화합니다. 이러한 방식으로 Ansible Vault를 사용하면 초기 서버 설정 도중 및 이후에 플레이북에서 암호가 일반 텍스트로 참조되지 않도록 할 수 있습니다.

    여전히 ansible-ubuntu 디렉토리에서 다음 명령을 사용하여 볼트 파일을 만들고 엽니다.

    1. ansible-vault create secret

    메시지가 표시되면 비밀 파일을 암호화하는 데 사용할 암호를 입력하고 확인합니다. 이것이 볼트 암호입니다. 8단계에서 플레이북을 실행하는 동안 볼트 암호가 필요하므로 잊지 마십시오.

    볼트 암호를 입력하고 확인하면 비밀 파일이 셸의 EDITOR 환경 변수에 연결된 텍스트 편집기에서 열립니다. 다음 줄을 파일에 추가하고 type_a_strong_password_heretype_a_salt_here의 값을 바꿉니다.

    1. password: type_a_strong_password_here
    2. password_salt: type_a_salt_here

    password 변수의 값은 각 호스트에서 생성할 sudo 사용자의 실제 비밀번호입니다. password_salt 변수는 salt 값을 사용합니다. 8단계.

    참고: 테스트에서 숫자로만 구성된 솔트가 8단계에서 플레이북을 실행하는 데 문제가 있음을 발견했습니다. 그러나 알파벳 문자로만 구성된 솔트는 작동했습니다. 영숫자 소금도 작동합니다. 소금을 지정할 때 이 점을 염두에 두십시오.

    파일 수정이 완료되면 저장하고 닫습니다.

    이제 호스트에서 sudo 사용자의 암호를 만드는 데 사용할 변수가 있는 암호화된 암호 파일을 만들었습니다. 다음 단계에서는 기본 Ansible 플레이북을 실행하여 2단계에서 지정한 서버의 초기 설정을 자동화합니다.

    6단계 - Ansible 호스트에 대해 기본 플레이북 실행

    이 단계에서는 Ansible을 사용하여 인벤토리 파일에 지정한 수만큼 서버의 초기 서버 설정을 자동화합니다. 기본 플레이북에 정의된 작업을 검토하는 것으로 시작합니다. 그런 다음 호스트에 대해 플레이북을 실행합니다.

    Ansible 플레이북은 각 플레이와 관련된 하나 이상의 작업이 있는 하나 이상의 플레이로 구성됩니다. Ansible 호스트에 대해 실행할 샘플 플레이북에는 총 14개의 작업이 있는 두 개의 플레이가 포함되어 있습니다.

    플레이북을 실행하기 전에 설정 프로세스와 관련된 각 작업을 검토합니다. 시작하려면 nano 또는 선호하는 텍스트 편집기로 파일을 엽니다.

    1. nano initial.yml

    플레이 1:

    파일의 첫 번째 섹션에는 플레이 동작에 영향을 미치는 다음 키워드가 포함되어 있습니다.

    - name: Initial server setup tasks
      hosts: initial
      remote_user: root
      vars_files:
        - vars/default.yml
        - secret
    ...
    

    name은 연극이 실행될 때 터미널에 표시되는 연극에 대한 짧은 설명입니다. hosts 키워드는 어떤 호스트가 연극의 대상인지를 나타냅니다. 이 경우 키워드에 전달되는 패턴은 2단계에서 /etc/ansible/hosts 파일에 지정한 호스트의 그룹 이름입니다. remote_user를 사용합니다. 호스트에 로그인하는 데 사용할 사용자 이름을 Ansible 컨트롤러에 알려주는 키워드입니다(이 경우 root). vars_files 키워드는 작업을 실행할 때 재생이 참조할 변수가 포함된 파일을 가리킵니다.

    이 설정으로 Ansible 컨트롤러는 SSH 포트 22를 통해 root 사용자로 호스트에 로그인을 시도합니다. 로그인할 수 있는 각 호스트에 대해 ok 응답을 보고합니다. 그렇지 않으면 서버에 연결할 수 없음이 있다고 보고하고 로그인할 수 있는 호스트에 대한 연극 작업 실행을 시작합니다. 이 설정을 수동으로 완료한 경우 이 자동화는 ssh root@host-ip-address를 사용하여 호스트에 로그인하는 것을 대체합니다.

    키워드 섹션 다음에는 순차적으로 실행할 작업 목록이 있습니다. 연극과 마찬가지로 각 작업은 작업이 수행할 작업에 대한 간단한 설명을 제공하는 이름으로 시작합니다.

    작업 1: 캐시 업데이트

    플레이북의 첫 번째 작업은 패키지 데이터베이스를 업데이트합니다.

    ...
    - name: update cache
      ansible.builtin.apt:
        update_cache: yes
    ...
    

    이 작업은 ansible.builtin.apt 모듈을 사용하여 패키지 데이터베이스를 업데이트하므로 update_cache: yes로 정의됩니다. 이 작업은 Ubuntu 서버에 로그인하고 sudo apt update를 입력할 때와 동일한 작업을 수행하며, 종종 설치된 모든 패키지를 업데이트하기 위한 전주곡입니다.

    작업 2: 설치된 모든 패키지 업데이트

    플레이북 업데이트 패키지의 두 번째 작업:

    ...
    - name: Update all installed packages
      ansible.builtin.apt:
        name: "*"
        state: latest
    ...
    

    첫 번째 작업과 마찬가지로 이 작업도 ansible.builtin.apt 모듈을 호출합니다. 여기에서 패키지(name: \*\) 및 state: latest를 지정하는 와일드카드를 사용하여 설치된 모든 패키지가 최신 상태인지 확인합니다. 서버에 로그인하고 sudo apt upgrade -y 명령을 실행합니다.

    작업 3: NTP 서비스가 실행 중인지 확인

    플레이북의 세 번째 작업은 NTP(Network Time Protocol) 데몬이 활성화되었는지 확인합니다.

    ...
    - name: Make sure NTP service is running
      ansible.builtin.systemd:
        state: started
        name: systemd-timesyncd
    ...
    

    이 작업은 ansible.builtin.systemd 모듈을 호출하여 systemd-timesyncd, NTP 데몬이 실행 중인지 확인합니다(state: started). 서버가 동일한 시간을 유지하도록 하려는 경우 이와 같은 작업을 실행하여 해당 서버에서 분산 응용 프로그램을 실행하는 데 도움이 될 수 있습니다.

    작업 4: sudo 그룹이 있는지 확인

    플레이북의 네 번째 작업은 sudo 그룹이 있는지 확인합니다.

    ...
    - name: Make sure we have a 'sudo' group
      ansible.builtin.group:
        name: sudo
        state: present
    ...
    

    이 작업은 ansible.builtin.group 모듈을 호출하여 sudo라는 이름의 그룹이 호스트(state: present)에 존재하는지 확인합니다. 다음 작업은 호스트에 sudo 그룹이 있는지에 따라 달라지므로 이 작업은 sudo 그룹이 있는지 확인하므로 다음 작업이 실패하지 않도록 할 수 있습니다. .

    작업 5: sudo 권한이 있는 사용자 만들기

    플레이북의 다섯 번째 작업은 sudo 권한이 있는 루트가 아닌 사용자를 생성합니다.

    ...
    - name: Create a user with sudo privileges
      ansible.builtin.user:
        name: "{{ create_user }}"
        state: present
        groups: sudo
        append: true
        create_home: true
        shell: /bin/bash
        password: "{{ password | password_hash('sha512', password_salt) }}"
        update_password: on_create
    ...
    

    여기에서 ansible.builtin.user 모듈을 호출하고 sudo 그룹을 사용자 그룹에 추가하여 각 호스트에서 사용자를 생성합니다. 사용자 이름은 vars/default.yml에서 지정한 create_user 변수의 값에서 파생됩니다. 이 작업은 또한 사용자를 위한 홈 디렉토리가 생성되고 적절한 셸이 할당되도록 합니다.

    password 매개변수와 SHA-512 암호화 해시 알고리즘에서 설정한 비밀번호와 소금의 조합을 사용하여 사용자의 해시된 비밀번호를 생성합니다. 비밀 볼트 파일과 쌍을 이루는 비밀번호는 일반 텍스트로 컨트롤러에 전달되지 않습니다. update_password를 사용하면 사용자가 처음 생성될 때만 해시된 암호가 설정되도록 할 수 있습니다. 플레이북을 다시 실행하면 암호가 다시 생성되지 않습니다.

    작업 6: 원격 사용자를 위한 인증 키 설정

    플레이북의 여섯 번째 작업은 사용자의 키를 설정합니다.

    ...
    - name: Set authorized key for remote user
      ansible.posix.authorized_key:
        user: "{{ create_user }}"
        state: present
        key: "{{ copy_local_key }}"
    ...
    

    이 작업에서는 ansible.posix.authorized_key를 호출하여 공개 SSH 키를 호스트에 복사합니다. user의 값은 이전 작업에서 호스트에 생성된 사용자 이름이고 key는 복사할 키를 가리킵니다. 두 변수 모두 var/default.yml 파일에 정의되어 있습니다. 이 작업은 ssh-copy-id 명령을 수동으로 실행하는 것과 동일한 효과가 있습니다.

    작업 7: 루트에 대한 원격 로그인 비활성화

    플레이북의 일곱 번째 작업은 root 사용자의 원격 로그인을 비활성화합니다.

    ...
    - name: Disable remote login for root
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        state: present
        regexp: '^PermitRootLogin yes'
        line: 'PermitRootLogin no'
    ...
    

    다음으로 ansible.builtin.lineinfile 모듈을 호출합니다. 이 작업은 정규식(regexp)을 사용하여 /etc/ssh/sshd_config 파일에서 PermitRootLogin으로 시작하는 줄을 검색한 다음 line의 값을 사용합니다. 이 작업은 이 플레이북에서 플레이를 실행한 후 root 계정을 사용한 원격 로그인이 실패하도록 합니다. 작업 6에서 만든 사용자 계정을 사용한 원격 로그인만 성공합니다. 원격 루트 로그인을 비활성화하면 일반 사용자만 로그인할 수 있고 관리자 권한을 얻기 위해 일반적으로 sudo와 같은 권한 에스컬레이션 방법이 필요합니다.

    작업 8: SSH 포트 변경

    플레이북의 여덟 번째 작업은 SSH 포트를 변경합니다.

    ...
    - name: Change the SSH port
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        state: present
        regexp: '^#Port 22'
        line: 'Port "{{ ssh_port }}"'
    ...
    

    SSH는 잘 알려진 포트 22에서 수신 대기하기 때문에 해당 포트를 대상으로 하는 자동화된 공격의 대상이 되는 경향이 있습니다. SSH가 수신 대기하는 포트를 변경하면 호스트를 공격하는 자동화된 공격의 수를 줄일 수 있습니다. 이 작업은 동일한 ansible.builtin.lineinfile 모듈을 사용하여 SSH 데몬의 구성 파일에서 regexp로 시작하고 해당 값을 < 코드>라인 매개변수. SSH가 수신하는 새 포트 번호는 4단계에서 ssh_port 변수에 할당한 포트 번호입니다. 이 플레이가 끝날 때 호스트를 다시 시작하면 로그인할 수 없습니다. 포트 22를 통해 호스트에.

    작업 9: UFW - SSH 연결 허용

    플레이북의 아홉 번째 작업은 SSH 트래픽을 허용합니다.

    ...
    - name: UFW - Allow SSH connections
      community.general.ufw:
        rule: allow
        port: "{{ ssh_port }}"
    ...
    

    여기에서 4단계를 호출합니다. 이 작업은 ufw allow 5995/tcp 명령을 수동으로 실행하는 것과 같습니다.

    작업 10: SSH에 대한 무차별 대입 시도 보호

    열 번째 작업은 무차별 대입 공격으로부터 보호합니다.

    ...
    - name: Brute-force attempt protection for SSH
      community.general.ufw:
        rule: limit
        port: "{{ ssh_port }}"
        proto: tcp
    ...
    

    community.general.ufw 모듈을 다시 호출하면 이 작업은 속도 제한 규칙을 사용하여 6회 이상의 연결 시도에 실패한 IP 주소에 대한 로그인 액세스를 거부합니다. 30초 기간 내의 SSH 포트. proto 매개변수는 대상 프로토콜(이 경우 TCP)을 가리킵니다.

    작업 11: UFW - 다른 수신 트래픽 거부 및 UFW 활성화

    열한 번째 작업은 방화벽을 활성화합니다.

    ...
    - name: UFW - Deny other incoming traffic and enable UFW
      community.general.ufw:
        state: enabled
        policy: deny
        direction: incoming
    ...
    

    이 작업에서 여전히 community.general.ufw 모듈에 의존하면서 방화벽을 활성화하고(state: enabled) 기본 정책을 설정합니다. 들어오는 모든 트래픽을 거부합니다.

    작업 12: 더 이상 필요하지 않은 종속성 제거

    이 연극의 열두 번째 작업은 패키지 종속성을 정리합니다.

    ...
    - name: Remove dependencies that are no longer required
      ansible.builtin.apt:
        autoremove: yes
    ...
    

    ansible.builtin.apt 모듈을 다시 호출하여 이 작업은 서버에서 더 이상 필요하지 않은 패키지 종속성을 제거합니다. 이는 sudo apt autoremove 명령을 실행하는 것과 동일합니다. 수동으로.

    작업 13: SSH 데몬 다시 시작

    이 플레이북의 열세 번째 작업은 SSH를 다시 시작합니다.

    ...
    - name: Restart the SSH daemon
      ansible.builtin.systemd:
        state: restarted
        name: ssh
    

    마지막 작업은 ansible.builtin.systemd 모듈을 호출하여 SSH 데몬을 다시 시작합니다. 데몬의 구성 파일에서 변경한 사항을 적용하려면 이 재시작을 수행해야 합니다. 이 작업은 sudo systemctl restart ssh를 사용하여 데몬을 다시 시작하는 것과 동일한 효과가 있습니다.

    호스트에 대한 초기 연결은 root 포트 22를 통해 이루어졌지만 이전 작업에서 포트 번호를 변경하고 원격 루트 로그인을 비활성화했기 때문에 SSH를 다시 시작해야 합니다. 극의 이 단계에서 데몬. 두 번째 플레이는 다른 연결 자격 증명(root 대신 사용자 이름 및 22가 아닌 새로 정의된 포트 번호)을 사용합니다.

    플레이 2: 초기 설정 후 호스트 재부팅

    이 플레이는 플레이 1의 마지막 작업이 성공적으로 완료된 후 시작됩니다. 다음 키워드의 영향을 받습니다.

    ...
    - name: Rebooting hosts after initial setup
      hosts: initial
      port: "{{ ssh_port }}"
      remote_user: "{{ create_user }}"
      become: true
      vars_files:
        - vars/default.yml
        - ~/secret
      vars:
        ansible_become_pass: "{{ password }}"
    ...
    

    hosts 키워드에 전달되는 패턴은 4단계에서 /etc/ansible/hosts 파일에 지정된 initial 그룹 이름입니다.

    첫 번째 플레이에서 Ansible 컨트롤러는 root 사용자로 호스트에 로그인했습니다. 첫 번째 플레이에서 원격 루트 로그인이 비활성화되었으므로 이제 Ansible 컨트롤러가 로그인해야 하는 사용자를 지정해야 합니다. remote_user 키워드는 Ansible 컨트롤러가 첫 번째 플레이의 작업 5에서 생성된 sudo 사용자로 각 호스트에 로그인하도록 지시합니다.

    become 키워드는 정의된 호스트에서 작업 실행에 사용되는 권한 에스컬레이션을 지정합니다. 이 키워드는 필요할 때 호스트에서 작업을 실행하기 위한 루트 권한을 가정하도록 Ansible 컨트롤러에 지시합니다. 이 경우 컨트롤러는 sudo를 사용하여 루트 권한을 가정합니다. ansible_become_pass 키워드는 루트 권한을 가정하는 데 사용되는 암호인 권한 에스컬레이션 암호를 설정합니다. 이 경우 5단계에서 Ansible Vault를 사용하여 구성한 비밀번호로 변수를 가리킵니다.

    vars/default.yml 파일을 가리키는 것 외에도 vars_files 키워드는 5단계에서 구성한 비밀 파일도 가리킵니다. password 변수를 찾을 위치를 Ansible 컨트롤러에 알려줍니다.

    키워드 섹션 뒤에는 이 극에서 실행될 고독한 작업이 있습니다.

    작업 14: 모든 호스트 재부팅

    참고: 이것은 두 번째 플레이의 첫 번째 작업이지만 Ansible Controller는 이를 플레이 2의 태스크 1이 아니라 플레이북의 태스크 14로 보기 때문에 태스크 14로 번호가 지정됩니다.

    플레이북의 마지막 작업은 모든 호스트를 재부팅합니다.

    ...
    - name: Reboot all hosts
      ansible.builtin.reboot:
    

    첫 번째 플레이에서 작업을 완료한 후 호스트를 재부팅하면 애플리케이션 설치를 시작하기 전에 커널 또는 라이브러리에 대한 모든 업데이트가 적용됩니다.

    전체 플레이북 파일은 다음과 같습니다.

    - name: Initial server setup tasks
      hosts: initial
      remote_user: root
      vars_files:
        - vars/default.yml
        - secret
    
      tasks:
        - name: update cache
          ansible.builtin.apt:
            update_cache: yes
    
        - name: Update all installed packages
          ansible.builtin.apt:
            name: "*"
            state: latest
    
        - name: Make sure NTP service is running
          ansible.builtin.systemd:
            state: started
            name: systemd-timesyncd
    
        - name: Make sure we have a 'sudo' group
          ansible.builtin.group:
            name: sudo
            state: present
    
        - name: Create a user with sudo privileges
          ansible.builtin.user:
            name: "{{ create_user }}"
            state: present
            groups: sudo
            append: true
            create_home: true
            shell: /bin/bash
            password: "{{ password | password_hash('sha512', password_salt) }}"
            update_password: on_create
    
        - name: Set authorized key for remote user
          ansible.builtin.authorized_key:
            user: "{{ create_user }}"
            state: present
            key: "{{ copy_local_key }}"
    
        - name: Disable remote login for root
          ansible.builtin.lineinfile:
            path: /etc/ssh/sshd_config
            state: present
            regexp: '^PermitRootLogin yes'
            line: 'PermitRootLogin no'
    
        - name: Change the SSH port
          ansible.builtin.lineinfile:
            path: /etc/ssh/sshd_config
            state: present
            regexp: '^#Port 22'
            line: 'Port "{{ ssh_port }}"'
    
        - name: UFW - Allow SSH connections
          community.general.ufw:
            rule: allow
            port: "{{ ssh_port }}"
    
        - name: Brute-force attempt protection for SSH
          community.general.ufw:
            rule: limit
            port: "{{ ssh_port }}"
            proto: tcp
    
        - name: UFW - Deny other incoming traffic and enable UFW
          community.general.ufw:
            state: enabled
            policy: deny
            direction: incoming
    
        - name: Remove dependencies that are no longer required
          ansible.builtin.apt:
            autoremove: yes
    
        - name: Restart the SSH daemon
          ansible.builtin.systemd:
            state: restarted
            name: ssh
    
    - name: Rebooting hosts after initial setup
      hosts: initial
      port: "{{ ssh_port }}"
      remote_user: "{{ create_user }}"
      become: true
      vars_files:
        - vars/default.yml
        - secret
      vars:
        ansible_become_pass: "{{ password }}"
    
      tasks:
        - name: Reboot all hosts
          ansible.builtin.reboot:
    

    파일 검토가 끝나면 저장하고 닫습니다.

    참고: 플레이북에 새 작업을 추가하거나 기존 작업을 수정할 수 있습니다. 그러나 YAML은 간격에 민감하기 때문에 YAML 파일을 변경하면 파일이 손상될 수 있으므로 파일의 모든 측면을 편집할 경우 주의하십시오. Ansible 플레이북 작업에 대한 자세한 내용은 Ansible 플레이북 작성 방법 시리즈를 따르십시오.

    이제 플레이북을 실행할 수 있습니다. 먼저 구문을 확인합니다.

    1. ansible-playbook --syntax-check --ask-vault-pass initial.yml

    5단계에서 생성한 볼트 암호를 입력하라는 메시지가 표시됩니다. 인증에 성공한 후 YAML 구문에 오류가 없으면 출력은 다음과 같습니다.

    Output
    playbook: initial.yml

    이제 다음 명령으로 파일을 실행할 수 있습니다.

    1. ansible-playbook --ask-vault-pass initial.yml

    볼트 암호를 묻는 메시지가 다시 나타납니다. 인증에 성공하면 Ansible 컨트롤러는 root 사용자로 각 호스트에 로그인하고 플레이북의 모든 작업을 수행합니다. 각 서버에서 개별적으로 ssh root@node-ip-address 명령을 실행하는 대신 Ansible은 /etc/ansible/hosts 에 지정된 모든 노드에 연결합니다. 플레이북의 작업을 실행합니다.

    이 자습서의 샘플 호스트의 경우 Ansible이 3개 호스트에서 작업을 완료하는 데 약 3분이 걸렸습니다. 작업이 완료되면 다음과 같은 출력이 표시됩니다.

    Output
    PLAY RECAP ***************************************************************************************************** host1 : ok=16 changed=11 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 host2 : ok=16 changed=11 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 host3 : ok=16 changed=11 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

    성공적으로 평가된 각 작업 및 재생 키워드 섹션은 ok 열의 숫자에 포함됩니다. 두 번의 플레이에 걸쳐 14개의 작업이 있고 모두 성공적으로 평가된 경우 카운트는 16입니다. 평가된 작업 중 11변경됨 열로 표시되는 서버의 변경으로 이어졌습니다.

    도달할 수 없는 수는 Ansible 컨트롤러가 로그인할 수 없는 호스트 수를 나타냅니다. 실패한 작업이 없으므로 실패의 수는 0입니다.

    작업에 지정된 조건이 충족되지 않으면 작업을 건너뜀합니다(일반적으로 when 매개변수 사용). 이 경우 건너뛰는 작업은 없지만 8단계에서 적용할 수 있습니다.

    마지막 두 열(rescuedignored)은 재생 또는 작업에 대해 지정된 오류 처리와 관련됩니다.

    이제 플레이북에서 지정한 모든 작업을 완료하는 하나의 명령을 실행하기 위해 Ansible을 사용하여 여러 Ubuntu 22.04 서버의 초기 서버 설정을 성공적으로 자동화했습니다.

    모든 것이 예상대로 작동하는지 확인하기 위해 호스트 중 하나에 로그인하여 설정을 확인합니다.

    (선택 사항) 7단계 - 수동으로 서버 설정 확인

    이전 단계 끝에서 재생 요약의 출력을 확인하려면 설정을 확인하기 위해 이전에 구성한 자격 증명을 사용하여 호스트 중 하나에 로그인할 수 있습니다. 이러한 작업은 Ansible 요약이 정확한 완료를 보고하기 때문에 학습 목적으로 선택 사항입니다.

    다음 명령을 사용하여 호스트 중 하나에 로그인하여 시작합니다.

    1. ssh -p 5995 sammy@host1-public-ip-address

    -p 옵션을 사용하여 6단계에서 SSH에 대해 구성한 사용자 지정 포트 번호를 가리킵니다. 해당 포트를 통해 해당 사용자로 호스트에 로그인할 수 있다면 Ansible은 이러한 작업을 성공적으로 완료했습니다.

    로그인한 후 패키지 데이터베이스를 업데이트할 수 있는지 확인하십시오.

    1. sudo apt update

    암호를 입력하라는 메시지가 표시되고 5단계에서 사용자에 대해 구성한 암호로 인증할 수 있는 경우 Ansible이 사용자 생성 및 사용자 암호 설정 작업을 성공적으로 완료했음을 확인할 수 있습니다.

    이제 설정 플레이북이 의도한 대로 작동했음을 알았으므로 지속적인 유지 관리를 위해 두 번째 플레이북을 실행할 수 있습니다.

    (선택 사항) 8단계 - 호스트의 지속적인 유지 관리를 위해 Ansible 사용

    3단계에서 실행한 초기 서버 설정 플레이북에서 지속적인 유지 관리에 사용할 수 있는 ongoing.yml 플레이북도 가져왔습니다. 이 단계에서는 ongoing.yml 플레이북을 실행하여 이 자습서에서 설정한 호스트의 지속적인 유지 관리를 자동화합니다.

    플레이북을 실행하기 전에 각 작업을 검토합니다. 시작하려면 nano 또는 선호하는 텍스트 편집기로 파일을 엽니다.

    1. nano ongoing.yml

    초기 설정 플레이북과 달리 유지 관리 플레이북에는 단 하나의 플레이와 더 적은 수의 작업만 포함됩니다.

    플레이 1:

    파일의 첫 번째 섹션에 있는 다음 키워드는 연극의 동작에 영향을 미칩니다.

    - hosts: ongoing
      port: "{{ ssh_port }}"
      remote_user: "{{ create_user }}"
      become: true
      vars_files:
        - vars/default.yml
        - secret
      vars:
        ansible_become_pass: "{{ password }}"
    ...
    

    hosts 키워드에 전달된 그룹 외에 설정 플레이북의 두 번째 플레이에서 사용된 것과 동일한 키워드입니다.

    키워드 뒤에는 순차적으로 실행할 작업 목록이 있습니다. 설정 플레이북에서와 같이 유지 관리 플레이북의 각 작업은 수행할 작업에 대한 간단한 설명을 제공하는 이름으로 시작합니다.

    작업 1: 캐시 업데이트

    첫 번째 작업은 패키지 데이터베이스를 업데이트합니다.

    ...
    - name: update cache
      ansible.builtin.apt:
        update_cache: yes
    ...
    

    이 작업은 ansible.builtin.apt 모듈을 사용하여 패키지 데이터베이스를 업데이트하므로 update_cache: yes로 정의됩니다. 이 작업은 Ubuntu 서버에 로그인하고 sudo apt update를 입력할 때와 동일한 작업을 수행합니다. 이는 종종 패키지를 설치하거나 설치된 모든 패키지를 업데이트하기 위한 전주곡입니다.

    작업 2: 설치된 모든 패키지 업데이트

    두 번째 작업은 패키지를 업데이트합니다.

    ...
    - name: Update all installed packages
      ansible.builtin.apt:
        name: "*"
        state: latest
    ...
    

    첫 번째 작업과 마찬가지로 이 작업도 ansible.builtin.apt 모듈을 호출합니다. 여기에서 패키지(name: \*\) 및 state: latest를 지정하는 와일드카드를 사용하여 설치된 모든 패키지가 최신 상태인지 확인합니다. 서버에 로그인하고 sudo apt upgrade -y 명령을 실행합니다.

    작업 3: NTP 서비스가 실행 중인지 확인

    플레이북의 세 번째 작업은 NTP 데몬이 설정되었는지 확인합니다.

    ...
    - name: Make sure NTP service is running
      ansible.builtin.systemd:
        state: started
        name: systemd-timesyncd
    ...
    

    서버의 활성 서비스는 다양한 이유로 실패할 수 있으므로 이러한 서비스가 활성 상태로 유지되도록 해야 합니다. 이 작업은 ansible.builtin.systemd 모듈을 호출하여 NTP 데몬인 systemd-timesyncd가 활성 상태로 유지되도록 합니다(state: started).

    작업 4: UFW - 실행 중입니까?

    네 번째 작업은 UFW 방화벽의 상태를 확인합니다.

    ...
    - name: UFW - Is it running?
      ansible.builtin.command: ufw status
        register: ufw_status
    ...
    

    sudo ufw status 명령을 사용하여 Ubuntu에서 UFW 방화벽의 상태를 확인할 수 있습니다. 출력의 첫 번째 줄에는 Status: active 또는 Status: inactive가 표시됩니다. 이 작업은 ansible.builtin.command 모듈을 사용하여 동일한 명령을 실행한 다음 출력을 ufw_status에 저장(등록)합니다. 변수. 해당 변수의 값은 다음 작업에서 쿼리됩니다.

    작업 5: UFW - UFW 활성화 및 들어오는 트래픽 거부

    다섯 번째 작업은 중지된 경우 UFW 방화벽을 다시 활성화합니다.

    ...
    - name: UFW - Enable UFW and deny incoming traffic
      community.general.ufw:
        state: enabled
      when: "'inactive' in ufw_status.stdout"
    ...
    

    이 작업은 community.general.ufw 모듈을 호출하여 ufw_status의 출력에 inactive라는 용어가 나타날 때만 방화벽을 활성화합니다. 변수. 방화벽이 활성화되어 있으면 when 조건이 충족되지 않고 작업이 건너뜀으로 표시됩니다.

    작업 6: 더 이상 필요하지 않은 종속성 제거

    이 플레이북의 여섯 번째 작업은 패키지 종속성을 정리합니다.

    ...
    - name: Remove dependencies that are no longer required
      ansible.builtin.apt:
        autoremove: yes
    ...
    

    이 작업은 ansible.builtin.apt 모듈을 호출하여 서버에서 더 이상 필요하지 않은 패키지 종속성을 제거합니다. 이는 sudo apt autoremove 명령을 실행하는 것과 같습니다.

    작업 7: 재부팅이 필요한지 확인

    일곱 번째 작업은 재부팅이 필요한지 확인합니다.

    ...
    - name: Check if reboot required
      ansible.builtin.stat:
        path: /var/run/reboot-required
      register: reboot_required
    ...
    

    Ubuntu에서 새로 설치되거나 업그레이드된 패키지는 /var/run/reboot-required 파일을 생성하여 설치 또는 업그레이드로 도입된 변경 사항을 적용하려면 재부팅이 필요하다는 신호를 보냅니다. stat /var/run/reboot-required 명령을 사용하여 해당 파일이 존재하는지 확인할 수 있습니다. 이 작업은 ansible.builtin.stat 모듈을 호출하여 동일한 작업을 수행한 다음 출력을 reboot_required에 저장(등록)합니다. 변수. 해당 변수의 값은 다음 작업에서 쿼리됩니다.

    작업 8: 필요한 경우 재부팅

    여덟 번째 작업은 필요한 경우 서버를 재부팅합니다.

    ...
    - name: Reboot if required
      ansible.builtin.reboot:
      when: reboot_required.stat.exists == true
    

    작업 7에서 reboot_required 변수를 쿼리하여 이 작업은 ansible.builtin.reboot 모듈을 호출하여 호스트를 경우 에만 재부팅합니다. /var/run/reboot-required가 존재합니다. 재부팅이 필요하고 호스트가 재부팅되면 작업이 변경됨으로 표시됩니다. 그렇지 않으면 Ansible이 재생 요약에서 건너뛰기로 표시합니다.

    지속적인 유지 관리를 위한 전체 플레이북 파일은 다음과 같습니다.

    - hosts: ongoing
      port: "{{ ssh_port }}"
      remote_user: "{{ create_user }}"
      become: true
      vars_files:
        - vars/default.yml
        - secret
      vars:
        ansible_become_pass: "{{ password }}"
    
      tasks:
        - name: update cache
          ansible.builtin.apt:
            update_cache: yes
    
        - name: Update all installed packages
          ansible.builtin.apt:
            name: "*"
            state: latest
    
        - name: Make sure NTP service is running
          ansible.builtin.systemd:
            state: started
            name: systemd-timesyncd
    
        - name: UFW - Is it running?
          ansible.builtin.command: ufw status
          register: ufw_status
          
        - name: UFW - Enable UFW and deny incoming traffic
          community.general.ufw:
            state: enabled
          when: "'inactive' in ufw_status.stdout"
    
        - name: Remove dependencies that are no longer required
          ansible.builtin.apt:
            autoremove: yes
    
        - name: Check if reboot required
          ansible.builtin.stat:
            path: /var/run/reboot-required
          register: reboot_required
    
        - name: Reboot if required
          ansible.builtin.reboot:
          when: reboot_required.stat.exists == true
    

    파일 검토가 끝나면 저장하고 닫습니다.

    참고: 플레이북에서 새 작업을 추가하거나 기존 작업을 수정할 수 있습니다. 그러나 YAML은 간격에 민감하기 때문에 YAML 파일을 변경하면 파일이 손상될 수 있으므로 파일의 모든 측면을 편집할 경우 주의하십시오. Ansible 플레이북 작업에 대한 자세한 내용은 Ansible 플레이북 작성 방법 시리즈를 따르십시오.

    이제 파일을 실행할 수 있습니다. 먼저 구문을 확인합니다.

    1. ansible-playbook --syntax-check --ask-vault-pass ongoing.yml

    5단계에서 생성한 볼트 암호를 입력하라는 메시지가 표시됩니다. 인증에 성공한 후 YAML 구문에 오류가 없으면 출력은 다음과 같습니다.

    Output
    playbook: ongoing.yml

    이제 다음 명령으로 파일을 실행할 수 있습니다.

    1. ansible-playbook --ask-vault-pass ongoing.yml

    볼트 암호를 입력하라는 메시지가 표시됩니다. 인증에 성공한 후 Ansible 컨트롤러는 각 호스트에 sammy(또는 지정한 사용자 이름)로 로그인하여 플레이북의 작업을 수행합니다. 각 서버에서 개별적으로 ssh -p 5995 sammy@host_ip_address 명령을 실행하는 대신 Ansible은 노드에 연결합니다. /etc/ansible/hostsongoing 그룹에서 지정한 다음 플레이북의 작업을 실행합니다.

    명령이 성공적으로 완료되면 다음 출력이 인쇄됩니다.

    Output
    PLAY RECAP ***************************************************************************************************** host1 : ok=7 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 host2 : ok=7 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 host3 : ok=7 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0

    초기 서버 설정에 대한 재생 요약과 달리 이 재생 요약은 when 매개변수로 각 작업에 대해 설정된 조건이 충족되지 않았기 때문에 건너뛴 두 작업을 기록합니다.

    각 호스트에 수동으로 로그인할 필요 없이 이 플레이북을 사용하여 호스트를 유지할 수 있습니다. 호스트에서 애플리케이션을 빌드하고 설치할 때 플레이북에 작업을 추가하여 Ansible로 해당 애플리케이션을 관리할 수도 있습니다.

    결론

    이 자습서에서는 Ansible을 사용하여 여러 Ubuntu 22.04 서버의 초기 설정을 자동화했습니다. 또한 해당 서버의 지속적인 유지 관리를 위해 보조 플레이북을 실행했습니다. Ansible 자동화는 분산 또는 클러스터 모드에서 MinIO와 같은 애플리케이션을 설정해야 할 때 시간을 절약해 주는 도구입니다.

    Ansible에 대한 자세한 내용은 구성 관리 101: Ansible 플레이북 작성에서 확인할 수 있습니다.