diff --git a/.gitignore b/.gitignore index c13bb9b..48494c3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -inventory.ini \ No newline at end of file +inventory.ini +venv/* \ No newline at end of file diff --git a/01_infra_setup.md b/01_infra_setup.md index 7599746..6fb7442 100644 --- a/01_infra_setup.md +++ b/01_infra_setup.md @@ -5,7 +5,11 @@ This describes how to prepare each machine before deploying services on them. ## 01.01 First steps * Create an ssh key or pick an existing one. We'll refer to it as the `personal_ssh_key`. -* The guide assumes the laptop (Lapy) has `ansible` installed. If not, do `sudo apt install -y ansible` and `ansible --version` to check. +* Deploy ansible on the laptop (Lapy), which will act as the ansible control node. To do so: + * Create a `venv`: `python3 -m venv venv` + * Activate it: `source venv/bin/activate` + * Install the listed ansible requirements with `pip install -r requirements.txt` +* Keep in mind you should activate this `venv` from now on when running `ansible` commands. ## 01.02 Prepare the VPS (Vipy) @@ -20,9 +24,13 @@ This describes how to prepare each machine before deploying services on them. ### 01.02.02 Prepare Ansible vars -* You have an example `infra/example.inventory.ini`. Copy it with `cp example.inventory.ini inventory.ini` and fill in with the vars for your VPS. +* You have an example `ansible/example.inventory.ini`. Copy it with `cp ansible/example.inventory.ini ansible/inventory.ini` and fill in with the values for your VPS. -### 01.02.03 First steps with Ansible +### 01.02.03 Create user and secure VPS access -* cd into `infra` -* Run `ansible-playbook playbook.yml` +* Ansible will create a user on the first playbook `01_basic_vps_setup_playbook.yml`. This is the user that will get used regularly. But, since this user doesn't exist, you obviosuly need to first run this playbook from some other user. We assume your VPS provider has given you a root user, which is what you need to define as the running user in the next command. +* cd into `ansible` +* Run `ansible-playbook -i inventory.ini infra/01_user_and_access_setup_playbook.yml -e 'ansible_user="your root user here"' +* Then, configure firewall access, fail2ban and auditd with `ansible-playbook -i inventory.ini infra/02_firewall_playbook.yml` + +Note that both the root user and the `counterweight` user will use the same SSH pubkey for auth. \ No newline at end of file diff --git a/02_vps_core_services_setup.md b/02_vps_core_services_setup.md new file mode 100644 index 0000000..f81ff63 --- /dev/null +++ b/02_vps_core_services_setup.md @@ -0,0 +1,18 @@ +# 02. VPS Core Services Setup + +Now that Vipy is ready, we need to deploy some basic services which are foundational for the apps we're actually interested in. + +This assumes you've completed the markdown `01`. + +## 02.01 Deploy Caddy + +* Use Ansible to run the caddy playbook: + + ``` + cd ansible + ansible-playbook -i inventory.ini services/caddy_playbook.yml + ``` + +* Starting config will be empty. Modifying the caddy config file to add endpoints as we add services is covered by the instructions of each service. + +## 02.02 Deploy Uptime Kuma \ No newline at end of file diff --git a/ansible/example.inventory.ini b/ansible/example.inventory.ini new file mode 100644 index 0000000..5958a54 --- /dev/null +++ b/ansible/example.inventory.ini @@ -0,0 +1,2 @@ +[vipy] +your.vps.ip.here ansible_user=counterweight ansible_port=22 ansible_ssh_private_key_file=~/.ssh/your-key \ No newline at end of file diff --git a/ansible/infra/01_user_and_access_setup_playbook.yml b/ansible/infra/01_user_and_access_setup_playbook.yml new file mode 100644 index 0000000..0b25b9a --- /dev/null +++ b/ansible/infra/01_user_and_access_setup_playbook.yml @@ -0,0 +1,65 @@ +- name: Secure Debian VPS + hosts: vipy + vars_files: + - ../vars.yml + become: true + + tasks: + - name: Update and upgrade apt packages + apt: + update_cache: yes + upgrade: full + autoremove: yes + + - name: Create new user + user: + name: "{{ new_user }}" + groups: sudo + shell: /bin/bash + state: present + create_home: yes + + - name: Set up SSH directory for new user + file: + path: "/home/{{ new_user }}/.ssh" + state: directory + mode: "0700" + owner: "{{ new_user }}" + group: "{{ new_user }}" + + - name: Copy current user's authorized_keys to new user + copy: + src: "/home/{{ ansible_user }}/.ssh/authorized_keys" + dest: "/home/{{ new_user }}/.ssh/authorized_keys" + owner: "{{ new_user }}" + group: "{{ new_user }}" + mode: "0600" + remote_src: true + + - name: Allow new user to run sudo without password + copy: + dest: "/etc/sudoers.d/{{ new_user }}" + content: "{{ new_user }} ALL=(ALL) NOPASSWD:ALL" + owner: root + group: root + mode: "0440" + + - name: Disable root login + lineinfile: + path: /etc/ssh/sshd_config + regexp: "{{ item.regexp }}" + line: "{{ item.line }}" + state: present + backrefs: yes + loop: + - { regexp: "^#?PermitRootLogin .*", line: "PermitRootLogin no" } + - { + regexp: "^#?PasswordAuthentication .*", + line: "PasswordAuthentication no", + } + + - name: Restart SSH + service: + name: ssh + state: restarted + diff --git a/infra/playbook.yml b/ansible/infra/02_firewall_playbook.yml similarity index 52% rename from infra/playbook.yml rename to ansible/infra/02_firewall_playbook.yml index 991fe81..31e6848 100644 --- a/infra/playbook.yml +++ b/ansible/infra/02_firewall_playbook.yml @@ -1,56 +1,10 @@ - name: Secure Debian VPS hosts: vipy vars_files: - - vars.yml + - ../vars.yml become: true tasks: - - name: Update and upgrade apt packages - apt: - update_cache: yes - upgrade: full - autoremove: yes - - - name: Create new user - user: - name: "{{ new_user }}" - groups: sudo - shell: /bin/bash - state: present - create_home: yes - - - name: Set up SSH directory for new user - file: - path: "/home/{{ new_user }}/.ssh" - state: directory - mode: "0700" - owner: "{{ new_user }}" - group: "{{ new_user }}" - - - name: Change SSH port and disable root login - lineinfile: - path: /etc/ssh/sshd_config - regexp: "{{ item.regexp }}" - line: "{{ item.line }}" - state: present - backrefs: yes - loop: - - { regexp: "^#?Port .*", line: "Port {{ ssh_port }}" } - - { regexp: "^#?PermitRootLogin .*", line: "PermitRootLogin no" } - - { - regexp: "^#?PasswordAuthentication .*", - line: "PasswordAuthentication no", - } - - - name: Restart SSH - service: - name: ssh - state: restarted - - - name: Set SSH port to new port - set_fact: - ansible_port: "{{ ssh_port }}" - - name: Install UFW apt: name: ufw @@ -68,11 +22,12 @@ - name: Allow outgoing traffic ufw: rule: allow - direction: outgoing + direction: out - name: Allow SSH port through UFW ufw: rule: allow + direction: in port: "{{ ssh_port }}" proto: tcp from_ip: "{{ allow_ssh_from if allow_ssh_from != 'any' else omit }}" diff --git a/ansible/services/caddy_playbook.yml b/ansible/services/caddy_playbook.yml new file mode 100644 index 0000000..13ff64b --- /dev/null +++ b/ansible/services/caddy_playbook.yml @@ -0,0 +1,61 @@ +- name: Install and configure Caddy on Debian 12 + hosts: vipy + become: yes + + tasks: + - name: Install required packages + apt: + name: + - debian-keyring + - debian-archive-keyring + - apt-transport-https + - curl + state: present + update_cache: yes + + - name: Download Caddy GPG armored key + ansible.builtin.get_url: + url: https://dl.cloudsmith.io/public/caddy/stable/gpg.key + dest: /tmp/caddy-stable-archive-keyring.asc + mode: '0644' + + - name: Convert ASCII armored key to binary keyring + ansible.builtin.command: + cmd: gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg /tmp/caddy-stable-archive-keyring.asc + args: + creates: /usr/share/keyrings/caddy-stable-archive-keyring.gpg + + - name: Ensure permissions on keyring file + ansible.builtin.file: + path: /usr/share/keyrings/caddy-stable-archive-keyring.gpg + owner: root + group: root + mode: '0644' + + - name: Add Caddy repository list file + ansible.builtin.get_url: + url: https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt + dest: /etc/apt/sources.list.d/caddy-stable.list + mode: '0644' + validate_certs: yes + + - name: Update apt cache after adding repo + apt: + update_cache: yes + + - name: Install Caddy + apt: + name: caddy + state: present + + - name: Ensure Caddy service is enabled and started + systemd: + name: caddy + enabled: yes + state: started + + - name: Allow HTTPS through UFW + ufw: + rule: allow + port: '443' + proto: tcp \ No newline at end of file diff --git a/ansible/services/docker_playbook.yml b/ansible/services/docker_playbook.yml new file mode 100644 index 0000000..3474a65 --- /dev/null +++ b/ansible/services/docker_playbook.yml @@ -0,0 +1,59 @@ +- name: Install Docker and Docker Compose on Debian 12 + hosts: all + become: yes + + tasks: + - name: Ensure required packages are installed + apt: + name: + - ca-certificates + - curl + - gnupg + - lsb-release + state: present + update_cache: yes + + - name: Add Docker GPG key + ansible.builtin.apt_key: + url: https://download.docker.com/linux/debian/gpg + state: present + + - name: Add Docker repository + ansible.builtin.apt_repository: + repo: "deb [arch=amd64] https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable" + state: present + filename: docker + + - name: Update apt cache after adding Docker repo + apt: + update_cache: yes + + - name: Install Docker Engine and CLI + apt: + name: + - docker-ce + - docker-ce-cli + - containerd.io + - docker-buildx-plugin + - docker-compose-plugin + state: latest + + - name: Ensure Docker is started and enabled + systemd: + name: docker + enabled: yes + state: started + + - name: Add user to docker group + user: + name: "{{ ansible_user }}" + groups: docker + append: yes + + - name: Create symlink for docker-compose (optional CLI alias) + file: + src: /usr/libexec/docker/cli-plugins/docker-compose + dest: /usr/local/bin/docker-compose + state: link + when: ansible_facts['os_family'] == "Debian" + ignore_errors: true # In case the plugin path differs slightly diff --git a/ansible/services/uptime_kuma_playbook.yml b/ansible/services/uptime_kuma_playbook.yml new file mode 100644 index 0000000..9f52798 --- /dev/null +++ b/ansible/services/uptime_kuma_playbook.yml @@ -0,0 +1,51 @@ +- name: Deploy Uptime Kuma with Docker Compose and configure Caddy reverse proxy + hosts: vipy + become: yes + vars: + uptime_kuma_dir: /opt/uptime-kuma + uptime_kuma_port: 3001 + caddy_sites_dir: /etc/caddy/sites-enabled + uptime_kuma_domain: uptime.example.com # Change to your domain + + tasks: + - name: Create uptime kuma directory + file: + path: "{{ uptime_kuma_dir }}" + state: directory + owner: {{ ansible_user }} + group: {{ ansible_user }} + mode: '0755' + + - name: Create docker-compose.yml for uptime kuma + copy: + dest: "{{ uptime_kuma_dir }}/docker-compose.yml" + content: | + version: "3" + services: + uptime-kuma: + image: louislam/uptime-kuma:latest + container_name: uptime-kuma + restart: unless-stopped + ports: + - "{{ uptime_kuma_port }}:3001" + volumes: + - ./data:/app/data + + - name: Deploy uptime kuma container with docker compose + command: docker-compose up -d + args: + chdir: "{{ uptime_kuma_dir }}" + + - name: Create Caddy reverse proxy configuration for uptime kuma + copy: + dest: "{{ caddy_sites_dir }}/uptime-kuma.conf" + content: | + {{ uptime_kuma_domain }} { + reverse_proxy localhost:{{ uptime_kuma_port }} + } + owner: root + group: root + mode: '0644' + + - name: Reload Caddy to apply new config + command: systemctl reload caddy \ No newline at end of file diff --git a/infra/vars.yml b/ansible/vars.yml similarity index 75% rename from infra/vars.yml rename to ansible/vars.yml index 193ccf4..44e4e24 100644 --- a/infra/vars.yml +++ b/ansible/vars.yml @@ -1,3 +1,4 @@ new_user: counterweight -ssh_port: 2222 +ssh_port: 22 allow_ssh_from: "any" + diff --git a/infra/example.inventory.ini b/infra/example.inventory.ini deleted file mode 100644 index 30f75b0..0000000 --- a/infra/example.inventory.ini +++ /dev/null @@ -1,2 +0,0 @@ -[vipy] -your.vps.ip.here ansible_user=debian ansible_port=22 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..cfc541b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,10 @@ +ansible==10.7.0 +ansible-core==2.17.12 +cffi==1.17.1 +cryptography==45.0.4 +Jinja2==3.1.6 +MarkupSafe==3.0.2 +packaging==25.0 +pycparser==2.22 +PyYAML==6.0.2 +resolvelib==1.0.1