怎样使用 Ansible 配置新的 Ubuntu 服务器

介绍

本指南将向您展示如何使用 Ansible 自动化初始 Ubuntu 服务器配置。 Ansible 是一种软件工具,可从本地控制节点自动配置一个或多个远程节点。 本地节点可以是其他 Linux 系统、Mac 或 Windows PC。 如果您使用 Windows PC,则可以使用以下命令安装 Ubuntu 适用于 Linux 的 Windows 子系统.

我们将使用一个 Ansible playbook 来执行以下操作:

升级已安装的 apt 软件包。 安装新的软件包。 设置完全限定域名 (FQDN)。 设置时区。 设置 SSH 端口号(允许设置非标准端口号)。 设置 sudo 密码超时(可以更改默认的 15 分钟超时)。 创建一个具有 sudo 权限的普通用户。 为 root 和新的普通用户创建一个 2 行提示。 为新的普通用户安装 SSH 密钥。 禁用 root 的密码验证。 禁用隧道明文密码。 使用 ufw 配置防火墙。 使用 fail2ban 配置蛮力缓解。 根据需要重新启动和重新启动服务。

先决条件

带有新安装的 Ubuntu 实例(20.04 或更高版本)的 Vultr 服务器。 本地 Mac、Windows(通过 WSL 安装 Ubuntu)或 Ubuntu 系统。 如果使用 Mac, 家酿 应该安装。 先前为 Vultr 主机生成的 SSH 密钥; 应为 root 用户安装 SSH 公钥。

1.在本地系统上安装Ansible

如果您使用的是安装了 Homebrew 的 Mac:

brew install ansible

如果您使用的是 Ubuntu:

apt install ansible

这将安装 Ansible 以及所有必需的依赖项。

创建一个简单的 Ansible 配置

创建 .ansible.cfg local_user 主目录中的配置文件。 这将告诉 Ansible 如何定位主机清单文件。

如果您使用的是 Mac,请添加以下内容:

[defaults]
inventory  = /Users/local_user/ansible/hosts.ini

如果您使用的是 Ubuntu:

[defaults]
inventory  = /home/local_user/ansible/hosts.ini

创建 hosts.ini 主机清单文件位于:

mkdir ~/ansible
cd ~/ansible

添加以下内容:

[vultr:vars]
user=remote_user
user_hashed_passwd="{{ remote_user_hashed_pw }}"
ssh_pub_key="{{ lookup('file', '~/.ssh/host_ed25519.pub')  }}"
ansible_become=yes
ansible_become_method=sudo 
ansible_become_pass="{{ remote_user_passwd }}"

[vultr]
host.example.com:22

user 是要创建的普通用户。 这 user_hashed_passwd 是以 Linux 散列格式存储的常规用户密码。 我们将描述如何创建它。 ssh_pub_key 指向 Vultr 主机的 SSH 公钥。 这 ansible_become 行为新创建的用户提供了执行 sudo 命令的能力(在未来的 ansible 剧本中)。 最后一行定义了完全限定的域名(带有 SSH 端口号)。 使用标准 SSH 端口 22 时, :22 端口声明不是必需的,但它显示了在使用非标准端口号时如何定义 SSH 端口号。

远程用户密码(和散列版本)与 Ansible playbook 存储在同一目录中。 它将存储在 Ansible Vault 中以提高密码安全性。

使用 Ansible Vault

为 Ansible 密码库和设置剧本创建目录:

$ mkdir -p ~/ansible/ubuntu
$ cd ~/ansible/ubuntu

您将需要用户密码的散列版本。 生成用户密码散列版本的最简单方法是登录到您的 Vultr Ubuntu 服务器并执行以下命令:

# mkpasswd --method=sha-512
Password:

这会生成一个很长的字符串,以 $6.

我复制并粘贴我的密码以避免输入密码时出现任何拼写错误。

创建 passwd.yml Ansible 密码保险库文件:

$ ansible-vault create passwd.yml
New Vault password: 
Confirm New Vault password:

输入密码库文件的密码(与您的用户密码不同)。 此命令将启动您的默认文本编辑器。 您应该添加以下内容:

remote_user_passwd: myRegualarUserPassword
remote_user_hashed_pw: '$6..........'

保存并退出编辑器。 这将创建一个只有 Ansible 可以读取的加密文件。 您可以使用以下命令查看文件的内容:

$ ansible-vault view passwd.yml
Vault password:

您可以使用以下命令编辑文件:

$ ansible-vault edit passwd.yml                
Vault password: 

您可以将多个用户添加到同一文件中。

2. 为 Vultr 主机创建一个 SSH 配置文件

这提供了以下功能:

$ ssh host
Welcome to Ubuntu 21.04 (GNU/Linux 5.11.0-18-generic x86_64)
o o o
[email protected]:~
$

. . . 以普通用户身份登录; 或者:

$ ssh [email protected]
Welcome to Ubuntu 21.04 (GNU/Linux 5.11.0-18-generic x86_64)
o o o
[email protected]:~
#

. . . 以 root 用户身份登录。

在这两种情况下,我都为主机名指定了一个快捷名称。 这假设您已经为 root 配置了基于 ssh-key 的身份验证并运行 Ansible playbook 来创建普通用户。

为此,创建(或更新) config 归档 ~/.ssh

Host *
  AddKeysToAgent yes
  UseKeychain yes
  IdentitiesOnly yes

Host host.example.com host
  Hostname host.example.com
  Port 22
  User remote_user
  IdentityFile ~/.ssh/host_ed25519

UserKeychain 特定于 macOS。 它将 SSH 公钥存储在 macOS 密钥链中。

host.example.com 是需要在本地系统上的 DNS 或 /etc/hosts 文件中定义的 FQDN。 Port 22 是可选的,但如果您定义了非标准 SSH 端口,则是必需的。

3. 测试您的 SSH/Ansible 配置

首先,验证 Ansible 是否正确安装在 Mac 上:

$ ansible --version
ansible [core 2.11.0] 
  config file = /Users/local_user/.ansible.cfg
  o o o

或在 Ubuntu 上:

$ ansible --version
ansible 2.9.6
  config file = /home/local_user/.ansible.cfg
  o o o

这些是目前 Mac/Homebrew 和 Ubuntu 20.04 上 Ansible 的最新版本。 需要注意的重要一点是配置文件行。

运行此命令以测试您的配置:

$ cd ~/ansible/ubuntu
$ ansible -m ping --ask-vault-pass --extra-vars '@passwd.yml' vultr -u root
Vault password: 
host.example.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

如果您看到上述输出,则一切正常。 如果没有,请返回并仔细检查所有配置设置。

4. 运行 Ansible Ubuntu 服务器配置手册

我们终于准备好运行 Ansible playbook,我将在稍后介绍。 确保您在 ~/ansible/ubuntu 目录。 这是您将运行的命令:

$ ansible-playbook --ask-vault-pass --extra-vars '@passwd.yml' setup-pb.yml -l vultr -u root
Vault password:

输入您的保管库密码。 该剧本将执行许多任务 PLAY RECAP 在最后。 您可以多次重新运行剧本,尤其是当您更改某些内容时。 它只会在需要时执行任务。

这是 setup-pb.yml 剧本:

# Initial server setup
#
---
- hosts: all
  become: true
  vars:
    ssh_port: "22"
    my_client_ip: XX.XX.XX.XX
    tmzone: America/Chicago
    sudo_timeout: 20
    f2b_jail_local: |
      [DEFAULT]
      ignoreip = 127.0.0.1/8 ::1 {{ my_client_ip }}

      [sshd]
      enabled = true
      port = {{ ssh_port }}
      maxretry = 3
      findtime = 15m
      bantime = 1h

  tasks:
    # Update and install the base software
    - name: Update apt package cache
      apt:
        update_cache: yes
        cache_valid_time: 600

    - name: Upgrade installed apt packages 
      apt:
        upgrade: dist
      register: upgrade

    - name: Ensure that these software packages are installed
      apt:
        pkg:
          - fail2ban
          - pwgen
          - needrestart
          - sudo
        state: latest

    - name: Check if a reboot is needed for Debian-based systems
      stat:
        path: /var/run/reboot-required
      register: reboot_required

    # Host Setup
    - name: Set static hostname
      hostname:
        name: "{{ inventory_hostname_short }}"

    - name: Add FQDN to /etc/hosts
      lineinfile:
        dest: /etc/hosts
        regexp: '^127.0.1.1'
        line: '127.0.1.1    {{ inventory_hostname }} {{ inventory_hostname_short }}'
        state: present

    - name: set timezone
      timezone:
        name: "{{ tmzone }}"

    - name: Set ssh port port number
      lineinfile:
        dest: /etc/ssh/sshd_config
        regexp: 'Port '
        line: 'Port {{ ssh_port }}'
        state: present
      notify:
        - restart sshd

    # Set sudo password timeout (default is 15 minutes)
    - name: Set sudo password timeout.
      lineinfile:
        path: /etc/sudoers
        state: present
        regexp: '^Defaultstenv_reset'
        line: 'Defaults env_reset, timestamp_timeout={{ sudo_timeout }}'
        validate: '/usr/sbin/visudo -cf %s'

    - name: Create/update regular user with sudo privileges
      user:
        name: "{{ user }}"
        password: "{{ user_hashed_passwd }}"
        state: present
        groups: sudo
        append: true
        shell: /bin/bash

    - name: Set user PS1 to a two-line prompt
      lineinfile:
        dest: "/home/{{ user }}/.bashrc"
        insertafter: EOF
        line: "PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\[email protected]\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\n\$ '"
        state: present

    - name: Set root PS1 to a two-line prompt
      lineinfile:
        path: '/root/.bashrc'
        state: present
        insertafter: EOF
        line: PS1='${debian_chroot:+($debian_chroot)}[email protected]:wn$ '

    - name: Ensure authorized keys for remote user is installed
      authorized_key:
        user: "{{ user }}"
        state: present
        key: "{{ ssh_pub_key }}"

    - name: Ensure authorized key for root user is installed
      authorized_key:
        user: root
        state: present
        key: "{{ ssh_pub_key }}"

    - name: Disable password authentication for root
      lineinfile:
        path: /etc/ssh/sshd_config
        state: present
        regexp: '^#?PermitRootLogin'
        line: 'PermitRootLogin prohibit-password'
      notify:
        - restart sshd

    - name: Disable tunneled clear-text passwords
      lineinfile:
        path: /etc/ssh/sshd_config
        state: present
        regexp: '^#?PasswordAuthentication'
        line: 'PasswordAuthentication no'
      notify:
        - restart sshd

    # Configure a firewall
    - name: Allow ssh port '{{ ssh_port }}'
      ufw:
        rule: allow
        port: '{{ ssh_port }}'
        proto: tcp
        state: enabled

    - name: Turn UFW logging off
      ufw:
        logging: "off"

    - name: configure fail2ban for ssh
      copy:
        dest: /etc/fail2ban/jail.local
        content: "{{ f2b_jail_local }}"
      notify:
        - restart fail2ban

    # simple shell script to display fail2ban-client status info; usage:
    #   f2bst
    #   f2bst sshd
    - name: Configure f2bst
      copy:
        dest: /usr/local/bin/f2bst
        content: |
          #!/usr/bin/sh
          fail2ban-client status $*
        owner: root
        group: root
        mode: 0750

    - name: run needrestart
      command: needrestart -r a
      when: upgrade.changed

    - name: Reboot the server if needed
      reboot:
        msg: "Reboot initiated by Ansible because of reboot required file."
        connect_timeout: 5
        reboot_timeout: 600
        pre_reboot_delay: 0
        post_reboot_delay: 30
        test_command: whoami
      when: reboot_required.stat.exists

    - name: Remove old packages from the cache
      apt:
        autoclean: yes

    - name: Remove dependencies that are no longer needed
      apt:
        autoremove: yes
        purge: yes

  handlers:
    - name: restart sshd
      service:
        name: sshd
        state: restarted
      when: reboot_required.stat.exists == false

    - name: restart fail2ban
      service:
        name: fail2ban
        state: restarted
      when: reboot_required.stat.exists == false

你可以阅读 Ansible 文档 了解有关 Ansible 的更多信息。

你应该只需要更新 vars: 部分更改您的特定情况的设置。 最有可能的是,您需要设置客户端 IP 和时区。 设置客户端 IP 可以防止一个人被 fail2ban 意外锁定。

结论

在本指南中,我们介绍了 Ansible 来自动化初始 Ubuntu 服务器设置。 这对于在测试应用程序后部署或重新部署服务器非常有用。