Ansible
Provisioning your server
For most applications running whole Kubernetes clusters is overkill since they tend not to need the horizontal scaling that it offers. Most medium to large scale applications can still run well on a single VPS. Certainly anything I have running in my home lab.
The following provision script configures a fresh Ubuntu box so that it is ready to serve Docker containers safely. It also configures the shell environment and makes sure the latest version of neovim gets installed through Asdf, a tool version manager.
Variables are pulled in from a separate variable file encrypted with ansible-vault encrypt <filepath>
which looks like this:
user:
name: <USERNAME>
password: <PASSWORD>
salt: <PASSWORD_SALT>
ssh_key: <PUBLIC_SSH_KEY_FILENAME>
Note
This playbook sets up the user environment and then revokes root SSH access. After running it you will no longer be able to SSH into your machine as root. Use the newly created user for all subsequent logins for example: ssh 99linesofcode@srv01
---
- name: Provision
hosts: personal
vars_files:
- ./variables.yaml
tasks:
- name: Add Docker public signing key
ansible.builtin.apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker repository to sources list
ansible.builtin.apt_repository:
repo: deb [signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu jammy stable
state: present
- name: Update package registry
ansible.builtin.apt:
upgrade: dist
- name: Install base dependencies
ansible.builtin.apt:
pkg:
- ca-certificates
- clang
- curl
- gnupg
- containerd.io
- dbus-user-session
- docker-ce
- docker-ce-cli
- docker-buildx-plugin
- docker-compose-plugin
- fd-find
- git
- neofetch
- ripgrep
- tmux
- uidmap
- zsh
# NOTE running Docker in rootless mode has its limitations: https://docs.docker.com/engine/security/rootless/#known-limitations
- name: Disable and mask Docker systemd services
ansible.builtin.systemd_service:
name: "{{ item }}"
enabled: false
masked: true
loop:
- docker.service
- docker.socket
- name: Ensure user group 1000 exists
ansible.builtin.group:
name: "{{ user.name }}"
state: present
gid: 1000
- name: Create user "{{ user.name }}", set group id and add to relevant groups
ansible.builtin.user:
name: "{{ user.name }}"
password: "{{ user.password | password_hash('sha512', user.salt) }}"
append: true
uid: 1000
group: 1000
groups:
- sudo
- docker
shell: /bin/zsh
- name: Configure user environment
block:
- name: Setup rootless Docker daemon
ansible.builtin.shell: dockerd-rootless-setuptool.sh install
- name: Enable Docker systemd user service
ansible.builtin.systemd_service:
name: docker.service
enabled: true
scope: user
- name: Create user directories
ansible.builtin.file:
path: "$HOME/{{ item }}"
state: directory
mode: 0700
loop:
- .ssh/
- .config/
- .local/
- .local/bin/
- .local/share/
- .local/state/
- name: Allow SSH access for user "{{ user.name }}"
ansible.posix.authorized_key:
user: "{{ user.name }}"
state: present
key: "{{ lookup('file', lookup('env', 'HOME') + '/.ssh/' + ssh_key) }}"
- name: Pull dotfiles repository
ansible.builtin.git:
repo: https://github.com/99linesofcode/dotfiles.git
dest: "$HOME/dotfiles"
single_branch: yes
version: main
force: true
- name: Clean up installation artifacts and other unused files
ansible.builtin.file:
path: "$HOME/{{ item }}"
state: absent
loop:
- .bash_logout
- .bashrc
- .profile
- .zshrc
- oh-my-zsh-install.sh
- name: Run dotfiles/install.sh script to configure user environment
ansible.builtin.shell:
chdir: "$HOME/dotfiles"
cmd: ./install.sh
- name: Remove keychain plugin and identity loading configuration from .zshrc
ansible.builtin.lineinfile:
path: "$HOME/.zshrc"
regexp: "{{ item }}"
state: absent
loop:
- "gpg-agent$"
- "^zstyle :omz:plugins:keychain agents gpg,ssh"
- "^zstyle :omz:plugins:keychain identities .*"
become: true
become_user: "{{ user.name }}"
- name: Launch Docker daemon on system startup
ansible.builtin.shell: "loginctl enable-linger {{ user.name }}"
- name: Allow Docker to expose priviledged ports (< 1024)
ansible.builtin.shell: "setcap cap_net_bind_service=ep $(which rootlesskit)"
- name: Remove public SSH key from root account
ansible.posix.authorized_key:
user: root
state: absent
key: "{{ lookup('file', lookup('env', 'HOME') + '/.ssh/' + ssh_key) }}"