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:
- User Management: Creates a non-root user with sudo privileges and ensures SSH keys are set up for secure access.
- Package Installation: Installs essential packages like Rsync, Sudo, jq, yq, and Docker, providing a robust environment for deployment and management.
- 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:
-
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.
-
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