Compare commits
	
		
			14 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| d8eba3b7be | |||
| 01e8e22c01 | |||
| a31bf233dc | |||
| 60fafed9cd | |||
| 2c00858590 | |||
| be80681485 | |||
| a2e60972c7 | |||
| 598359854f | |||
| ef812c1877 | |||
| 385e60aee5 | |||
| 5633468f41 | |||
| 7f91b24adb | |||
| 5b09029239 | |||
| 7adb5f10e9 | 
							
								
								
									
										69
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,41 +1,76 @@ | ||||
| # Project Moxie | ||||
| # Homelab | ||||
|  | ||||
| Project Moxie is a personal IT homelab project written in Ansible and executed by Jenkins. It is a growing collection of infrastructure as code (IaC) I write out of curiosity and for reference purposes, keeping a handful of beneficial projects managed and secured. | ||||
| This project is my personal IT homelab initiative for self-hosting and | ||||
| exploring Free and Open Source Software (FOSS) infrastructure. As a technology | ||||
| enthusiast and professional, this project is primarily a practical tool for | ||||
| hosting services. It serves as a playground for engaging with systems | ||||
| technology in functional, intriguing, and gratifying ways. Self-hosting | ||||
| empowers individuals to govern their digital space, ensuring that their online | ||||
| environments reflect personal ethics rather than centralized entities' opaque | ||||
| policies. | ||||
|  | ||||
| Built on Debian Stable, this project utilizes Ansible and Vagrant, providing | ||||
| relatively easy-to-use reproducible ephemeral environments to test | ||||
| infrastructure automation before pushing to live systems. | ||||
|  | ||||
| ## Quick Start | ||||
|  | ||||
| To configure a local virtual machine for testing, follow these simple steps. | ||||
|  | ||||
| ### Prerequisites | ||||
|  | ||||
| Vagrant and VirtualBox are used to develop Project Moxie. You will need to install these before continuing. | ||||
|  | ||||
| ### Installation | ||||
|  | ||||
| 1. Clone this repository | ||||
|    ``` | ||||
|    git clone https://github.com/krislamo/moxie | ||||
|    git clone https://git.krislamo.org/kris/homelab | ||||
|    ``` | ||||
|    Optionally clone from the GitHub mirror instead: | ||||
|    ``` | ||||
|    git clone https://github.com/krislamo/homelab | ||||
|    ``` | ||||
| 2. Set the `PLAYBOOK` environmental variable to a development playbook name in the `dev/` directory | ||||
|  | ||||
|    The following `PLAYBOOK` names are available: `dockerbox`, `hypervisor`, `minecraft`, `bitwarden`, `nextcloud`, `nginx` | ||||
|  | ||||
|    To list available options in the `dev/` directory and choose a suitable PLAYBOOK, run: | ||||
|    ``` | ||||
|    ls dev/*.yml | xargs -n 1 basename -s .yml | ||||
|    ``` | ||||
|    Export the `PLAYBOOK` variable | ||||
|    ``` | ||||
|    export PLAYBOOK=dockerbox | ||||
|    ``` | ||||
| 3. Bring the Vagrant box up | ||||
| 3. Clean up any previous provision and build the VM | ||||
|    ``` | ||||
|    vagrant up | ||||
|    make clean && make | ||||
|    ``` | ||||
|  | ||||
| #### Copyright and License | ||||
| Copyright (C) 2020-2021  Kris Lamoureux | ||||
| ## Vagrant Settings | ||||
| The Vagrantfile configures the environment based on settings from `.vagrant.yml`, | ||||
| with default values including: | ||||
|  | ||||
| - PLAYBOOK: `default` | ||||
|    - Runs a `default` playbook that does nothing. | ||||
|    - You can set this by an environmental variable with the same name. | ||||
| - VAGRANT_BOX: `debian/bookworm64` | ||||
|    - Current Debian Stable codename | ||||
| - VAGRANT_CPUS: `2` | ||||
|    - Threads or cores per node, depending on CPU architecture | ||||
| - VAGRANT_MEM: `2048` | ||||
|    - Specifies the amount of memory (in MB) allocated | ||||
| - SSH_FORWARD: `false` | ||||
|    - Enable this if you need to forward SSH agents to the Vagrant machine | ||||
|  | ||||
|  | ||||
| ## Copyright and License | ||||
| Copyright (C) 2019-2023  Kris Lamoureux | ||||
|  | ||||
| [](https://www.gnu.org/licenses/gpl-3.0) | ||||
|  | ||||
| This program is free software: you can redistribute it and/or modify it under | ||||
| the terms of the GNU General Public License as published by the Free Software | ||||
| Foundation, version 3 of the License. | ||||
|  | ||||
| This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. | ||||
| This program is distributed in the hope that it will be useful, but WITHOUT ANY | ||||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A | ||||
| PARTICULAR PURPOSE.  See the GNU General Public License for more details. | ||||
|  | ||||
| This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details. | ||||
|  | ||||
| You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| You should have received a copy of the GNU General Public License along with | ||||
| this program. If not, see <https://www.gnu.org/licenses/>. | ||||
|   | ||||
| @@ -7,6 +7,7 @@ users: | ||||
|     uid: 1001 | ||||
|     gid: 1001 | ||||
|     home: true | ||||
|     system: true | ||||
|  | ||||
| # Import my GPG key for git signature verification | ||||
| root_gpgkeys: | ||||
| @@ -14,6 +15,7 @@ root_gpgkeys: | ||||
|     id: FBF673CEEC030F8AECA814E73EDA9C3441EDA925 | ||||
|  | ||||
| # docker | ||||
| docker_official: true # docker's apt repos | ||||
| docker_users: | ||||
|   - vagrant | ||||
|  | ||||
| @@ -33,6 +35,9 @@ docker_compose_deploy: | ||||
|   - name: gitea | ||||
|     url: https://github.com/krislamo/gitea | ||||
|     version: b0ce66f6a1ab074172eed79eeeb36d7e9011ef8f | ||||
|     enabled: true | ||||
|     trusted_keys: | ||||
|       - FBF673CEEC030F8AECA814E73EDA9C3441EDA925 | ||||
|     env: | ||||
|       USER_UID: "{{ users.git.uid }}" | ||||
|       USER_GID: "{{ users.git.gid }}" | ||||
|   | ||||
| @@ -5,7 +5,12 @@ allow_reboot: false | ||||
| manage_network: false | ||||
|  | ||||
| users: | ||||
|   - name: jellyfin | ||||
|   jellyfin: | ||||
|     uid: 1001 | ||||
|     gid: 1001 | ||||
|     shell: /usr/sbin/nologin | ||||
|     home: false | ||||
|     system: true | ||||
|  | ||||
| samba: | ||||
|   users: | ||||
|   | ||||
| @@ -4,6 +4,18 @@ base_domain: local.krislamo.org | ||||
| allow_reboot: false | ||||
| manage_network: false | ||||
|  | ||||
| users: | ||||
|   git: | ||||
|     uid: 1001 | ||||
|     gid: 1001 | ||||
|     home: true | ||||
|     system: true | ||||
|  | ||||
| # Import my GPG key for git signature verification | ||||
| root_gpgkeys: | ||||
|   - name: kris@lamoureux.io | ||||
|     id: FBF673CEEC030F8AECA814E73EDA9C3441EDA925 | ||||
|  | ||||
| # proxy | ||||
| proxy: | ||||
|   #production: true | ||||
| @@ -15,14 +27,49 @@ proxy: | ||||
|       - "{{ base_domain }}" | ||||
|   servers: | ||||
|     - domain: "{{ bitwarden_domain }}" | ||||
|       proxy_pass: "http://127.0.0.1:8080" | ||||
|       proxy_pass: "http://127.0.0.1" | ||||
|     - domain: "{{ gitea_domain }}" | ||||
|       proxy_pass: "http://127.0.0.1:3000" | ||||
|       proxy_pass: "http://127.0.0.1" | ||||
|  | ||||
| # docker | ||||
| docker_official: true # docker's apt repos | ||||
| docker_users: | ||||
|   - vagrant | ||||
|  | ||||
| docker_compose_env_nolog: false # dev only setting | ||||
| docker_compose_deploy: | ||||
|   # Traefik | ||||
|   - name: traefik | ||||
|     url: https://github.com/krislamo/traefik | ||||
|     version: e97db75e2e214582fac5f5e495687ab5cdf855ad | ||||
|     path: docker-compose.web.yml | ||||
|     enabled: true | ||||
|     accept_newhostkey: true | ||||
|     trusted_keys: | ||||
|       - FBF673CEEC030F8AECA814E73EDA9C3441EDA925 | ||||
|     env: | ||||
|       ENABLE: true | ||||
|   # Gitea | ||||
|   - name: gitea | ||||
|     url: https://github.com/krislamo/gitea | ||||
|     version: b0ce66f6a1ab074172eed79eeeb36d7e9011ef8f | ||||
|     enabled: true | ||||
|     trusted_keys: | ||||
|       - FBF673CEEC030F8AECA814E73EDA9C3441EDA925 | ||||
|     env: | ||||
|       ENTRYPOINT: web | ||||
|       ENABLE_TLS: false | ||||
|       USER_UID: "{{ users.git.uid }}" | ||||
|       USER_GID: "{{ users.git.gid }}" | ||||
|       DB_PASSWD: "{{ gitea.DB_PASSWD }}" | ||||
|  | ||||
| # gitea | ||||
| gitea_domain: "git.{{ base_domain }}" | ||||
| gitea: | ||||
|   DB_NAME: gitea | ||||
|   DB_USER: gitea | ||||
|   DB_PASSWD: password | ||||
|  | ||||
| # bitwarden | ||||
| # Get Installation ID & Key at https://bitwarden.com/host/ | ||||
| bitwarden_domain: "vault.{{ base_domain }}" | ||||
| @@ -30,8 +77,3 @@ bitwarden_dbpass: password | ||||
| bitwarden_install_id: 4ea840a3-532e-4cb6-a472-abd900728b23 | ||||
| bitwarden_install_key: 1yB3Z2gRI0KnnH90C6p | ||||
| #bitwarden_prodution: true | ||||
|  | ||||
| # gitea | ||||
| gitea_domain: "git.{{ base_domain }}" | ||||
| gitea_version: 1 | ||||
| gitea_dbpass: password | ||||
|   | ||||
| @@ -5,8 +5,8 @@ | ||||
|     - host_vars/proxy.yml | ||||
|   roles: | ||||
|     - base | ||||
|     - mariadb | ||||
|     - proxy | ||||
|     - docker | ||||
|     - mariadb | ||||
|     - gitea | ||||
|     - bitwarden | ||||
|   | ||||
| @@ -1,17 +1,33 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Finds the SSH private key under ./.vagrant and connects to | ||||
| # the Vagrant box, port forwarding localhost ports: 8443, 80, 443 | ||||
| # the Vagrant box, port forwarding localhost ports: 8443, 443, 80, 22 | ||||
| # | ||||
| # Download the latest script: | ||||
| # https://git.krislamo.org/kris/homelab/raw/branch/main/forward-ssh.sh | ||||
| # | ||||
| # Copyright (C) 2023  Kris Lamoureux | ||||
| # | ||||
| # This program is free software: you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| # the Free Software Foundation, version 3 of the License. | ||||
| # | ||||
| # This program is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
|  | ||||
| # Root check | ||||
| if [ "$EUID" -ne 0 ]; then | ||||
|   echo "[ERROR]: Please run script as root" | ||||
|   echo "[ERROR]: Please run this script as root" | ||||
|   exit 1 | ||||
| fi | ||||
|  | ||||
| # Clean environment | ||||
| unset PRIVATE_KEY | ||||
| unset HOST_IP | ||||
| unset MATCH_PATTERN | ||||
| unset PKILL_ANSWER | ||||
|  | ||||
| @@ -24,8 +40,8 @@ function ssh_connect { | ||||
|       printf "[INFO]: Starting new vagrant SSH tunnel on PID " | ||||
|       sudo -u "$USER" ssh -fNT -i "$PRIVATE_KEY" \ | ||||
|         -L 22:localhost:22 \ | ||||
|         -L 80:localhost:80 \ | ||||
|         -L 443:localhost:443 \ | ||||
|         -L 80:"$HOST_IP":80 \ | ||||
|         -L 443:"$HOST_IP":443 \ | ||||
|         -L 8443:localhost:8443 \ | ||||
|         -o UserKnownHostsFile=/dev/null \ | ||||
|         -o StrictHostKeyChecking=no \ | ||||
| @@ -34,28 +50,50 @@ function ssh_connect { | ||||
|       pgrep -f "$MATCH_PATTERN" | ||||
|       ;; | ||||
|     *) | ||||
|       echo "[INFO]: Delined to start a new vagrant SSH tunnel" | ||||
|       echo "[INFO]: Declined to start a new vagrant SSH tunnel" | ||||
|       exit 0 | ||||
|       ;; | ||||
|   esac | ||||
| } | ||||
|  | ||||
| # Check for valid PRIVATE_KEY location | ||||
| PRIVATE_KEY="$(find .vagrant -name "private_key" 2>/dev/null)" | ||||
| if ! ssh-keygen -l -f "$PRIVATE_KEY" &>/dev/null; then | ||||
| PRIVATE_KEY="$(find .vagrant -name "private_key" 2>/dev/null | sort)" | ||||
|  | ||||
| # Single vagrant machine or multiple | ||||
| if [ "$(echo "$PRIVATE_KEY" | wc -l)" -gt 1 ]; then | ||||
|   while IFS= read -r KEYFILE; do | ||||
|     if ! ssh-keygen -l -f "$KEYFILE" &>/dev/null; then | ||||
|       echo "[ERROR]: The SSH key '$KEYFILE' is not valid. Are your virtual machines running?" | ||||
|       exit 1 | ||||
|     fi | ||||
|     echo "[CHECK]: Valid key at $KEYFILE" | ||||
|   done < <(echo "$PRIVATE_KEY") | ||||
|   PRIVATE_KEY="$(echo "$PRIVATE_KEY" | grep -m1 "${1:-default}")" | ||||
| elif ! ssh-keygen -l -f "$PRIVATE_KEY" &>/dev/null; then | ||||
|   echo "[ERROR]: The SSH key '$PRIVATE_KEY' is not valid. Is your virtual machine running?" | ||||
|   exit 1 | ||||
| else | ||||
|   echo "[CHECK]: Valid key at $PRIVATE_KEY" | ||||
| fi | ||||
| echo "[CHECK]: Valid key at $PRIVATE_KEY" | ||||
|  | ||||
| # Grab first IP or use whatever HOST_IP_FIELD is set to and check that the guest is up | ||||
| HOST_IP="$(vagrant ssh -c "hostname -I | cut -d' ' -f${HOST_IP_FIELD:-1}" 2>/dev/null)" | ||||
| HOST_IP="${HOST_IP::-1}" # trim | ||||
| if [ -z "$HOST_IP" ]; then | ||||
|   HOST_IP="$(sudo -u "$SUDO_USER" vagrant ssh -c "hostname -I | cut -d' ' -f${HOST_IP_FIELD:-1}" "${1:-default}" 2>/dev/null)" | ||||
|  | ||||
|   if [ -z "$HOST_IP" ]; then | ||||
|     echo "[ERROR]: Failed to find ${1:-default}'s IP" | ||||
|     exit 1 | ||||
|   fi | ||||
|   HOST_IP="${HOST_IP::-1}" # trim | ||||
| else | ||||
|   echo "[INFO]: HOST_IP configured by the shell environment" | ||||
| fi | ||||
|  | ||||
| if ! ping -c 1 "$HOST_IP" &>/dev/null; then | ||||
|   echo "[ERROR]: Cannot ping the host IP '$HOST_IP'" | ||||
|   exit 1 | ||||
| fi | ||||
| echo "[CHECK]: Host at $HOST_IP is up" | ||||
| echo "[CHECK]: Host at $HOST_IP (${1:-default}) is up" | ||||
|  | ||||
| # Pattern for matching processes running | ||||
| MATCH_PATTERN="ssh -fNT -i ${PRIVATE_KEY}.*vagrant@" | ||||
|   | ||||
| @@ -5,6 +5,10 @@ | ||||
|   listen: reboot_host | ||||
|   when: allow_reboot | ||||
|  | ||||
| - name: Reconfigure locales | ||||
|   ansible.builtin.command: dpkg-reconfigure -f noninteractive locales | ||||
|   listen: reconfigure_locales | ||||
|  | ||||
| - name: Restart WireGuard | ||||
|   ansible.builtin.service: | ||||
|     name: wg-quick@wg0 | ||||
|   | ||||
| @@ -2,4 +2,4 @@ | ||||
|   ansible.builtin.file: | ||||
|     path: "~/.ansible/tmp" | ||||
|     state: directory | ||||
|     mode: 0700 | ||||
|     mode: "700" | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|   ansible.builtin.template: | ||||
|     src: ddclient.conf.j2 | ||||
|     dest: /etc/ddclient.conf | ||||
|     mode: 0600 | ||||
|     mode: "600" | ||||
|   register: ddclient_settings | ||||
|  | ||||
| - name: Start ddclient and enable on boot | ||||
|   | ||||
| @@ -32,14 +32,14 @@ | ||||
|   ansible.builtin.template: | ||||
|     src: fail2ban-ssh.conf.j2 | ||||
|     dest: /etc/fail2ban/jail.d/sshd.conf | ||||
|     mode: 0640 | ||||
|     mode: "640" | ||||
|   notify: restart_fail2ban | ||||
|  | ||||
| - name: Install Fail2ban IP allow list | ||||
|   ansible.builtin.template: | ||||
|     src: fail2ban-allowlist.conf.j2 | ||||
|     dest: /etc/fail2ban/jail.d/allowlist.conf | ||||
|     mode: 0640 | ||||
|     mode: "640" | ||||
|   when: fail2ban_ignoreip is defined | ||||
|   notify: restart_fail2ban | ||||
|  | ||||
|   | ||||
| @@ -11,10 +11,10 @@ | ||||
|   ansible.builtin.template: | ||||
|     src: msmtprc.j2 | ||||
|     dest: /root/.msmtprc | ||||
|     mode: 0600 | ||||
|     mode: "600" | ||||
|  | ||||
| - name: Install /etc/aliases | ||||
|   ansible.builtin.copy: | ||||
|     dest: /etc/aliases | ||||
|     content: "root: {{ mail.rootalias }}" | ||||
|     mode: 0644 | ||||
|     mode: "644" | ||||
|   | ||||
| @@ -10,6 +10,6 @@ | ||||
|   ansible.builtin.template: | ||||
|     src: "interface.j2" | ||||
|     dest: "/etc/network/interfaces.d/{{ item.name }}" | ||||
|     mode: 0400 | ||||
|     mode: "400" | ||||
|   loop: "{{ interfaces }}" | ||||
|   notify: reboot_host | ||||
|   | ||||
| @@ -3,23 +3,15 @@ | ||||
|     name: samba | ||||
|     state: present | ||||
|  | ||||
| - name: Create nologin shell accounts for Samba | ||||
|   ansible.builtin.user: | ||||
|     name: "{{ item.name }}" | ||||
|     state: present | ||||
|     shell: /usr/sbin/nologin | ||||
|     createhome: false | ||||
|     system: yes | ||||
|   loop: "{{ samba.users }}" | ||||
|   when: item.manage_user is defined and item.manage_user is true | ||||
|  | ||||
| - name: Create Samba users | ||||
|   ansible.builtin.shell: "smbpasswd -a {{ item.name }}" | ||||
|   ansible.builtin.command: "smbpasswd -a {{ item.name }}" | ||||
|   args: | ||||
|     stdin: "{{ item.password }}\n{{ item.password }}" | ||||
|   loop: "{{ samba.users }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.name }}" | ||||
|   register: samba_users | ||||
|   changed_when: "'User added' in samba_users.stdout" | ||||
|   changed_when: "'Added user' in samba_users.stdout" | ||||
|  | ||||
| - name: Ensure share directories exist | ||||
|   ansible.builtin.file: | ||||
| @@ -27,13 +19,14 @@ | ||||
|     owner: "{{ item.owner }}" | ||||
|     group: "{{ item.group }}" | ||||
|     state: directory | ||||
|     mode: 0755 | ||||
|     mode: "755" | ||||
|   loop: "{{ samba.shares }}" | ||||
|  | ||||
| - name: Configure Samba shares | ||||
|   ansible.builtin.template: | ||||
|     src: smb.conf.j2 | ||||
|     dest: /etc/samba/smb.conf | ||||
|     mode: "700" | ||||
|   notify: restart_samba | ||||
|  | ||||
| - name: Start smbd and enable on boot | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|     state: present | ||||
|  | ||||
| - name: Check for existing GPG keys | ||||
|   command: "gpg --list-keys {{ item.id }} 2>/dev/null" | ||||
|   ansible.builtin.command: "gpg --list-keys {{ item.id }} 2>/dev/null" | ||||
|   register: gpg_check | ||||
|   loop: "{{ root_gpgkeys }}" | ||||
|   failed_when: false | ||||
| @@ -18,20 +18,22 @@ | ||||
|   when: root_gpgkeys is defined | ||||
|  | ||||
| - name: Import GPG keys | ||||
|   command: "gpg --keyserver {{ item.item.server | default('keys.openpgp.org') }} --recv-key {{ item.item.id }}" | ||||
|   ansible.builtin.command: | ||||
|     "gpg --keyserver {{ item.item.server | default('keys.openpgp.org') }} --recv-key {{ item.item.id }}" | ||||
|   register: gpg_check_import | ||||
|   loop: "{{ gpg_check.results }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.item }}" | ||||
|   changed_when: false | ||||
|   when: root_gpgkeys is defined and item.rc != 0 | ||||
|  | ||||
| - name: Check GPG key imports | ||||
|   fail: | ||||
|   ansible.builtin.fail: | ||||
|     msg: "{{ item.stderr }}" | ||||
|   loop: "{{ gpg_check_import.results }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.item.item }}" | ||||
|   when: (item.skipped | default(false) == false) and ('imported' not in item.stderr) | ||||
|   when: root_gpgkeys is defined and (not item.skipped | default(false)) and ('imported' not in item.stderr) | ||||
|  | ||||
| - name: Install NTPsec | ||||
|   ansible.builtin.apt: | ||||
| @@ -47,7 +49,7 @@ | ||||
|   community.general.locale_gen: | ||||
|     name: "{{ locale_default }}" | ||||
|     state: present | ||||
|   register: locale_gen_output | ||||
|   notify: reconfigure_locales | ||||
|  | ||||
| - name: Set the default locale | ||||
|   ansible.builtin.lineinfile: | ||||
| @@ -55,15 +57,11 @@ | ||||
|     regexp: "^LANG=" | ||||
|     line: "LANG={{ locale_default }}" | ||||
|  | ||||
| - name: Reconfigure locales | ||||
|   ansible.builtin.command: dpkg-reconfigure -f noninteractive locales | ||||
|   when: locale_gen_output.changed | ||||
|  | ||||
| - name: Manage root authorized_keys | ||||
|   ansible.builtin.template: | ||||
|     src: authorized_keys.j2 | ||||
|     dest: /root/.ssh/authorized_keys | ||||
|     mode: 0400 | ||||
|     mode: "400" | ||||
|   when: authorized_keys is defined | ||||
|  | ||||
| - name: Create system user groups | ||||
| @@ -84,6 +82,7 @@ | ||||
|     group: "{{ item.value.gid }}" | ||||
|     shell: "{{ item.value.shell | default('/bin/bash') }}" | ||||
|     create_home: "{{ item.value.home | default(false) }}" | ||||
|     system: "{{ item.value.system | default(false) }}" | ||||
|   loop: "{{ users | dict2items }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.key }}" | ||||
|   | ||||
| @@ -22,7 +22,7 @@ | ||||
|   ansible.builtin.template: | ||||
|     src: wireguard.j2 | ||||
|     dest: /etc/wireguard/wg0.conf | ||||
|     mode: 0400 | ||||
|     mode: "400" | ||||
|   notify: restart_wireguard | ||||
|  | ||||
| - name: Start WireGuard interface | ||||
|   | ||||
| @@ -5,7 +5,12 @@ | ||||
|   listen: rebuild_bitwarden | ||||
|  | ||||
| - name: Rebuild Bitwarden | ||||
|   ansible.builtin.shell: "{{ bitwarden_root }}/bitwarden.sh rebuild" | ||||
|   ansible.builtin.command: "{{ bitwarden_root }}/bitwarden.sh rebuild" | ||||
|   listen: rebuild_bitwarden | ||||
|  | ||||
| - name: Reload systemd manager configuration | ||||
|   ansible.builtin.systemd: | ||||
|     daemon_reload: true | ||||
|   listen: rebuild_bitwarden | ||||
|  | ||||
| - name: Start Bitwarden after rebuild | ||||
| @@ -14,3 +19,10 @@ | ||||
|     state: started | ||||
|     enabled: true | ||||
|   listen: rebuild_bitwarden | ||||
|  | ||||
| - name: Create Bitwarden's initial log file | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ bitwarden_logs_identity }}/{{ bitwarden_logs_identity_date }}.txt" | ||||
|     state: touch | ||||
|     mode: "644" | ||||
|   listen: touch_bitwarden | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ bitwarden_root }}" | ||||
|     state: directory | ||||
|     mode: "755" | ||||
|  | ||||
| - name: Download Bitwarden script | ||||
|   ansible.builtin.get_url: | ||||
| @@ -22,22 +23,23 @@ | ||||
|     mode: u+x | ||||
|  | ||||
| - name: Run Bitwarden installation script | ||||
|   ansible.builtin.shell: "{{ bitwarden_root }}/bw_wrapper" | ||||
|   ansible.builtin.command: "{{ bitwarden_root }}/bw_wrapper" | ||||
|   args: | ||||
|     creates: "{{ bitwarden_root }}/bwdata/config.yml" | ||||
|  | ||||
| - name: Install docker-compose override | ||||
| - name: Install compose override | ||||
|   ansible.builtin.template: | ||||
|     src: compose.override.yml.j2 | ||||
|     dest: "{{ bitwarden_root }}/bwdata/docker/docker-compose.override.yml" | ||||
|   when: traefik_version is defined | ||||
|     mode: "644" | ||||
|   when: bitwarden_override | default(true) | ||||
|   notify: rebuild_bitwarden | ||||
|  | ||||
| - name: Disable bitwarden-nginx HTTP on 80 | ||||
|   ansible.builtin.replace: | ||||
|     path: "{{ bitwarden_root }}/bwdata/config.yml" | ||||
|     regexp: "^http_port: 80$" | ||||
|     replace: "http_port: 127.0.0.1:8080" | ||||
|     replace: "http_port: {{ bitwarden_http_port | default('127.0.0.1:9080') }}" | ||||
|   when: not bitwarden_standalone | ||||
|   notify: rebuild_bitwarden | ||||
|  | ||||
| @@ -45,7 +47,7 @@ | ||||
|   ansible.builtin.replace: | ||||
|     path: "{{ bitwarden_root }}/bwdata/config.yml" | ||||
|     regexp: "^https_port: 443$" | ||||
|     replace: "https_port: 127.0.0.1:8443" | ||||
|     replace: "https_port: {{ bitwarden_https_port | default('127.0.0.1:9443') }}" | ||||
|   when: not bitwarden_standalone | ||||
|   notify: rebuild_bitwarden | ||||
|  | ||||
| @@ -76,6 +78,7 @@ | ||||
|   ansible.builtin.template: | ||||
|     src: bitwarden.service.j2 | ||||
|     dest: "/etc/systemd/system/{{ bitwarden_name }}.service" | ||||
|     mode: "644" | ||||
|   register: bitwarden_systemd | ||||
|   notify: rebuild_bitwarden | ||||
|  | ||||
| @@ -83,22 +86,12 @@ | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ bitwarden_logs_identity }}" | ||||
|     state: directory | ||||
|   register: bitwarden_logs | ||||
|  | ||||
| - name: Create Bitwarden's initial log file | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ bitwarden_logs_identity }}/{{ bitwarden_logs_identity_date }}.txt" | ||||
|     state: touch | ||||
|   when: bitwarden_logs.changed | ||||
|     mode: "755" | ||||
|   notify: touch_bitwarden | ||||
|  | ||||
| - name: Install Bitwarden's Fail2ban jail | ||||
|   ansible.builtin.template: | ||||
|     src: fail2ban-jail.conf.j2 | ||||
|     dest: /etc/fail2ban/jail.d/bitwarden.conf | ||||
|     mode: "640" | ||||
|   notify: restart_fail2ban | ||||
|  | ||||
| - name: Reload systemd manager configuration | ||||
|   ansible.builtin.systemd: | ||||
|     daemon_reload: true | ||||
|   when: bitwarden_systemd.changed | ||||
|   notify: rebuild_bitwarden | ||||
|   | ||||
| @@ -23,10 +23,13 @@ send "{{ bitwarden_install_id }}\r" | ||||
| expect "Enter your installation key:" | ||||
| send "{{ bitwarden_install_key }}\r" | ||||
|  | ||||
| expect "Do you have a SSL certificate to use? (y/n):" | ||||
| expect "Enter your region (US/EU) \\\[US\\\]:" | ||||
| send "US\r" | ||||
|  | ||||
| expect "Do you have a SSL certificate to use? (y/N):" | ||||
| send "n\r" | ||||
|  | ||||
| expect "Do you want to generate a self-signed SSL certificate? (y/n):" | ||||
| expect "Do you want to generate a self-signed SSL certificate? (y/N):" | ||||
| {% if bitwarden_standalone and not bitwarden_production %} | ||||
| send "y\r" | ||||
| {% else %} | ||||
|   | ||||
| @@ -6,13 +6,11 @@ services: | ||||
|       - traefik | ||||
|     labels: | ||||
|       traefik.http.routers.bitwarden.rule: "Host(`{{ bitwarden_domain }}`)" | ||||
|       traefik.http.routers.bitwarden.entrypoints: websecure | ||||
|       traefik.http.routers.bitwarden.tls.certresolver: letsencrypt | ||||
|       traefik.http.routers.bitwarden.middlewares: "securehttps@file" | ||||
|       traefik.http.routers.bitwarden.entrypoints: {{ bitwarden_entrypoint | default('web') }} | ||||
|       traefik.http.routers.bitwarden.tls: {{ bitwarden_traefik_tls | default('false') }} | ||||
|       traefik.http.services.bitwarden.loadbalancer.server.port: 8080 | ||||
|       traefik.docker.network: traefik | ||||
|       traefik.enable: "true" | ||||
|  | ||||
| networks: | ||||
|   traefik: | ||||
|     external: true | ||||
|   | ||||
| @@ -1,6 +1,11 @@ | ||||
| docker_apt_keyring: /etc/apt/keyrings/docker.asc | ||||
| docker_apt_keyring_hash: 1500c1f56fa9e26b9b8f42452a553675796ade0807cdce11975eb98170b3a570 | ||||
| docker_apt_keyring_url: https://download.docker.com/linux/debian/gpg | ||||
| docker_apt_repo: https://download.docker.com/linux/debian | ||||
| docker_compose_root: /var/lib/compose | ||||
| docker_compose_service: compose | ||||
| docker_compose: /usr/bin/docker-compose | ||||
| docker_compose: "{{ (docker_official | bool) | ternary('/usr/bin/docker compose', '/usr/bin/docker-compose') }}" | ||||
| docker_official: false | ||||
| docker_repos_keys: "{{ docker_repos_path }}/.keys" | ||||
| docker_repos_keytype: rsa | ||||
| docker_repos_path: /srv/.compose_repos | ||||
| @@ -4,7 +4,7 @@ | ||||
|   listen: compose_systemd | ||||
|  | ||||
| - name: Find which services had a docker-compose.yml updated | ||||
|   set_fact: | ||||
|   ansible.builtin.set_fact: | ||||
|     compose_restart_list: "{{ (compose_restart_list | default([])) + [item.item.name] }}" | ||||
|   loop: "{{ compose_update.results }}" | ||||
|   loop_control: | ||||
| @@ -13,7 +13,7 @@ | ||||
|   listen: compose_restart | ||||
|  | ||||
| - name: Find which services had their .env updated | ||||
|   set_fact: | ||||
|   ansible.builtin.set_fact: | ||||
|     compose_restart_list: "{{ (compose_restart_list | default([])) + [item.item.name] }}" | ||||
|   loop: "{{ compose_env_update.results }}" | ||||
|   loop_control: | ||||
| @@ -21,10 +21,34 @@ | ||||
|   when: item.changed | ||||
|   listen: compose_restart | ||||
|  | ||||
| - name: Restart {{ docker_compose_service }} services | ||||
| - name: Restart MariaDB | ||||
|   ansible.builtin.service: | ||||
|     name: mariadb | ||||
|     state: restarted | ||||
|   when: not mariadb_restarted | ||||
|   listen: restart_mariadb # hijack handler for early restart | ||||
|  | ||||
| - name: Set MariaDB as restarted | ||||
|   ansible.builtin.set_fact: | ||||
|     mariadb_restarted: true | ||||
|   when: not mariadb_restarted | ||||
|   listen: restart_mariadb | ||||
|  | ||||
| - name: Restart compose services | ||||
|   ansible.builtin.systemd: | ||||
|     state: restarted | ||||
|     name: "{{ docker_compose_service }}@{{ item }}" | ||||
|   loop: "{{ compose_restart_list | unique }}" | ||||
|   loop: "{{ compose_restart_list | default([]) | unique }}" | ||||
|   when: compose_restart_list is defined | ||||
|   listen: compose_restart | ||||
|  | ||||
| - name: Start compose services and enable on boot | ||||
|   ansible.builtin.service: | ||||
|     name: "{{ docker_compose_service }}@{{ item.name }}" | ||||
|     state: started | ||||
|     enabled: true | ||||
|   loop: "{{ docker_compose_deploy }}" | ||||
|   loop_control: | ||||
|     label: "{{ docker_compose_service }}@{{ item.name }}" | ||||
|   when: item.enabled is defined and item.enabled is true | ||||
|   listen: compose_enable | ||||
|   | ||||
| @@ -1,7 +1,40 @@ | ||||
| - name: Install Docker | ||||
| - name: Add official Docker APT key | ||||
|   ansible.builtin.get_url: | ||||
|     url: "{{ docker_apt_keyring_url }}" | ||||
|     dest: "{{ docker_apt_keyring }}" | ||||
|     checksum: "sha256:{{ docker_apt_keyring_hash }}" | ||||
|     mode: "644" | ||||
|     owner: root | ||||
|     group: root | ||||
|   when: docker_official | ||||
|  | ||||
| - name: Remove official Docker APT key | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ docker_apt_keyring }}" | ||||
|     state: absent | ||||
|   when: not docker_official | ||||
|  | ||||
| - name: Add/remove official Docker APT repository | ||||
|   ansible.builtin.apt_repository: | ||||
|     repo: > | ||||
|       deb [arch=amd64 signed-by={{ docker_apt_keyring }}] | ||||
|       {{ docker_apt_repo }} {{ ansible_distribution_release }} stable | ||||
|     state: "{{ 'present' if docker_official else 'absent' }}" | ||||
|     filename: "{{ docker_apt_keyring | regex_replace('^.*/', '') }}" | ||||
|  | ||||
| - name: Install/uninstall Docker from Debian repositories | ||||
|   ansible.builtin.apt: | ||||
|     name: ['docker.io', 'docker-compose'] | ||||
|     state: present | ||||
|     name: ['docker.io', 'docker-compose', 'containerd', 'runc'] | ||||
|     state: "{{ 'absent' if docker_official else 'present' }}" | ||||
|     autoremove: true | ||||
|     update_cache: true | ||||
|  | ||||
| - name: Install/uninstall Docker from Docker repositories | ||||
|   ansible.builtin.apt: | ||||
|     name: ['docker-ce', 'docker-ce-cli', 'containerd.io', | ||||
|            'docker-buildx-plugin', 'docker-compose-plugin'] | ||||
|     state: "{{ 'present' if docker_official else 'absent' }}" | ||||
|     autoremove: true | ||||
|     update_cache: true | ||||
|  | ||||
| - name: Login to private registry | ||||
| @@ -15,20 +48,20 @@ | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ docker_compose_root }}" | ||||
|     state: directory | ||||
|     mode: 0500 | ||||
|     mode: "500" | ||||
|  | ||||
| - name: Install docker-compose systemd service | ||||
|   ansible.builtin.template: | ||||
|     src: docker-compose.service.j2 | ||||
|     dest: "/etc/systemd/system/{{ docker_compose_service }}@.service" | ||||
|     mode: 0400 | ||||
|     mode: "400" | ||||
|   notify: compose_systemd | ||||
|  | ||||
| - name: Create directories to clone docker-compose repositories | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ item }}" | ||||
|     state: directory | ||||
|     mode: 0400 | ||||
|     mode: "400" | ||||
|   loop: | ||||
|     - "{{ docker_repos_path }}" | ||||
|     - "{{ docker_repos_keys }}" | ||||
| @@ -39,7 +72,13 @@ | ||||
|     path: "{{ docker_repos_keys }}/id_{{ docker_repos_keytype }}" | ||||
|     type: "{{ docker_repos_keytype }}" | ||||
|     comment: "{{ ansible_hostname }}-deploy-key" | ||||
|     mode: 0400 | ||||
|     mode: "400" | ||||
|     state: present | ||||
|   when: docker_compose_deploy is defined | ||||
|  | ||||
| - name: Check for git installation | ||||
|   ansible.builtin.apt: | ||||
|     name: git | ||||
|     state: present | ||||
|   when: docker_compose_deploy is defined | ||||
|  | ||||
| @@ -48,7 +87,7 @@ | ||||
|     repo: "{{ item.url }}" | ||||
|     dest: "{{ docker_repos_path }}/{{ item.name }}" | ||||
|     version: "{{ item.version }}" | ||||
|     accept_newhostkey: "{{ item.accept_newhostkey | default('false') }}" | ||||
|     accept_newhostkey: "{{ item.accept_newhostkey | default(false) }}" | ||||
|     gpg_whitelist: "{{ item.trusted_keys | default([]) }}" | ||||
|     verify_commit: "{{ true if (item.trusted_keys is defined and item.trusted_keys) else false }}" | ||||
|     key_file: "{{ docker_repos_keys }}/id_{{ docker_repos_keytype }}" | ||||
| @@ -61,7 +100,7 @@ | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ docker_compose_root }}/{{ item.name }}" | ||||
|     state: directory | ||||
|     mode: 0400 | ||||
|     mode: "400" | ||||
|   loop: "{{ docker_compose_deploy }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.name }}" | ||||
| @@ -73,7 +112,9 @@ | ||||
|     dest: "{{ docker_compose_root }}/{{ item.name }}/docker-compose.yml" | ||||
|   delegate_to: "{{ inventory_hostname }}" | ||||
|   register: compose_update | ||||
|   notify: compose_restart | ||||
|   notify: | ||||
|     - compose_restart | ||||
|     - compose_enable | ||||
|   loop: "{{ docker_compose_deploy | default([]) }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.name }}" | ||||
| @@ -83,10 +124,12 @@ | ||||
|   ansible.builtin.template: | ||||
|     src: docker-compose-env.j2 | ||||
|     dest: "{{ docker_compose_root }}/{{ item.name }}/.env" | ||||
|     mode: 0400 | ||||
|     mode: "400" | ||||
|   register: compose_env_update | ||||
|   notify: compose_restart | ||||
|   no_log: "{{ docker_compose_env_nolog | default('true') }}" | ||||
|   notify: | ||||
|     - compose_restart | ||||
|     - compose_enable | ||||
|   no_log: "{{ docker_compose_env_nolog | default(true) }}" | ||||
|   loop: "{{ docker_compose_deploy }}" | ||||
|   loop_control: | ||||
|     label: "{{ item.name }}" | ||||
| @@ -105,13 +148,4 @@ | ||||
|     name: docker | ||||
|     state: started | ||||
|     enabled: true | ||||
|  | ||||
| - name: Start docker-compose services and enable on boot | ||||
|   ansible.builtin.service: | ||||
|     name: "{{ docker_compose_service }}@{{ item.name }}" | ||||
|     state: started | ||||
|     enabled: true | ||||
|   loop: "{{ docker_compose_deploy }}" | ||||
|   loop_control: | ||||
|     label: "{{ docker_compose_service }}@{{ item.name }}" | ||||
|   when: item.enabled is defined and item.enabled is true | ||||
|   when: docker_managed | default(true) | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| [Unit] | ||||
| Description=%i docker-compose service | ||||
| Description=%i {{ docker_compose_service }} service | ||||
| PartOf=docker.service | ||||
| After=docker.service | ||||
|  | ||||
|   | ||||
| @@ -21,6 +21,7 @@ | ||||
| - name: Create git's .ssh directory | ||||
|   ansible.builtin.file: | ||||
|     path: /home/git/.ssh | ||||
|     mode: "700" | ||||
|     state: directory | ||||
|  | ||||
| - name: Generate git's SSH keys | ||||
| @@ -40,6 +41,7 @@ | ||||
| - name: Create git's authorized_keys file | ||||
|   ansible.builtin.file: | ||||
|     path: /home/git/.ssh/authorized_keys | ||||
|     mode: "600" | ||||
|     state: touch | ||||
|   when: not git_authkeys.stat.exists | ||||
|  | ||||
| @@ -53,27 +55,24 @@ | ||||
|   ansible.builtin.template: | ||||
|     src: gitea.sh.j2 | ||||
|     dest: /usr/local/bin/gitea | ||||
|     mode: 0755 | ||||
|     mode: "755" | ||||
|  | ||||
| - name: Create Gitea's logging directory | ||||
|   ansible.builtin.file: | ||||
|     name: /var/log/gitea | ||||
|     state: directory | ||||
|     mode: "755" | ||||
|  | ||||
| - name: Install Gitea's Fail2ban filter | ||||
|   ansible.builtin.template: | ||||
|     src: fail2ban-filter.conf.j2 | ||||
|     dest: /etc/fail2ban/filter.d/gitea.conf | ||||
|     mode: "644" | ||||
|   notify: restart_fail2ban | ||||
|  | ||||
| - name: Install Gitea's Fail2ban jail | ||||
|   ansible.builtin.template: | ||||
|     src: fail2ban-jail.conf.j2 | ||||
|     dest: /etc/fail2ban/jail.d/gitea.conf | ||||
|     mode: "640" | ||||
|   notify: restart_fail2ban | ||||
|  | ||||
| - name: Start and enable Gitea service | ||||
|   ansible.builtin.service: | ||||
|     name: "{{ docker_compose_service }}@{{ gitea_name }}" | ||||
|     state: started | ||||
|     enabled: true | ||||
|   | ||||
| @@ -2,4 +2,11 @@ | ||||
|   ansible.builtin.service: | ||||
|     name: mariadb | ||||
|     state: restarted | ||||
|   when: not mariadb_restarted | ||||
|   listen: restart_mariadb | ||||
|  | ||||
| - name: Set MariaDB as restarted | ||||
|   ansible.builtin.set_fact: | ||||
|     mariadb_restarted: true | ||||
|   when: not mariadb_restarted | ||||
|   listen: restart_mariadb | ||||
|   | ||||
| @@ -3,6 +3,10 @@ | ||||
|     name: mariadb-server | ||||
|     state: present | ||||
|  | ||||
| - name: Set MariaDB restarted fact | ||||
|   ansible.builtin.set_fact: | ||||
|     mariadb_restarted: false | ||||
|  | ||||
| - name: Regather facts for the potentially new docker0 interface | ||||
|   ansible.builtin.setup: | ||||
|  | ||||
|   | ||||
| @@ -1,3 +1,13 @@ | ||||
| - name: Enable nginx sites configuration | ||||
|   ansible.builtin.file: | ||||
|     src: "/etc/nginx/sites-available/{{ item.item.domain }}.conf" | ||||
|     dest: "/etc/nginx/sites-enabled/{{ item.item.domain }}.conf" | ||||
|     state: link | ||||
|     mode: "400" | ||||
|   loop: "{{ nginx_sites.results }}" | ||||
|   when: item.changed | ||||
|   listen: reload_nginx | ||||
|  | ||||
| - name: Reload nginx | ||||
|   ansible.builtin.service: | ||||
|     name: nginx | ||||
|   | ||||
| @@ -19,28 +19,18 @@ | ||||
|   ansible.builtin.template: | ||||
|     src: nginx.conf.j2 | ||||
|     dest: /etc/nginx/nginx.conf | ||||
|     mode: 0644 | ||||
|     mode: "644" | ||||
|   notify: reload_nginx | ||||
|  | ||||
| - name: Install nginx sites configuration | ||||
|   ansible.builtin.template: | ||||
|     src: server-nginx.conf.j2 | ||||
|     dest: "/etc/nginx/sites-available/{{ item.domain }}.conf" | ||||
|     mode: 0400 | ||||
|     mode: "400" | ||||
|   loop: "{{ proxy.servers }}" | ||||
|   notify: reload_nginx | ||||
|   register: nginx_sites | ||||
|  | ||||
| - name: Enable nginx sites configuration | ||||
|   ansible.builtin.file: | ||||
|     src: "/etc/nginx/sites-available/{{ item.item.domain }}.conf" | ||||
|     dest: "/etc/nginx/sites-enabled/{{ item.item.domain }}.conf" | ||||
|     state: link | ||||
|     mode: 0400 | ||||
|   loop: "{{ nginx_sites.results }}" | ||||
|   when: item.changed | ||||
|   notify: reload_nginx | ||||
|  | ||||
| - name: Generate self-signed certificate | ||||
|   ansible.builtin.command: 'openssl req -newkey rsa:4096 -x509 -sha256 -days 3650 -nodes \ | ||||
|           -subj   "/C=US/ST=Local/L=Local/O=Org/OU=IT/CN=example.com" \ | ||||
| @@ -61,14 +51,14 @@ | ||||
|   ansible.builtin.template: | ||||
|     src: cloudflare.ini.j2 | ||||
|     dest: /root/.cloudflare.ini | ||||
|     mode: 0400 | ||||
|     mode: "400" | ||||
|   when: proxy.production is defined and proxy.production and proxy.dns_cloudflare is defined | ||||
|  | ||||
| - name: Create nginx post renewal hook directory | ||||
|   ansible.builtin.file: | ||||
|     path: /etc/letsencrypt/renewal-hooks/post | ||||
|     state: directory | ||||
|     mode: 0500 | ||||
|     mode: "500" | ||||
|   when: proxy.production is defined and proxy.production | ||||
|  | ||||
| - name: Install nginx post renewal hook | ||||
|   | ||||
		Reference in New Issue
	
	Block a user