Daniel Garcia Pulpeiro logo

dpulpeiro.xyz

Streamlining Server Setup: Integrating GitHub Actions with Ansible

Introduction

In the dynamic world of DevOps, efficiency and security are paramount. This blog post dives into an innovative approach combining GitHub Actions and an Ansible playbook to automate and harden the setup of a Debian server. GitHub Actions facilitates continuous integration by automating workflows, while Ansible's playbook ensures the server is secure and configured according to best practices.

What the Playbook Does

The Ansible playbook, named "Harden Debian Server," performs a series of tasks aimed at securing and preparing a Debian server for deployment:

  1. User Management: Creates a non-root user with sudo privileges and ensures SSH keys are set up for secure access.
  2. Package Installation: Installs essential packages like Rsync, Sudo, jq, yq, and Docker, providing a robust environment for deployment and management.
  3. Security Enhancements: Adds the user to the sudoers file, installs and configures Uncomplicated Firewall (UFW) to manage network traffic, and disables SSH password authentication for increased security.

These tasks are executed in a sequence designed to minimize vulnerabilities and prepare the server for deployment.

Using the GitHub Action

To use the GitHub Action named "Setup Machine," follow these steps:

  1. Trigger the Action: To initiate the GitHub Action named "Setup Machine," you'll need to manually trigger the workflow using the workflow_dispatch event. This is typically done through the GitHub repository's Actions tab, where you can select the specific action and then click on "Run workflow."

    You will be prompted to provide several pieces of crucial information to ensure the action can interact with your server correctly:

    • SSH User: The username for SSH access to the remote server. It's important to have a user with sufficient permissions to carry out the necessary tasks.
    • SSH Password: While using SSH keys is a more secure method, if your setup requires a password, you'll need to provide it here. Ensure this is kept secure and consider using secrets to store sensitive information.
    • SSH Public Key: The public key for SSH authentication. This should correspond to a private key that the server recognizes and trusts.
    • Remote Host: The IP address or domain name of the server you wish to automate the setup for. Ensure that the host is accessible from the GitHub Actions runner environment.
    • Remote Port: The port on which the SSH server is listening. By default, this is typically port 22, but it may be different if you've configured it for security reasons or are using a non-standard setup.

    After filling out these fields, submit the form to trigger the action. The GitHub Action will then execute the steps defined in the workflow, using the provided information to establish an SSH connection to the remote host and carry out the subsequent tasks.

  2. Action Workflow:

    • Checkout Repository: The repository is checked out using actions/checkout@v3.
    • Set Up SSH: SSH keys are configured, and the target host is added to the known hosts.
    • Run Ansible Playbook: Finally, the Ansible playbook is executed using the provided credentials and host details.

Monitor and Review

Once initiated, monitor the action's progress and review logs for any errors or important information. It's important to delete the action, as the password will be shown in the logs.

The github action:


name: Setup Machine

on:
  workflow_dispatch:
    inputs:
      ssh_user:
        description: 'SSH user'
        required: true
        default: 'root'
      ssh_pass:
        description: 'SSH password'
        required: true
        default: ''
      ssh_public_key:
        description: 'SSH Public Key'
        required: true
        default: ''
      remote_host:
        description: 'Remote host. Example: 10.10.10.10'
        required: true
        default: ''
      remote_port:
        description: 'Remote port'
        required: true
        default: '22'

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Set up SSH
        run: |
          mkdir -p ~/.ssh
          ssh-keyscan -H ${{ github.event.inputs.remote_host }} >> ~/.ssh/known_hosts
          echo "${{ github.event.inputs.ssh_public_key }}" > ~/.ssh/id_rsa.pub

      - name: Run Ansible Playbook
        run: |
          echo $PWD
          ls
          sshpass -p ${{ github.event.inputs.ssh_pass }} ansible-playbook -i "${{ github.event.inputs.remote_host }}:${{ github.event.inputs.remote_port }}," resources/playbook-setup-machine.yml --user ${{ github.event.inputs.ssh_user }} --ask-pass

Ansible playbook:

- name: Harden Debian Server
  hosts: all
  become: yes

  vars:
    new_user: user
    public_key_file: ~/.ssh/id_rsa.pub  # Replace with the path to your public key file

  tasks:
    - name: Read SSH public key from a local file
      set_fact:
        ssh_public_key: "{{ lookup('file', public_key_file) }}"

    - name: Create a non-root user
      user:
        name: "{{ new_user }}"
        state: present
        create_home: yes
        shell: /bin/bash

    - name: Install Rsync
      apt:
        name: rsync
        state: present

    - name: Install Sudo
      apt:
        name: sudo
        state: present

    - name: Install jq
      apt:
        name: jq
        state: present

    - name: Install yq
      shell: >
        wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq && \
        chmod +x /usr/bin/yq
      args:
        creates: /usr/bin/yq

    - name: Add the user to the sudoers file without requiring a password
      lineinfile:
        path: /etc/sudoers
        line: "{{ new_user }} ALL=(ALL) NOPASSWD: ALL"
        validate: 'visudo -cf %s'

    - name: Ensure the .ssh directory exists
      file:
        path: "/home/{{ new_user }}/.ssh"
        state: directory
        mode: '0700'
        owner: "{{ new_user }}"
        group: "{{ new_user }}"

    - name: Add the given public key to the authorized keys
      authorized_key:
        user: "{{ new_user }}"
        state: present
        key: "{{ ssh_public_key }}"

    - name: Install Docker
      become: yes
      shell: curl -fsSL https://get.docker.com | sh

    - name: Add user to Docker group
      user:
        name: "{{ new_user }}"
        groups: docker
        append: yes
      become: yes

    - name: Install UFW
      apt:
        name: ufw
        state: present

    - name: Initialize Docker Swarm
      shell: docker swarm init --advertise-addr "{{ ansible_default_ipv4.address }}"
      become: yes

    - name: Allow selected ports and deny all others
      block:
        - name: Reset UFW rules
          ufw:
            state: reset

        - name: Deny all incoming by default
          ufw:
            direction: incoming
            policy: deny

        - name: Allow SSH (port 22)
          ufw:
            rule: allow
            port: 22

        - name: Allow HTTP (port 80)
          ufw:
            rule: allow
            port: 80

        - name: Allow HTTPS (port 443)
          ufw:
            rule: allow
            port: 443

        - name: Allow custom SSH port (2222)
          ufw:
            rule: allow
            port: 2222

    - name: Enable UFW
      ufw:
        state: enabled

    - name: Disable SSH password authentication
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^#?\s*PasswordAuthentication.*'
        line: 'PasswordAuthentication no'
        state: present
      notify: Restart ssh

  handlers:
    - name: Restart ssh
      service:
        name: ssh
        state: restarted

Final Thoughts

Integrating GitHub Actions with Ansible offers a streamlined, automated approach to server setup and security hardening. By combining these tools, teams can ensure consistent, secure, and efficient server deployments. Remember, while automation is powerful, always review and understand each step being automated to maintain security and efficiency in your operations.

In the post, I've included key sections of the code and YAML configurations to provide context and practical examples. You can further customize this template by adding specific examples from your setup or expanding on certain sections to provide more detailed explanations or advanced configurations. Link to Evergreen Template in github