Compare commits
	
		
			72 Commits
		
	
	
		
			0.0.2
			...
			lego/set-e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e93bb182c0 | |||
| 27d9555428 | |||
| b14f36c7e8 | |||
| 762e2ffc27 | |||
| 115cfa8236 | |||
| e27eb145f1 | |||
| c286e1a6b4 | |||
| 517a2fe96c | |||
| 9d4baad491 | |||
| 0090baee97 | |||
| 0111f74f0f | |||
| 83d223defa | |||
| c9b2f61787 | |||
| 0771787c98 | |||
| 1024921a74 | |||
| 62263726fa | |||
| ae887a1936 | |||
| 1e57ae1ec0 | |||
| 90dae1311f | |||
| 3733c0b7ab | |||
| 877c5a137a | |||
| 11d4b397ef | |||
| 76e89db5c6 | |||
| 4bddc95161 | |||
| ed95d4fd3d | |||
| 788c4bada0 | |||
| 7ecf8778ca | |||
| 11fec18afc | |||
| b0ba86f78e | |||
| bd418a6199 | |||
| 60b36db8a7 | |||
| 9a02652d98 | |||
| 2c1b3cb47e | |||
| 114cf13871 | |||
| b77c81f754 | |||
| bdf1871855 | |||
| 9454845ea1 | |||
| 4e8cc9bcf2 | |||
| 3b9d6e19da | |||
| c847046720 | |||
| d7b7c59f46 | |||
| 153df81836 | |||
| 7021ed1a89 | |||
| afe72f554e | |||
| c36e95d7eb | |||
| 97526aec36 | |||
| fc73fdd1fa | |||
| bd43f3963c | |||
| 1076a9f384 | |||
| 82e69bdda3 | |||
| b9b5c19d38 | |||
| b9e4abdf36 | |||
| aac6891518 | |||
| 31d025ed75 | |||
| 1423d2a243 | |||
| 967ebab4c1 | |||
| 5f4fbd492c | |||
| 96f5da9bf6 | |||
| 2aaa529585 | |||
| 8941b9357a | |||
| 04b5837fd8 | |||
| 4837172f64 | |||
| ab7cca0947 | |||
| 960d95a924 | |||
| eab7b7e915 | |||
| e7886d8c98 | |||
| 13d40341a0 | |||
| 12b98487a5 | |||
| 2e6cb0a4d5 | |||
| 52d25942b4 | |||
| af17bea1e1 | |||
| 52bf02e084 | 
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							| @@ -5,15 +5,12 @@ | ||||
| This ansible collection provides various roles for installing | ||||
| and configuring basic system utilities like gnupg, ssh etc | ||||
|  | ||||
| - [`elasticsearch`](roles/elasticsearch/README.md): Deploy [elasticsearch](https://www.docker.elastic.co/r/elasticsearch/elasticsearch-oss), | ||||
|   a popular (distributed) search and analytics engine, mostly known by it's | ||||
|   letter "E" in the ELK-stack. | ||||
|  | ||||
| - [`git`](roles/git/README.md): configures git on the target system | ||||
|  | ||||
| - [`gnupg`](roles/gnupg/README.md): configures gnupg on the target system | ||||
|  | ||||
| - [`mariadb`](roles/mariadb/README.md): runs [MariaDB Server](https://mariadb.org/), one of the world's most popular open source relational database | ||||
| - [`lego`](roles/lego/README.md): runs [lego (LetsEncrypt Go)](https://github.com/go-acme/lego), | ||||
|   a ACME client written in go, using systemd (timers). Multi-instance capable. | ||||
|  | ||||
| - [`minio`](roles/minio/README.md): Deploy [min.io](https://min.io), an | ||||
|   s3-compatible object storage server, using docker containers. | ||||
| @@ -24,6 +21,9 @@ and configuring basic system utilities like gnupg, ssh etc | ||||
| - [`restic`](roles/restic/README.md): Manage backups using restic | ||||
|   and persist them to a configurable backend. | ||||
|  | ||||
| - [`powerdns_tsig_key`](roles/powerdns_tsig_key/README.md): Simple ansible role | ||||
|   for generating TSIG keys in PowerDNS. | ||||
|  | ||||
| ## License | ||||
|  | ||||
| [CNPLv7+](LICENSE.md): Cooperative Nonviolent Public License | ||||
|   | ||||
							
								
								
									
										21
									
								
								galaxy.yml
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								galaxy.yml
									
									
									
									
									
								
							| @@ -1,12 +1,27 @@ | ||||
| namespace: finallycoffee | ||||
| name: base | ||||
| version: 0.0.2 | ||||
| version: 0.3.0 | ||||
| readme: README.md | ||||
| authors: | ||||
| - transcaffeine <transcaffeine@finally.coffee> | ||||
| description: Roles for base services which are common dependencies other services like databases | ||||
| description: >-2 | ||||
|   Roles for base services which are core functionality like managing packages | ||||
|   and ssh or common dependencies other services like databases | ||||
| dependencies: | ||||
|   "community.docker": "^4.2.0" | ||||
|   "community.general": "^10.0.0" | ||||
| license_file: LICENSE.md | ||||
| build_ignore: | ||||
| - '*.tar.gz' | ||||
| repository: https://git.finally.coffee/finallycoffee/base | ||||
| issues: https://git.finally.coffee/finallycoffee/base/issues | ||||
| issues: https://codeberg.org/finallycoffee/ansible-collection-base/issues | ||||
| tags: | ||||
|   - bootstrap | ||||
|   - ssh | ||||
|   - mosh | ||||
|   - docker | ||||
|   - lego | ||||
|   - minio | ||||
|   - nginx | ||||
|   - restic | ||||
|   - user_management | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| --- | ||||
|  | ||||
| requires_ansible: ">=2.12" | ||||
| requires_ansible: ">=2.15" | ||||
|   | ||||
							
								
								
									
										78
									
								
								playbooks/bootstrap.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								playbooks/bootstrap.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| --- | ||||
| - name: Bootstrap everything needed for an ansible connection | ||||
|   hosts: "{{ target_hosts | default('all', true) }}" | ||||
|   become: "{{ target_host_become | default(true, false) }}" | ||||
|   gather_facts: false | ||||
|   pre_tasks: | ||||
|     - name: Gather information about the target system id | ||||
|       ansible.builtin.raw: "cat /etc/os-release | grep '^ID=' | cut -d '=' -f2" | ||||
|       register: target_host_os_info | ||||
|       check_mode: false | ||||
|       changed_when: false | ||||
|     - name: Set /etc/os-release system id | ||||
|       ansible.builtin.set_fact: | ||||
|         target_host_system_id: "{{ target_host_os_info.stdout_lines | first | trim }}" | ||||
|       delegate_to: localhost | ||||
|     - name: Gather information about the target system version | ||||
|       ansible.builtin.raw: "cat /etc/os-release | grep '^VERSION_ID=' | cut -d '=' -f2" | ||||
|       register: target_host_os_info_version | ||||
|       check_mode: false | ||||
|       changed_when: false | ||||
|     - name: Set /etc/os-release system version id | ||||
|       ansible.builtin.set_fact: | ||||
|         target_host_system_version_id: "{{ target_host_os_info_version.stdout_lines | first | trim }}" | ||||
|       delegate_to: localhost | ||||
|  | ||||
|   tasks: | ||||
|     - name: Ensure apt bootstrap packages are installed | ||||
|       ansible.builtin.raw: "apt install {{ apt_bootstrap_packages | join(' ') }}" | ||||
|       register: target_host_apt_info | ||||
|       when: target_host_system_id in targets_using_apt | ||||
|       changed_when: | ||||
|         - "'0 upgraded' not in target_host_apt_info.stdout_lines | last" | ||||
|         - "'0 newly installed' not in target_host_apt_info.stdout_lines | last" | ||||
|     - name: Ensure dnf < 4 bootstrap packages are installed | ||||
|       ansible.builtin.raw: "dnf install --assumeyes {{ dnf4_bootstrap_packages | join(' ') }}" | ||||
|       register: target_host_dnf_info | ||||
|       when: | ||||
|         - target_host_system_id in targets_using_dnf4.keys() | ||||
|         - target_host_system_version_id | int < targets_using_dnf4[target_host_system_id] | ||||
|       changed_when: | ||||
|         - "(target_host_dnf_info.stdout_lines | last) != 'Nothing to do.'" | ||||
|     - name: Ensure dnf5 bootstrap packages are installed | ||||
|       ansible.builtin.raw: "dnf install --assumeyes {{ dnf5_bootstrap_packages | join(' ') }}" | ||||
|       register: target_host_dnf_info | ||||
|       when: | ||||
|         - target_host_system_id in targets_using_dnf5.keys() | ||||
|         - target_host_system_version_id | int >= targets_using_dnf5[target_host_system_id] | ||||
|       changed_when: | ||||
|         - "(target_host_dnf_info.stdout_lines | last) != 'Nothing to do.'" | ||||
|     - name: Sort hosts into os-specific groups | ||||
|       ansible.builtin.group_by: | ||||
|         key: >-2 | ||||
|           {{ (os_group_prefix | ||||
|              | default(false, true) | ||||
|              | ternary(os_group_prefix | default('') + (os_group_seperator | default('_')), '')) | ||||
|              + target_host_system_id }} | ||||
|       when: target_hosts_sort_into_system_ids | default(false, true) | ||||
|       changed_when: false | ||||
|       delegate_to: localhost | ||||
|   vars: | ||||
|     targets_using_apt: | ||||
|       - debian | ||||
|       - ubuntu | ||||
|     apt_bootstrap_packages: | ||||
|       - python3 | ||||
|       - python3-apt | ||||
|     # default package manager is dnf5 since fedora 41 | ||||
|     # https://fedoraproject.org/wiki/Changes/SwitchToDnf5#Current_status | ||||
|     targets_using_dnf4: | ||||
|       fedora: 41 | ||||
|     targets_using_dnf5: | ||||
|       fedora: 41 | ||||
|     dnf4_bootstrap_packages: | ||||
|       - python3 | ||||
|       - python3-dnf | ||||
|       - python3-libdnf | ||||
|     dnf5_bootstrap_packages: | ||||
|       - python3-libdnf5 | ||||
							
								
								
									
										6
									
								
								playbooks/docker.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								playbooks/docker.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| --- | ||||
| - name: Install and configure docker daemon | ||||
|   hosts: "{{ docker_hosts | default('docker', true) }}" | ||||
|   become: "{{ docker_become | default(false, true) }}" | ||||
|   roles: | ||||
|     - role: finallycoffee.base.docker | ||||
							
								
								
									
										16
									
								
								playbooks/docker_registry.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								playbooks/docker_registry.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| --- | ||||
| - name: Manage docker registry credentials | ||||
|   hosts: "{{ docker_hosts | default('docker', true) }}" | ||||
|   become: "{{ docker_become | default(false) }}" | ||||
|   gather_facts: "{{ docker_registry_gather_facts | default(true) }}" | ||||
|   tasks: | ||||
|     - name: Manage docker registry credentials | ||||
|       community.docker.docker_login: | ||||
|         registry_url: "{{ docker_registry.registry }}" | ||||
|         username: "{{ docker_registry.username | default(omit) }}" | ||||
|         password: "{{ docker_registry.password | default(omit) }}" | ||||
|         state: "{{ docker_registry.state | default('present') }}" | ||||
|       loop: "{{ docker_registries | default([], true) }}" | ||||
|       loop_control: | ||||
|         loop_var: "docker_registry" | ||||
|         label: "{{ docker_registry.username}}@{{ docker_registry.registry }}" | ||||
							
								
								
									
										6
									
								
								playbooks/mosh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								playbooks/mosh.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| --- | ||||
| - name: Manage and configure mosh | ||||
|   hosts: "{{ mosh_hosts | default('mosh', true) }}" | ||||
|   become: "{{ mosh_become | default(true) }}" | ||||
|   roles: | ||||
|     - role: finallycoffee.base.mosh | ||||
							
								
								
									
										7
									
								
								playbooks/openssh.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								playbooks/openssh.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| --- | ||||
| - name: Ensure openssh is installed and configured | ||||
|   hosts: "{{ openssh_target | default('openssh') }}" | ||||
|   become: "{{ openssh_become | default(true) }}" | ||||
|   gather_facts: "{{ openssh_gather_facts | default(true) }}" | ||||
|   roles: | ||||
|     - role: finallycoffee.base.openssh | ||||
							
								
								
									
										24
									
								
								playbooks/packages.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								playbooks/packages.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| --- | ||||
| - name: Install system packages on the remote | ||||
|   hosts: "{{ target_hosts | default('all', true) }}" | ||||
|   become: "{{ target_host_become | default(true, true) }}" | ||||
|   gather_facts: "{{ target_host_gather_facts | default(true, true) }}" | ||||
|   tasks: | ||||
|     - name: Install packages (apt) | ||||
|       ansible.builtin.apt: | ||||
|         package: "{{ package.name }}" | ||||
|         state: "{{ package.state | default('present') }}" | ||||
|       loop: "{{ system_packages | default([], true) }}" | ||||
|       loop_control: | ||||
|         loop_var: package | ||||
|         label: "{{ package.name }}" | ||||
|       when: ansible_facts['pkg_mgr'] == 'apt' | ||||
|     - name: Install packages (dnf) | ||||
|       ansible.builtin.dnf: | ||||
|         name: "{{ package.name }}" | ||||
|         state: "{{ package.state | default('present') }}" | ||||
|       loop: "{{ system_packages | default([], true) }}" | ||||
|       loop_control: | ||||
|         loop_var: package | ||||
|         label: "{{ package.name }}" | ||||
|       when: ansible_facts['pkg_mgr'] in ['dnf', 'dnf5', 'yum'] | ||||
							
								
								
									
										7
									
								
								playbooks/user.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								playbooks/user.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| --- | ||||
| - name: Configure user accounts | ||||
|   hosts: "{{ user_hosts | default('all', true) }}" | ||||
|   become: "{{ user_role_become | default(false, true) }}" | ||||
|   gather_facts: "{{ user_role_gather_facts | default(false, true) }}" | ||||
|   roles: | ||||
|     - role: finallycoffee.base.user | ||||
							
								
								
									
										33
									
								
								roles/dns/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								roles/dns/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| # `finallycoffee.base.dns` ansible role | ||||
|  | ||||
| Simple role for wrapping around the | ||||
| [`famedly.dns.update`](https://github.com/famedly/ansible-collection-dns/blob/main/plugins/modules/update.py) | ||||
| ansible module. | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| ### Example playbook | ||||
| ```yaml | ||||
| - target: "{{ target_hosts }}" | ||||
|   roles: | ||||
|     - role: finallycoffee.base.dns | ||||
|       vars: | ||||
|         dns_server: "dns.example.org" | ||||
|         dns_zone: "zone.example.org" | ||||
|         dns_records: "{{ dns_records }}" | ||||
|         dns_record_state: exact | ||||
|         dns_tsig_name: "mykeyname" | ||||
|         dns_tsig_algo: "hmac-sha256" | ||||
|         dns_tsig_key: "mykeycontent" | ||||
|   vars: | ||||
|     dns_records: | ||||
|       - type: A | ||||
|         name: gitea | ||||
|         content: "127.0.0.1" | ||||
|       - type: AAAA | ||||
|         name: gitea | ||||
|         content: "fe80::1" | ||||
|       - type: CNAME | ||||
|         name: "_acme_challenge.gitea" | ||||
|         content: "delegated-cname.challenge.example.org" | ||||
| ``` | ||||
							
								
								
									
										2
									
								
								roles/dns/defaults/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								roles/dns/defaults/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| --- | ||||
| dns_record_state: present | ||||
							
								
								
									
										11
									
								
								roles/dns/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								roles/dns/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| --- | ||||
|  | ||||
| - name: Ensure DNS records in '{{ dns_zone }}' are up to date | ||||
|   famedly.dns.update: | ||||
|     primary_master: "{{ dns_server }}" | ||||
|     zone: "{{ dns_zone }}" | ||||
|     tsig_name: "{{ dns_tsig_name }}" | ||||
|     tsig_algo: "{{ dns_tsig_algo }}" | ||||
|     tsig_key: "{{ dns_tsig_key }}" | ||||
|     rr_set: "{{ dns_records }}" | ||||
|     state: "{{ dns_record_state }}" | ||||
							
								
								
									
										13
									
								
								roles/docker/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								roles/docker/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| # `finallycoffee.base.docker` ansible role | ||||
|  | ||||
| Install and configure the docker daemon. | ||||
|  | ||||
| ## Configuration | ||||
|  | ||||
| - `docker_daemon_config` - configuration for the docker daemon | ||||
| - `docker_remove_legacy_packages` - clean up old versions of docker (see https://docs.docker.com/engine/install/debian/#uninstall-old-versions) | ||||
|  | ||||
| ## Plugins | ||||
|  | ||||
| - `docker_plugin_buildx_enable` - enable the buildx plugin | ||||
| - `docker_plugin_compose_enable` - enable docker compose | ||||
							
								
								
									
										31
									
								
								roles/docker/defaults/main/debian.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								roles/docker/defaults/main/debian.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| --- | ||||
| docker_apt_key_url: "https://download.docker.com/linux/debian/gpg" | ||||
| docker_apt_key_id: "9DC858229FC7DD38854AE2D88D81803C0EBFCD88" | ||||
|  | ||||
| docker_apt_arch: amd64 | ||||
| docker_apt_release_channel: stable | ||||
| docker_apt_repository_url: "https://download.docker.com/linux/debian" | ||||
| docker_apt_repository: >-2 | ||||
|   deb [arch={{ docker_apt_arch }}] {{ docker_apt_repository_url }} {{ ansible_distribution_release }} {{ docker_apt_release_channel }} | ||||
| docker_apt_cli_package: "docker-ce-cli" | ||||
| docker_apt_plugin_buildx_package: "docker-buildx-plugin" | ||||
| docker_apt_plugin_compose_package: "docker-compose-plugin" | ||||
| docker_apt_base_packages: | ||||
|   - "docker-ce" | ||||
|   - "docker-ce-cli" | ||||
|   - "containerd.io" | ||||
| docker_apt_packages: >-2 | ||||
|   {{ | ||||
|     docker_apt_base_packages | ||||
|     + (docker_plugin_buildx_enable | default(false) | ||||
|       | ternary([ docker_apt_plugin_buildx_package ], [])) | ||||
|     + (docker_plugin_compose_enable | default(false) | ||||
|       | ternary([ docker_apt_plugin_compose_package ], [])) | ||||
|   }} | ||||
| docker_apt_legacy_packages: | ||||
|   - "docker.io" | ||||
|   - "docker-compose" | ||||
|   - "docker-doc" | ||||
|   - "podman-docker" | ||||
|   - "containerd" | ||||
|   - "runc" | ||||
							
								
								
									
										34
									
								
								roles/docker/defaults/main/fedora.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								roles/docker/defaults/main/fedora.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| --- | ||||
| docker_fedora_repo_name: "docker-ce-stable" | ||||
| docker_fedora_repo_description: "Docker CE Stable - $basearch" | ||||
| docker_fedora_repo_url: "https://download.docker.com/linux/fedora/$releasever/$basearch/stable" | ||||
| docker_fedora_repo_validate_certs: true | ||||
| docker_fedora_repo_gpg_check: true | ||||
| docker_fedora_repo_gpg_key: "https://download.docker.com/linux/fedora/gpg" | ||||
|  | ||||
| docker_fedora_cli_package: "docker-ce-cli" | ||||
| docker_fedora_plugin_buildx_package: "docker-buildx-plugin" | ||||
| docker_fedora_plugin_compose_package: "docker-compose-plugin" | ||||
| docker_fedora_base_packages: | ||||
|   - "docker-ce" | ||||
|   - "docker-ce-cli" | ||||
|   - "containerd.io" | ||||
| docker_fedora_packages: >-2 | ||||
|   {{ | ||||
|     docker_fedora_base_packages | ||||
|     + (docker_plugin_buildx_enable | default(false) | ||||
|       | ternary([ docker_fedora_plugin_buildx_package ], [])) | ||||
|     + (docker_plugin_compose_enable | default(false) | ||||
|       | ternary([ docker_fedora_plugin_compose_package ], [])) | ||||
|   }} | ||||
| docker_fedora_legacy_packages: | ||||
|   - "docker" | ||||
|   - "docker-client" | ||||
|   - "docker-client-latest" | ||||
|   - "docker-common" | ||||
|   - "docker-latest" | ||||
|   - "docker-latest-logrotate" | ||||
|   - "docker-logrotate" | ||||
|   - "docker-selinux" | ||||
|   - "docker-engine-selinux" | ||||
|   - "docker-engine" | ||||
							
								
								
									
										13
									
								
								roles/docker/defaults/main/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								roles/docker/defaults/main/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| --- | ||||
| docker_state: "present" | ||||
|  | ||||
| docker_daemon_config: {} | ||||
| docker_daemon_config_file: "/etc/docker/daemon.json" | ||||
| docker_daemon_config_file_mode: "0644" | ||||
| docker_daemon_config_owner: root | ||||
| docker_daemon_config_group: "{{ docker_daemon_config_owner }}" | ||||
|  | ||||
| docker_plugin_buildx_enable: false | ||||
| docker_plugin_compose_enable: false | ||||
|  | ||||
| docker_remove_legacy_packages: true | ||||
							
								
								
									
										5
									
								
								roles/docker/defaults/main/systemd.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								roles/docker/defaults/main/systemd.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| --- | ||||
| docker_systemd_service_name: "docker.service" | ||||
| docker_systemd_service_state: >-2 | ||||
|   {{ (docker_state == 'present') | ternary('started', 'stopped') }} | ||||
| docker_systemd_service_enabled: "{{ (docker_state == 'present') }}" | ||||
							
								
								
									
										6
									
								
								roles/docker/handlers/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								roles/docker/handlers/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| --- | ||||
| - name: Restart docker daemon | ||||
|   ansible.builtin.systemd_service: | ||||
|     name: "{{ docker_systemd_service_name }}" | ||||
|     state: "restarted" | ||||
|   listen: "docker-restart" | ||||
							
								
								
									
										18
									
								
								roles/docker/tasks/configure.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								roles/docker/tasks/configure.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| --- | ||||
| - name: Ensure config directory '{{ docker_daemon_config_file | dirname }}' is present | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ docker_daemon_config_file | dirname }}" | ||||
|     state: "directory" | ||||
|     mode: "0755" | ||||
|     owner: "{{ docker_daemon_config_owner }}" | ||||
|     group: "{{ docker_daemon_config_group }}" | ||||
|  | ||||
| - name: Configure docker daemon using '{{ docker_daemon_config_file }}' | ||||
|   ansible.builtin.copy: | ||||
|     content: "{{ docker_daemon_config | to_nice_json }}" | ||||
|     dest: "{{ docker_daemon_config_file }}" | ||||
|     mode: "{{ docker_daemon_config_file_mode }}" | ||||
|     owner: "{{ docker_daemon_config_owner }}" | ||||
|     group: "{{ docker_daemon_config_group }}" | ||||
|   when: docker_daemon_config | string | length > 0 | ||||
|   notify: docker-restart | ||||
							
								
								
									
										30
									
								
								roles/docker/tasks/install-debian.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								roles/docker/tasks/install-debian.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| --- | ||||
| - name: Ensure legacy docker packages are removed | ||||
|   ansible.builtin.apt: | ||||
|     name: "{{ docker_apt_legacy_packages }}" | ||||
|     state: absent | ||||
|   when: docker_remove_legacy_packages | ||||
|  | ||||
| - name: Add apt key for docker repository | ||||
|   ansible.builtin.apt_key: | ||||
|     id: "{{ docker_apt_key_id }}" | ||||
|     url: "{{ docker_apt_key_url }}" | ||||
|     state: "{{ docker_state }}" | ||||
|  | ||||
| - name: Add apt repository for docker | ||||
|   ansible.builtin.apt_repository: | ||||
|     repo: "{{ docker_apt_repository }}" | ||||
|     state: "{{ docker_state }}" | ||||
|   register: docker_apt_repository_info | ||||
|  | ||||
| - name: Update apt cache if repository was newly added | ||||
|   ansible.builtin.apt: | ||||
|     update_cache: true | ||||
|   when: | ||||
|     - docker_state == 'present' | ||||
|     - docker_apt_repository_info.changed | ||||
|  | ||||
| - name: Install apt packages for docker | ||||
|   ansible.builtin.apt: | ||||
|     name: "{{ docker_apt_packages }}" | ||||
|     state: "{{ docker_state }}" | ||||
							
								
								
									
										21
									
								
								roles/docker/tasks/install-fedora.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								roles/docker/tasks/install-fedora.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| --- | ||||
| - name: Ensure legacy docker packages are removed | ||||
|   ansible.builtin.dnf: | ||||
|     name: "{{ docker_fedora_legacy_packages }}" | ||||
|     state: "removed" | ||||
|   when: docker_remove_legacy_packages | ||||
|  | ||||
| - name: Ensure dnf repository for docker is {{ docker_state }} | ||||
|   ansible.builtin.yum_repository: | ||||
|     name: "{{ docker_fedora_repo_name }}" | ||||
|     description: "{{ docker_fedora_repo_description }}" | ||||
|     baseurl: "{{ docker_fedora_repo_url }}" | ||||
|     validate_certs: "{{ docker_fedora_repo_validate_certs }}"  | ||||
|     gpgkey: "{{ docker_fedora_repo_gpg_key }}" | ||||
|     gpgcheck: "{{ docker_fedora_repo_gpg_check }}" | ||||
|     state: "{{ docker_state }}" | ||||
|  | ||||
| - name: Install dnf packages for docker | ||||
|   ansible.builtin.dnf: | ||||
|     name: "{{ docker_fedora_packages }}" | ||||
|     state: "{{ docker_state }}" | ||||
							
								
								
									
										29
									
								
								roles/docker/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								roles/docker/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| --- | ||||
| - name: Check if target OS is supported | ||||
|   ansible.builtin.fail: | ||||
|     msg: >-2 | ||||
|       OS '{{ docker_os }}' is not supported! | ||||
|   when: docker_os not in docker_supported_oses | ||||
|   vars: | ||||
|     docker_os: "{{ ansible_distribution | lower }}" | ||||
|  | ||||
| - name: Ensure docker is {{ docker_state }} on {{ ansible_distribution }} | ||||
|   ansible.builtin.include_tasks: | ||||
|     file: "install-{{ ansible_distribution | lower }}.yml" | ||||
|  | ||||
| - name: Configure docker daemon | ||||
|   ansible.builtin.include_tasks: | ||||
|     file: "configure.yml" | ||||
|   when: docker_state == 'present' | ||||
|  | ||||
| - name: Ensure docker daemon is {{ docker_systemd_service_enabled | ternary('enabled', 'disabled') }} | ||||
|   ansible.builtin.systemd_service: | ||||
|     name: "{{ docker_systemd_service_name }}" | ||||
|     enabled: "{{ docker_systemd_service_enabled }}" | ||||
|   when: ansible_facts['service_mgr'] == 'systemd' | ||||
|  | ||||
| - name: Ensure docker daemon is {{ docker_systemd_service_state }} | ||||
|   ansible.builtin.systemd_service: | ||||
|     name: "{{ docker_systemd_service_name }}" | ||||
|     state: "{{ docker_systemd_service_state }}" | ||||
|   when: ansible_facts['service_mgr'] == 'systemd' | ||||
							
								
								
									
										4
									
								
								roles/docker/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								roles/docker/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| --- | ||||
| docker_supported_oses: | ||||
|   - 'debian' | ||||
|   - 'fedora' | ||||
| @@ -1,22 +0,0 @@ | ||||
| # `finallycoffee.base.elastiscsearch` | ||||
|  | ||||
| A simple ansible role which deploys a single-node elastic container to provide | ||||
| an easy way to do some indexing. | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| Per default, `/opt/elasticsearch/data` is used to persist data, it is | ||||
| customizable by using either `elasticsearch_base_path` or `elasticsearch_data_path`. | ||||
|  | ||||
| As elasticsearch be can be quite memory heavy, the maximum amount of allowed RAM | ||||
| can be configured using `elasticsearch_allocated_ram_mb`, defaulting to 512 (mb). | ||||
|  | ||||
| The cluster name and discovery type can be overridden using | ||||
| `elasticsearch_config_cluster_name` (default: elastic) and | ||||
| `elasticsearch_config_discovery_type` (default: single-node), should one | ||||
| need a multi-node elasticsearch deployment. | ||||
|  | ||||
| Per default, no ports or networks are mapped, and explizit mapping using | ||||
| either ports (`elasticsearch_container_ports`) or networks | ||||
| (`elasticsearch_container_networks`) is required in order for other services | ||||
| to use elastic. | ||||
| @@ -1,35 +0,0 @@ | ||||
| --- | ||||
|  | ||||
| elasticsearch_version: 7.17.7 | ||||
|  | ||||
| elasticsearch_base_path: /opt/elasticsearch | ||||
| elasticsearch_data_path: "{{ elasticsearch_base_path }}/data" | ||||
|  | ||||
| elasticsearch_config_cluster_name: elastic | ||||
| elasticsearch_config_discovery_type: single-node | ||||
| elasticsearch_config_boostrap_memory_lock: true | ||||
| elasticsearch_allocated_ram_mb: 512 | ||||
|  | ||||
| elasticsearch_container_image_name: docker.elastic.co/elasticsearch/elasticsearch-oss | ||||
| elasticsearch_container_image_tag: ~ | ||||
| elasticsearch_container_image: >- | ||||
|   {{ elasticsearch_container_image_name }}:{{ elasticsearch_container_image_tag | default(elasticsearch_version, true) }} | ||||
|  | ||||
| elasticsearch_container_name: elasticsearch | ||||
| elasticsearch_container_env: | ||||
|   "ES_JAVA_OPTS": "-Xms{{ elasticsearch_allocated_ram_mb }}m -Xmx{{ elasticsearch_allocated_ram_mb }}m" | ||||
|   "cluster.name": "{{ elasticsearch_config_cluster_name }}" | ||||
|   "discovery.type": "{{ elasticsearch_config_discovery_type }}" | ||||
|   "bootstrap.memory_lock": "{{ 'true' if elasticsearch_config_boostrap_memory_lock else 'false' }}" | ||||
| elasticsearch_container_user: ~ | ||||
| elasticsearch_container_ports: ~ | ||||
| elasticsearch_container_labels: | ||||
|   version: "{{ elasticsearch_version }}" | ||||
| elasticsearch_container_ulimits: | ||||
| #  - "memlock:{{ (1.5 * 1024 * elasticsearch_allocated_ram_mb) | int }}:{{ (1.5 * 1024 * elasticsearch_allocated_ram_mb) | int }}" | ||||
|   - "memlock:-1:-1" | ||||
| elasticsearch_container_volumes: | ||||
|   - "{{ elasticsearch_data_path }}:/usr/share/elasticsearch/data:z" | ||||
| elasticsearch_container_networks: ~ | ||||
| elasticsearch_container_purge_networks: ~ | ||||
| elasticsearch_container_restart_policy: unless-stopped | ||||
| @@ -1,32 +0,0 @@ | ||||
| --- | ||||
|  | ||||
| - name: Ensure host directories are present | ||||
|   file: | ||||
|     path: "{{ item }}" | ||||
|     state: directory | ||||
|     mode: "0777" | ||||
|   loop: | ||||
|     - "{{ elasticsearch_base_path }}" | ||||
|     - "{{ elasticsearch_data_path }}" | ||||
|  | ||||
| - name: Ensure elastic container image is present | ||||
|   docker_image: | ||||
|     name: "{{ elasticsearch_container_image }}" | ||||
|     state: present | ||||
|     source: pull | ||||
|     force_source: "{{ elasticsearch_container_image_tag|default(false, true)|bool }}" | ||||
|  | ||||
| - name: Ensure elastic container is running | ||||
|   docker_container: | ||||
|     name: "{{ elasticsearch_container_name }}" | ||||
|     image: "{{ elasticsearch_container_image }}" | ||||
|     env: "{{ elasticsearch_container_env | default(omit, True) }}" | ||||
|     user: "{{ elasticsearch_container_user | default(omit, True) }}" | ||||
|     ports: "{{ elasticsearch_container_ports | default(omit, True) }}" | ||||
|     labels: "{{ elasticsearch_container_labels | default(omit, True) }}" | ||||
|     volumes: "{{ elasticsearch_container_volumes }}" | ||||
|     ulimits: "{{ elasticsearch_container_ulimits }}" | ||||
|     networks: "{{ elasticsearch_container_networks | default(omit, True) }}" | ||||
|     purge_networks: "{{ elasticsearch_container_purge_networks | default(omit, True) }}" | ||||
|     restart_policy: "{{ elasticsearch_container_restart_policy }}" | ||||
|     state: started | ||||
							
								
								
									
										46
									
								
								roles/lego/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								roles/lego/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| # `finallycoffee.base.lego` ansible role | ||||
|  | ||||
| This role can be used to retrieve ACME certificates on the target host. It uses `lego` for that, and with systemd template units provides an easy way to configure and monitor the status for each certificate. | ||||
|  | ||||
| ## Requirements | ||||
|  | ||||
| - `systemd` | ||||
| - write access to /tmp to unpack the lego release tarball during installation | ||||
| - write access to /opt/lego (or whatever `lego_base_path` is set to) for configuration and certificate data | ||||
| - `become` privileges of the `ansible_user` on the target | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| ### Required configuration | ||||
|  | ||||
| - `lego_instance` - used for allowing multiple lego jobs to run with systemd template units. recommended to be set to the CN / first SAN of the certificate. | ||||
| - `lego_cert_domains` - list of FQDNs to request a certificate for | ||||
| - `lego_acme_account_email` - when using letsencrypt, a contact email is mandatory | ||||
|  | ||||
| ### Proxies / Registries | ||||
|  | ||||
| The role ensure `lego` is downloaded from the github release page. If you are behind a proxy or use a registry like Nexus3, set `lego_release_archive_server`. | ||||
|  | ||||
| ### ACME server | ||||
|  | ||||
| Per default, the Letsencrypt Staging ACME server is configured. Set `lego_acme_server_url` from `lego_letsencrypt_server_urls.{qa,prod}` or configure your own ACME v2 server directly. | ||||
|  | ||||
| ### Certificate | ||||
|  | ||||
| To set for which domains to request a certificate for, set them as a list of SANs in `lego_cert_domains`. The default key type is EC256 and can be overridden using `lego_cert_key_type`. | ||||
|  | ||||
| Set the type of challenge in `lego_acme_challenge_type` (to either `http` or `dns`), and `lego_acme_challenge_provider` to, for example, `rfc2136` for DNS challenges using the DNSUPDATE mechanism. If your challenge needs additional data, set that in `lego_command_config` as a dictionary analog to `lego_base_command_config` (see [defaults](defaults/main.yml)). | ||||
|  | ||||
| ## Trivia | ||||
|  | ||||
| ### Architecture | ||||
|  | ||||
| By default, the lego distribution for `linux` on `amd64` is downloaded. If your target needs a different architecture or target OS, adjust this in `lego_os` and `lego_architecture`, cross-checking with the [lego GitHub release page](https://github.com/go-acme/lego/releases/tag/v4.17.4) for upstream availability. | ||||
|  | ||||
| ### User management | ||||
|  | ||||
| The role will attempt to create user+group for each seperate lego instance for data isolation (i.e. to avoid leaking a TSIG key from one lego instance to other services). The user and group are of the form `acme-{{ lego_instance }}`. Beware that changing this in `lego_cert_{user,group}` also requires `lego_systemd_{user,group}` to be adjusted! | ||||
|  | ||||
| ### Binding to ports < 1024 (HTTP-01 challenge) | ||||
|  | ||||
| Set `lego_binary_allow_net_bind_service: true` to allow the lego binary to bind to ports in the 'privileged' (< 1024) port range. | ||||
							
								
								
									
										71
									
								
								roles/lego/defaults/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								roles/lego/defaults/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| --- | ||||
| lego_user: "lego" | ||||
| lego_version: "4.23.0" | ||||
| lego_instance: default | ||||
| lego_base_path: "/opt/lego" | ||||
| lego_cert_user: "acme-{{ lego_instance }}" | ||||
| lego_cert_group: "{{ lego_cert_user }}" | ||||
| lego_cert_mode: "0640" # rw-r----- | ||||
| lego_systemd_user: "acme-%i" | ||||
| lego_systemd_group: "{{ lego_systemd_user }}" | ||||
| lego_instance_base_path: "{{ lego_base_path }}/instances" | ||||
| lego_instance_path: "{{ lego_instance_base_path }}/{{ lego_instance }}" | ||||
|  | ||||
| lego_cert_domains: [] | ||||
| lego_cert_key_type: ec256 | ||||
| lego_cert_days_to_renew: 30 | ||||
| lego_acme_account_email: ~ | ||||
| lego_acme_challenge_type: http | ||||
| lego_acme_challenge_provider: ~ | ||||
| lego_letsencrypt_server_urls: | ||||
|   qa: "https://acme-staging-v02.api.letsencrypt.org/directory" | ||||
|   prod: "https://acme-v02.api.letsencrypt.org/directory" | ||||
| lego_acme_server_url: "{{ lego_letsencrypt_server_urls.qa }}" | ||||
|  | ||||
| lego_base_environment: | ||||
|   LEGO_CERT_USER: "{{ lego_cert_user }}" | ||||
|   LEGO_CERT_GROUP: "{{ lego_cert_group }}" | ||||
|   LEGO_CERT_MODE: "{{ lego_cert_mode }}" | ||||
|   LEGO_CERT_STORE_PATH: "{{ lego_instance_path }}" | ||||
|   LEGO_CERT_DAYS_TO_RENEW: "{{ lego_cert_days_to_renew }}" | ||||
|   LEGO_KEY_TYPE: "{{ lego_cert_key_type }}" | ||||
|   LEGO_ACME_CHALLENGE_TYPE: "{{ lego_acme_challenge_type }}" | ||||
|   LEGO_ACME_SERVER: "{{ lego_acme_server_url }}" | ||||
|   LEGO_COMMAND_ARGS: "{{ lego_command_args }}" | ||||
|  | ||||
| lego_base_command_config: | ||||
|   server: "{{ lego_acme_server_url }}" | ||||
|   accept_tos: true | ||||
|   email: "{{ lego_acme_account_email }}" | ||||
|   path: "{{ lego_instance_path }}" | ||||
|   key_type: "{{ lego_cert_key_type }}" | ||||
|  | ||||
| lego_acme_challenge_config: >- | ||||
|   {{ {lego_acme_challenge_type: lego_acme_challenge_provider} }} | ||||
|  | ||||
| lego_systemd_unit_path: "/etc/systemd/system" | ||||
| lego_systemd_template_unit_name: "lego@.service" | ||||
| lego_systemd_template_unit_file: "{{ lego_systemd_template_unit_name }}.j2" | ||||
| lego_systemd_service_name: "lego@{{ lego_instance }}.service" | ||||
| lego_systemd_environment: >- | ||||
|   {{ lego_base_environment | combine(lego_environment | default({})) }} | ||||
| lego_full_command_config: >- | ||||
|   {{ lego_base_command_config | ||||
|   | combine(lego_acme_challenge_config) | ||||
|   | combine(lego_command_config | default({})) }} | ||||
|  | ||||
| lego_systemd_timer_name: "lego-{{ lego_instance }}.timer" | ||||
| lego_systemd_timer_template: lego.timer.j2 | ||||
| lego_systemd_timer_calendar: "*-*-* *:00/15:00" | ||||
|  | ||||
| lego_architecture: "{{ 'arm64' if ansible_architecture == 'aarch64' else 'amd64' }}" | ||||
| lego_os: "linux" | ||||
| lego_binary_allow_net_bind_service: false | ||||
|  | ||||
| lego_release_archive_server: "https://github.com" | ||||
| lego_release_archive_filename: >- | ||||
|   lego_v{{ lego_version }}_{{ lego_os }}_{{ lego_architecture }}.tar.gz | ||||
| lego_release_archive_url: >- | ||||
|   {{ lego_release_archive_server }}/go-acme/lego/releases/download/v{{ lego_version }}/{{ lego_release_archive_filename }} | ||||
| lego_release_archive_file_path: "/tmp/{{ lego_release_archive_filename }}" | ||||
| lego_release_archive_path: "/tmp/lego_v{{ lego_version }}_{{ lego_os }}_{{ lego_architecture }}" | ||||
							
								
								
									
										37
									
								
								roles/lego/files/lego_run.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								roles/lego/files/lego_run.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| #!/usr/bin/env bash | ||||
| set -euo pipefail | ||||
|  | ||||
| LEGO_BINARY=$(/usr/bin/env which lego) | ||||
|  | ||||
| if [[ -n "${LEGO_HTTP_FALLBACK_PORT:-}" ]]; then | ||||
|   if ! nc_binary="$(type -p 'nc')" || [[ -z $nc_binary ]]; then | ||||
|     echo "nc not found (in PATH), exiting" | ||||
|     exit 1 | ||||
|   fi | ||||
|   set +e | ||||
|   nc_exit_code=$(nc -z 127.0.0.1 $LEGO_HTTP_PORT); | ||||
|   set -e | ||||
|   if [[ $nc_exit_code -eq 0 ]]; then | ||||
|       LEGO_HTTP_PORT=$LEGO_HTTP_FALLBACK_PORT | ||||
|   fi | ||||
| fi | ||||
|  | ||||
| if [[ -n "${LEGO_PRE_RENEWAL_HOOK:-}" ]]; then | ||||
|   $LEGO_PRE_RENEWAL_HOOK | ||||
| fi | ||||
|  | ||||
| LEGO_COMMAND_ARGS_EXPANDED=$(bash -c "echo $LEGO_COMMAND_ARGS") # This is a bit icky | ||||
|  | ||||
| FILES_IN_DIR=$(find "$LEGO_CERT_STORE_PATH/certificates" -type f | wc -l) | ||||
| if [[ $FILES_IN_DIR -gt 2 ]]; then | ||||
|         $LEGO_BINARY $LEGO_COMMAND_ARGS_EXPANDED renew --days=$LEGO_CERT_DAYS_TO_RENEW | ||||
| else | ||||
|         $LEGO_BINARY $LEGO_COMMAND_ARGS_EXPANDED run | ||||
| fi | ||||
|  | ||||
| find "$LEGO_CERT_STORE_PATH/certificates" -type f | xargs -I{} -n 1 chmod "$LEGO_CERT_MODE" "{}" | ||||
| find "$LEGO_CERT_STORE_PATH/certificates" -type f | xargs -I{} -n 1 chown "${LEGO_CERT_USER}:${LEGO_CERT_GROUP}" "{}" | ||||
|  | ||||
| if [[ -n "${LEGO_POST_RENEWAL_HOOK:-}" ]]; then | ||||
|   $LEGO_POST_RENEWAL_HOOK | ||||
| fi | ||||
							
								
								
									
										5
									
								
								roles/lego/handlers/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								roles/lego/handlers/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| --- | ||||
| - name: Ensure systemd daemon is reloaded | ||||
|   ansible.builtin.systemd: | ||||
|     daemon_reload: true | ||||
|   listen: systemd_reload | ||||
							
								
								
									
										178
									
								
								roles/lego/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								roles/lego/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,178 @@ | ||||
| --- | ||||
| - name: Ensure lego cert group is created | ||||
|   ansible.builtin.group: | ||||
|     name: "{{ lego_cert_group }}" | ||||
|     state: present | ||||
|     system: true | ||||
|  | ||||
| - name: Ensure lego cert user is created | ||||
|   ansible.builtin.user: | ||||
|     name: "{{ lego_cert_user }}" | ||||
|     state: present | ||||
|     system: true | ||||
|     create_home: false | ||||
|     groups: | ||||
|       - "{{ lego_cert_group }}" | ||||
|     append: true | ||||
|  | ||||
| - name: Ensure lego user is created | ||||
|   ansible.builtin.user: | ||||
|     name: "{{ lego_user }}" | ||||
|     state: present | ||||
|     system: true | ||||
|     create_home: false | ||||
|     groups: | ||||
|       - "{{ lego_cert_group }}" | ||||
|     append: true | ||||
|  | ||||
| - name: Check if lego is present | ||||
|   ansible.builtin.command: | ||||
|     cmd: which lego | ||||
|   changed_when: false | ||||
|   failed_when: false | ||||
|   register: lego_binary_info | ||||
|   check_mode: false | ||||
|  | ||||
| - name: Check which version of lego is present | ||||
|   ansible.builtin.command: | ||||
|     cmd: "lego --version" | ||||
|   changed_when: false | ||||
|   failed_when: false | ||||
|   register: lego_binary_version_info | ||||
|   when: lego_binary_info.rc == 0 | ||||
|   check_mode: false | ||||
|  | ||||
| - name: Ensure lego is installed | ||||
|   when: (lego_binary_info.rc != 0) or (lego_version not in lego_binary_version_info.stdout) | ||||
|   block: | ||||
|     - name: Download lego from source | ||||
|       ansible.builtin.get_url: | ||||
|         url: "{{ lego_release_archive_url }}" | ||||
|         url_username: "{{ lego_release_archive_url_username | default(omit) }}" | ||||
|         url_password: "{{ lego_release_archive_url_password | default(omit) }}" | ||||
|         dest: "{{ lego_release_archive_file_path }}" | ||||
|  | ||||
|     - name: Create folder to uncompress into | ||||
|       ansible.builtin.file: | ||||
|         dest: "{{ lego_release_archive_path }}" | ||||
|         state: directory | ||||
|  | ||||
|     - name: Uncompress lego source archive | ||||
|       ansible.builtin.unarchive: | ||||
|         src: "{{ lego_release_archive_file_path }}" | ||||
|         dest: "{{ lego_release_archive_path }}" | ||||
|         remote_src: true | ||||
|       ignore_errors: "{{ ansible_check_mode }}" | ||||
|  | ||||
|     - name: Ensure lego binary is present in PATH | ||||
|       ansible.builtin.copy: | ||||
|         src: "{{ lego_release_archive_path }}/lego" | ||||
|         dest: "/usr/local/bin/lego" | ||||
|         mode: "u+rwx,g+rx,o+rx" | ||||
|         remote_src: true | ||||
|       ignore_errors: "{{ ansible_check_mode }}" | ||||
|  | ||||
|     - name: Ensure intermediate data is gone | ||||
|       ansible.builtin.file: | ||||
|         path: "{{ item }}" | ||||
|         state: absent | ||||
|       loop: | ||||
|         - "{{ lego_release_archive_path }}" | ||||
|         - "{{ lego_release_archive_file_path }}" | ||||
|  | ||||
|     - name: Ensure lego is allowed to bind to ports < 1024 | ||||
|       community.general.capabilities: | ||||
|         path: "/usr/local/bin/lego" | ||||
|         capability: "cap_net_bind_service+ep" | ||||
|         state: present | ||||
|       when: lego_binary_allow_net_bind_service | ||||
|  | ||||
| - name: Ensure lego base path exists | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ lego_base_path }}" | ||||
|     state: directory | ||||
|     mode: "0755" | ||||
|  | ||||
| - name: Ensure template unit file is present | ||||
|   ansible.builtin.template: | ||||
|     src: "{{ lego_systemd_template_unit_file }}" | ||||
|     dest: "{{ lego_systemd_unit_path }}/{{ lego_systemd_template_unit_name }}" | ||||
|   notify: | ||||
|     - systemd_reload | ||||
|  | ||||
| - name: Ensure env file is templated | ||||
|   ansible.builtin.copy: | ||||
|     content: |+ | ||||
|       {% for entry in lego_systemd_environment | dict2items %} | ||||
|       {{ entry.key }}={{ entry.value }} | ||||
|       {% endfor %} | ||||
|     dest: "{{ lego_base_path }}/{{ lego_instance }}.conf" | ||||
|   register: lego_env_file_info | ||||
|  | ||||
| - name: Ensure timer unit is templated | ||||
|   ansible.builtin.template: | ||||
|     src: "{{ lego_systemd_timer_template }}" | ||||
|     dest: "{{ lego_systemd_unit_path }}/{{ lego_systemd_timer_name }}" | ||||
|   notify: | ||||
|     - systemd_reload | ||||
|  | ||||
| - name: Ensure handling script is templated | ||||
|   ansible.builtin.copy: | ||||
|     src: "lego_run.sh" | ||||
|     dest: "{{ lego_base_path }}/run.sh" | ||||
|     mode: "0755" | ||||
|   register: lego_handler_script_info | ||||
|  | ||||
| - name: Ensure per-instance base path is created | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ lego_instance_path }}" | ||||
|     state: directory | ||||
|     owner: "{{ lego_cert_user }}" | ||||
|     group: "{{ lego_cert_group }}" | ||||
|     mode: "0755" | ||||
|  | ||||
| - name: Ensure per-instance sub folders are created with correct permissions | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ item.path }}" | ||||
|     state: directory | ||||
|     owner: "{{ item.owner | default(lego_cert_user) }}" | ||||
|     group: "{{ item.group | default(lego_cert_group) }}" | ||||
|     mode: "{{ item.mode }}" | ||||
|   loop: | ||||
|     - path: "{{ lego_instance_path }}/secrets" | ||||
|       mode: "0750" | ||||
|     - path: "{{ lego_instance_path }}/accounts" | ||||
|       mode: "0770" | ||||
|     - path: "{{ lego_instance_path }}/certificates" | ||||
|       mode: "0775" | ||||
|   loop_control: | ||||
|     label: "{{ item.path }}" | ||||
|  | ||||
| - name: Ensure systemd daemon is reloaded | ||||
|   meta: flush_handlers | ||||
|  | ||||
| - name: Ensure systemd timer is enabled | ||||
|   ansible.builtin.systemd_service: | ||||
|     name: "{{ lego_systemd_timer_name }}" | ||||
|     enabled: true | ||||
|  | ||||
| - name: Ensure systemd timer is started | ||||
|   ansible.builtin.systemd_service: | ||||
|     name: "{{ lego_systemd_timer_name }}" | ||||
|     state: "started" | ||||
|  | ||||
| - name: Check if certificates are present | ||||
|   ansible.builtin.find: | ||||
|     path: "{{ lego_instance_path }}/certificates" | ||||
|     recurse: false | ||||
|     file_type: "file" | ||||
|   register: lego_certificate_info | ||||
|  | ||||
| - name: Ensure systemd service is started once to obtain the certificate | ||||
|   ansible.builtin.systemd_service: | ||||
|     name: "{{ lego_systemd_service_name }}" | ||||
|     state: "started" | ||||
|   when: >-2 | ||||
|     lego_handler_script_info.changed | ||||
|     or lego_env_file_info.changed | ||||
|     or lego_certificate_info.files | default([]) | length == 0 | ||||
							
								
								
									
										9
									
								
								roles/lego/templates/lego.timer.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								roles/lego/templates/lego.timer.j2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| [Unit] | ||||
| Description=Run lego@{{ lego_instance}}.service | ||||
|  | ||||
| [Timer] | ||||
| OnCalendar={{ lego_systemd_timer_calendar }} | ||||
| Unit=lego@{{ lego_instance }}.service | ||||
|  | ||||
| [Install] | ||||
| WantedBy=timers.target | ||||
							
								
								
									
										14
									
								
								roles/lego/templates/lego@.service.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								roles/lego/templates/lego@.service.j2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| [Unit] | ||||
| Description=Run lego (letsencrypt client in go) | ||||
|  | ||||
| [Service] | ||||
| Type=oneshot | ||||
| EnvironmentFile={{ lego_base_path }}/%i.conf | ||||
| User={{ lego_systemd_user }} | ||||
| Group={{ lego_systemd_group }} | ||||
| ExecStart={{ lego_base_path }}/run.sh | ||||
| AmbientCapabilities=CAP_NET_BIND_SERVICE | ||||
|  | ||||
| [Install] | ||||
| WantedBy=basic.target | ||||
| DefaultInstance=default | ||||
							
								
								
									
										16
									
								
								roles/lego/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								roles/lego/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| --- | ||||
|  | ||||
| lego_domain_command_args: >- | ||||
|   {% for domain in lego_cert_domains %} | ||||
|    --domains={{ domain }} | ||||
|   {%- endfor %} | ||||
|  | ||||
| lego_config_command_args: >- | ||||
|   {% for key in lego_full_command_config %} | ||||
|    --{{ key | replace("_", "-") }} | ||||
|   {%- if lego_full_command_config[key] != None and lego_full_command_config[key] != '' -%} | ||||
|   ={{ lego_full_command_config[key] }} | ||||
|   {%- endif -%} | ||||
|   {%- endfor -%} | ||||
|  | ||||
| lego_command_args: "{{ lego_domain_command_args }} {{ lego_config_command_args }}" | ||||
| @@ -1,19 +0,0 @@ | ||||
| # `finallycoffee.base.mariadb` ansible role | ||||
|  | ||||
| This role deploys a MariaDB instance in a docker container. | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| The role expects the following variables to be populated with values and/or secrets: | ||||
|  | ||||
| ```yaml | ||||
| mariadb_root_password: #mariadb root password | ||||
| mariadb_database: # name of the database to create | ||||
| mariadb_username: # name of a user to auto-create and assign permission on the mariadb_database | ||||
| mariadb_password: # password of the user in mariadb_username | ||||
| ``` | ||||
|  | ||||
| ## Requirements | ||||
|  | ||||
| - Docker installed | ||||
| - python-docker present on target system for ansible to be able to talk with the docker API. | ||||
| @@ -1,32 +0,0 @@ | ||||
| --- | ||||
|  | ||||
| mariadb_version: "10.6.11" | ||||
| mariadb_base_path: /var/lib/mariadb | ||||
| mariadb_data_path: "{{ mariadb_base_path }}/{{ mariadb_version }}" | ||||
|  | ||||
| mariadb_root_password: ~ | ||||
| mariadb_database: ~ | ||||
| mariadb_username: ~ | ||||
| mariadb_password: ~ | ||||
|  | ||||
| mariadb_container_base_environment: | ||||
|   MARIADB_ROOT_PASSWORD: "{{ mariadb_root_password }}" | ||||
| mariadb_container_extra_environment: {} | ||||
|  | ||||
| mariadb_container_name: mariadb | ||||
| mariadb_container_image_name: docker.io/mariadb | ||||
| mariadb_container_image_tag: ~ | ||||
| mariadb_container_image: "{{ mariadb_container_image_name }}:{{ mariadb_container_image_tag | default(mariadb_version, true) }}" | ||||
| mariadb_container_base_volumes: | ||||
|   - "{{ mariadb_data_path }}:{{ mariadb_container_data_path }}:z" | ||||
| mariadb_container_extra_volumes: [] | ||||
| mariadb_container_base_labels: | ||||
|   version: "{{ mariadb_version }}" | ||||
| mariadb_container_extra_labels: {} | ||||
| mariadb_container_restart_policy: "unless-stopped" | ||||
| mariadb_container_environment: >-2 | ||||
|   {{ mariadb_container_base_environment | ||||
|   | combine(mariadb_container_database_environment | ||||
|       if (mariadb_database and mariadb_username and mariadb_password) | ||||
|       else {}, recursive=True) | ||||
|   | combine(mariadb_container_extra_environment) }} | ||||
| @@ -1,20 +0,0 @@ | ||||
| --- | ||||
| - name: Ensure mariaDB container image is present on host | ||||
|   community.docker.docker_image: | ||||
|     name: "{{ mariadb_container_image }}" | ||||
|     state: present | ||||
|     source: pull | ||||
|  | ||||
| - name: Ensure mariaDB {{ mariadb_version }} is running as '{{ mariadb_container_name }}' | ||||
|   community.docker.docker_container: | ||||
|     name: "{{ mariadb_container_name }}" | ||||
|     image: "{{ mariadb_container_image }}" | ||||
|     env: "{{ mariadb_container_environment }}" | ||||
|     ports: "{{ mariadb_container_ports }}" | ||||
|     labels: "{{ mariadb_container_labels }}" | ||||
|     volumes: "{{ mariadb_container_volumes }}" | ||||
|     networks: "{{ mariadb_container_networks | default(omit, true) }}" | ||||
|     etc_hosts: "{{ mariadb_container_etc_hosts | default(omit, true) }}" | ||||
|     purge_networks: "{{ mariadb_container_purge_networks | default(omit, true) }}" | ||||
|     restart_policy: "{{ mariadb_container_restart_policy }}" | ||||
|     state: started | ||||
| @@ -1,10 +0,0 @@ | ||||
| --- | ||||
|  | ||||
| mariadb_container_database_environment: | ||||
|   MARIADB_DATABASE: "{{ mariadb_database }}" | ||||
|   MARIADB_USER: "{{ mariadb_username }}" | ||||
|   MARIADB_PASSWORD: "{{ mariadb_password }}" | ||||
|  | ||||
| mariadb_container_data_path: /var/lib/mysql | ||||
| mariadb_container_volumes: "{{ mariadb_container_base_volumes + mariadb_container_extra_volumes }}" | ||||
| mariadb_container_labels: "{{ mariadb_container_base_labels | combine(mariadb_container_extra_labels, recursive=True) }}" | ||||
| @@ -1,17 +1,7 @@ | ||||
| --- | ||||
| 
 | ||||
| minio_user: ~ | ||||
| minio_data_path: /opt/minio | ||||
| 
 | ||||
| minio_create_user: false | ||||
| minio_manage_host_filesystem: false | ||||
| 
 | ||||
| minio_root_username: root | ||||
| minio_root_password: ~ | ||||
| 
 | ||||
| minio_container_name: minio | ||||
| minio_container_image_name: docker.io/minio/minio | ||||
| minio_container_image_tag: latest | ||||
| minio_container_image_name: "docker.io/minio/minio" | ||||
| minio_container_image_tag: "RELEASE.2025-04-22T22-12-26Z" | ||||
| minio_container_image: "{{ minio_container_image_name }}:{{ minio_container_image_tag }}" | ||||
| minio_container_networks: [] | ||||
| minio_container_ports: [] | ||||
| @@ -30,9 +20,12 @@ minio_container_labels: {} | ||||
| minio_container_command: | ||||
|   - "server" | ||||
|   - "{{ minio_container_data_path }}" | ||||
|   - "--console-address \":{{ minio_container_listen_port_console }}\"" | ||||
|   - "--console-address" | ||||
|   - ":{{ minio_container_listen_port_console }}" | ||||
| minio_container_restart_policy: "unless-stopped" | ||||
| minio_container_image_force_source: "{{ (minio_container_image_tag == 'latest')|bool }}" | ||||
| minio_container_state: >-2 | ||||
|   {{ (minio_state == 'present') | ternary('started', 'absent') }} | ||||
| 
 | ||||
| minio_container_listen_port_api: 9000 | ||||
| minio_container_listen_port_console: 8900 | ||||
							
								
								
									
										12
									
								
								roles/minio/defaults/main/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								roles/minio/defaults/main/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| --- | ||||
| minio_user: ~ | ||||
| minio_data_path: /opt/minio | ||||
|  | ||||
| minio_create_user: false | ||||
| minio_manage_host_filesystem: false | ||||
|  | ||||
| minio_root_username: root | ||||
| minio_root_password: ~ | ||||
|  | ||||
| minio_state: present | ||||
| minio_deployment_method: docker | ||||
							
								
								
									
										29
									
								
								roles/minio/tasks/deploy-docker.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								roles/minio/tasks/deploy-docker.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| --- | ||||
| - name: Ensure filesystem mounts ({{ minio_data_path }}) for container volumes are present | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ minio_data_path }}" | ||||
|     state: directory | ||||
|     user: "{{ minio_user|default(omit, True) }}" | ||||
|     group: "{{ minio_user|default(omit, True) }}" | ||||
|   when: minio_manage_host_filesystem | ||||
|  | ||||
| - name: Ensure container image '{{ minio_container_image }}' is {{ minio_state }} | ||||
|   community.docker.docker_image: | ||||
|     name: "{{ minio_container_image }}" | ||||
|     state: "{{ minio_state }}" | ||||
|     source: pull | ||||
|     force_source: "{{ minio_container_image_force_source }}" | ||||
|  | ||||
| - name: Ensure container '{{ minio_container_name }}' is {{ minio_container_state }} | ||||
|   community.docker.docker_container: | ||||
|     name: "{{ minio_container_name }}" | ||||
|     image: "{{ minio_container_image }}" | ||||
|     volumes: "{{ minio_container_volumes }}" | ||||
|     env: "{{ minio_container_env }}" | ||||
|     labels: "{{ minio_container_labels }}" | ||||
|     networks: "{{ minio_container_networks }}" | ||||
|     ports: "{{ minio_container_ports }}" | ||||
|     user: "{{ minio_user|default(omit, True) }}" | ||||
|     command: "{{ minio_container_command }}" | ||||
|     restart_policy: "{{ minio_container_restart_policy }}" | ||||
|     state: "{{ minio_container_state }}" | ||||
| @@ -1,37 +1,25 @@ | ||||
| --- | ||||
| - name: Ensure 'minio_state' is valid | ||||
|   ansible.builtin.fail: | ||||
|     msg: >- | ||||
|       Unsupported state '{{ minio_state }}'! | ||||
|       Supported states are {{ minio_states | join(', ') }}. | ||||
|   when: minio_state not in minio_states | ||||
|  | ||||
| - name: Ensure minio run user is present | ||||
|   user: | ||||
| - name: Ensure 'minio_deployment_method' is valid | ||||
|   ansible.builtin.fail: | ||||
|     msg: >- | ||||
|       Unsupported state '{{ minio_deployment_method }}'! | ||||
|       Supported states are {{ minio_deployment_methods | join(', ') }}. | ||||
|   when: minio_deployment_method not in minio_deployment_methods | ||||
|  | ||||
| - name: Ensure minio run user is {{ minio_state }} | ||||
|   ansible.builtin.user: | ||||
|     name: "{{ minio_user }}" | ||||
|     state: present | ||||
|     system: yes | ||||
|     state: "{{ minio_state }}" | ||||
|     system: true | ||||
|   when: minio_create_user | ||||
|  | ||||
| - name: Ensure filesystem mounts ({{ minio_data_path }}) for container volumes are present | ||||
|   file: | ||||
|     path: "{{ minio_data_path }}" | ||||
|     state: directory | ||||
|     user: "{{ minio_user|default(omit, True) }}" | ||||
|     group: "{{ minio_user|default(omit, True) }}" | ||||
|   when: minio_manage_host_filesystem | ||||
|  | ||||
| - name: Ensure container image for minio is present | ||||
|   community.docker.docker_image: | ||||
|     name: "{{ minio_container_image }}" | ||||
|     state: present | ||||
|     source: pull | ||||
|     force_source: "{{ minio_container_image_force_source }}" | ||||
|  | ||||
| - name: Ensure container {{ minio_container_name }} is running | ||||
|   docker_container: | ||||
|     name: "{{ minio_container_name }}" | ||||
|     image: "{{ minio_container_image }}" | ||||
|     volumes: "{{ minio_container_volumes }}" | ||||
|     env: "{{ minio_container_env }}" | ||||
|     labels: "{{ minio_container_labels }}" | ||||
|     networks: "{{ minio_container_networks }}" | ||||
|     ports: "{{ minio_container_ports }}" | ||||
|     user: "{{ minio_user|default(omit, True) }}" | ||||
|     command: "{{ minio_container_command }}" | ||||
|     restart_policy: "{{ minio_container_restart_policy }}" | ||||
|     state: started | ||||
| - name: Deploy minio using {{ minio_deployment_method }} | ||||
|   ansible.builtin.include_tasks: | ||||
|     file: "deploy-{{ minio_deployment_method }}.yml" | ||||
|   | ||||
| @@ -1,5 +1,9 @@ | ||||
| --- | ||||
|  | ||||
| minio_states: | ||||
|   - present | ||||
|   - absent | ||||
| minio_deployment_methods: | ||||
|   - docker | ||||
| minio_container_volumes: "{{ minio_container_base_volumes + minio_container_extra_volumes }}" | ||||
|  | ||||
| minio_container_env: "{{ minio_container_base_env | combine(minio_container_extra_env) }}" | ||||
|   | ||||
							
								
								
									
										4
									
								
								roles/mosh/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								roles/mosh/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| # `finallycoffee.base.mosh` | ||||
|  | ||||
| Installs [`mosh`](https://mosh.org/#), a remote 'mobile shell' which supports | ||||
| roaming and re-uses SSH for the authentication layer. | ||||
							
								
								
									
										2
									
								
								roles/mosh/defaults/main/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								roles/mosh/defaults/main/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| --- | ||||
| mosh_state: present | ||||
							
								
								
									
										15
									
								
								roles/mosh/defaults/main/packages.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								roles/mosh/defaults/main/packages.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| --- | ||||
| mosh_debian_packages: | ||||
|   - "mosh" | ||||
|   - "openssh-server" | ||||
| mosh_fedora_packages: | ||||
|   - "mosh" | ||||
|   - "openssh-server" | ||||
| mosh_archlinux_packages: | ||||
|   - "mosh" | ||||
|   - "openssh" | ||||
|  | ||||
| mosh_packages: | ||||
|   debian: "{{ mosh_debian_packages }}" | ||||
|   fedora: "{{ mosh_fedora_packages }}" | ||||
|   archlinux: "{{ mosh_archlinux_packages }}" | ||||
							
								
								
									
										30
									
								
								roles/mosh/tasks/install.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								roles/mosh/tasks/install.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| --- | ||||
| - name: Ensure mosh is {{ mosh_state }} (dnf) | ||||
|   ansible.builtin.dnf: | ||||
|     name: "{{ mosh_packages[_key] }}" | ||||
|     state: "{{ mosh_state }}" | ||||
|   when: | ||||
|     - ansible_facts['pkg_mgr'] in ['dnf', 'dnf5'] | ||||
|     - _key in mosh_packages.keys() | ||||
|   vars: | ||||
|     _key: "{{ ansible_distribution | lower }}" | ||||
|  | ||||
| - name: Ensure mosh is {{ mosh_state }} (apt) | ||||
|   ansible.builtin.apt: | ||||
|     package: "{{ mosh_packages[_key] }}" | ||||
|     state: "{{ mosh_state }}" | ||||
|   when: | ||||
|     - ansible_facts['pkg_mgr'] in ['apt'] | ||||
|     - _key in mosh_packages.keys() | ||||
|   vars: | ||||
|     _key: "{{ ansible_distribution | lower }}" | ||||
|  | ||||
| - name: Ensure mosh is {{ mosh_state }} (pacman) | ||||
|   community.general.pacman: | ||||
|     name: "{{ mosh_packages[_key] }}" | ||||
|     state: "{{ mosh_state }}" | ||||
|   when: | ||||
|     - ansible_facts['pkg_mgr'] in ['pacman'] | ||||
|     - _key in mosh_packages.keys() | ||||
|   vars: | ||||
|     _key: "{{ ansible_distribution | lower }}" | ||||
							
								
								
									
										11
									
								
								roles/mosh/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								roles/mosh/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| --- | ||||
| - name: Ensure 'mosh_state' is valid | ||||
|   ansible.builtin.fail: | ||||
|     msg: >-2 | ||||
|       Invalid state '{{ mosh_state }}' for 'mosh_state'! | ||||
|       Allowed states are {{ mosh_states | join(', ') }}. | ||||
|   when: mosh_state not in mosh_states | ||||
|  | ||||
| - name: Ensure mosh is {{ mosh_state }} | ||||
|   ansible.builtin.include_tasks: | ||||
|     file: "install.yml" | ||||
							
								
								
									
										4
									
								
								roles/mosh/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								roles/mosh/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| --- | ||||
| mosh_states: | ||||
|   - "present" | ||||
|   - "absent" | ||||
| @@ -26,3 +26,8 @@ For exposing this server to the host and/or internet, the `nginx_container_ports | ||||
| from host to container), `nginx_container_networks` (docker networking) or `nginx_container_labels` | ||||
| (for label-based routing discovery like traefik) can be used. The options correspond to the arguments | ||||
| of the `community.docker.docker_container` module. | ||||
|  | ||||
| ## Deployment methods | ||||
|  | ||||
| Set `nginx_deployment_method` to either `docker` or `podman` to use the respective ansible modules for | ||||
| creating and managing the container and its image. See all supported methods in `nginx_deployment_methods`. | ||||
|   | ||||
| @@ -1,9 +1,10 @@ | ||||
| --- | ||||
|  | ||||
| nginx_version: "1.25.1" | ||||
| nginx_version: "1.28.0" | ||||
| nginx_flavour: alpine | ||||
| nginx_base_path: /opt/nginx | ||||
| nginx_config_file: "{{ nginx_base_path }}/nginx.conf" | ||||
| nginx_state: present | ||||
| nginx_deployment_method: docker | ||||
|  | ||||
| nginx_container_name: nginx | ||||
| nginx_container_image_reference: >- | ||||
| @@ -26,6 +27,9 @@ nginx_container_image_repository: >- | ||||
| nginx_container_image_registry: "docker.io" | ||||
| nginx_container_image_name: "nginx" | ||||
| nginx_container_image_tag: ~ | ||||
| nginx_container_image_source: pull | ||||
| nginx_container_state: >-2 | ||||
|   {{ (nginx_state == 'present') | ternary('started', 'absent') }} | ||||
|  | ||||
| nginx_container_restart_policy: "unless-stopped" | ||||
| nginx_container_volumes: | ||||
|   | ||||
							
								
								
									
										12
									
								
								roles/nginx/meta/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								roles/nginx/meta/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| --- | ||||
| allow_duplicates: true | ||||
| dependencies: [] | ||||
| galaxy_info: | ||||
|   role_name: nginx | ||||
|   description: Deploy nginx, a webserver | ||||
|   galaxy_tags: | ||||
|     - nginx | ||||
|     - http | ||||
|     - webserver | ||||
|     - docker | ||||
|     - podman | ||||
							
								
								
									
										28
									
								
								roles/nginx/tasks/deploy-docker.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								roles/nginx/tasks/deploy-docker.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| --- | ||||
| - name: Ensure docker container image '{{ nginx_container_image_reference }}' is {{ nginx_state }} | ||||
|   community.docker.docker_image: | ||||
|     name: "{{ nginx_container_image_reference }}" | ||||
|     state: "{{ nginx_state }}" | ||||
|     source: "{{ nginx_container_image_source }}" | ||||
|     force_source: >-2 | ||||
|       {{ nginx_container_image_force_source | ||||
|         | default(nginx_container_image_tag | default(false, true)) }} | ||||
|   register: nginx_container_image_info | ||||
|   until: nginx_container_image_info is success | ||||
|   retries: 5 | ||||
|   delay: 3 | ||||
|  | ||||
| - name: Ensure docker container '{{ nginx_container_name }}' is {{ nginx_container_state }} | ||||
|   community.docker.docker_container: | ||||
|     name: "{{ nginx_container_name }}" | ||||
|     image: "{{ nginx_container_image_reference }}" | ||||
|     env: "{{ nginx_container_env | default(omit, true) }}" | ||||
|     user: "{{ nginx_container_user | default(omit, true) }}" | ||||
|     ports: "{{ nginx_container_ports | default(omit, true) }}" | ||||
|     labels: "{{ nginx_container_labels | default(omit, true) }}" | ||||
|     volumes: "{{ nginx_container_volumes | default(omit, true) }}" | ||||
|     etc_hosts: "{{ nginx_container_etc_hosts | default(omit, true) }}" | ||||
|     networks: "{{ nginx_container_networks | default(omit, true) }}" | ||||
|     purge_networks: "{{ nginx_container_purge_networks | default(omit, true) }}" | ||||
|     restart_policy: "{{ nginx_container_restart_policy }}" | ||||
|     state: "{{ nginx_container_state }}" | ||||
							
								
								
									
										27
									
								
								roles/nginx/tasks/deploy-podman.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								roles/nginx/tasks/deploy-podman.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| --- | ||||
| - name: Ensure container image '{{ nginx_container_image_reference }}' is {{ nginx_state }} | ||||
|   containers.podman.podman_image: | ||||
|     name: "{{ nginx_container_image_reference }}" | ||||
|     state: "{{ nginx_state }}" | ||||
|     pull: "{{ nginx_container_image_source == 'pull' }}" | ||||
|     force: >-2 | ||||
|       {{ nginx_container_image_force_source | ||||
|         | default(nginx_container_image_tag | default(false, true)) }} | ||||
|   register: nginx_container_image_info | ||||
|   until: nginx_container_image_info is success | ||||
|   retries: 5 | ||||
|   delay: 3 | ||||
|  | ||||
| - name: Ensure container '{{ nginx_container_name }}' is {{ nginx_container_state }} | ||||
|   containers.podman.podman_container: | ||||
|     name: "{{ nginx_container_name }}" | ||||
|     image: "{{ nginx_container_image_reference }}" | ||||
|     env: "{{ nginx_container_env | default(omit, true) }}" | ||||
|     user: "{{ nginx_container_user | default(omit, true) }}" | ||||
|     ports: "{{ nginx_container_ports | default(omit, true) }}" | ||||
|     labels: "{{ nginx_container_labels | default(omit, true) }}" | ||||
|     volumes: "{{ nginx_container_volumes | default(omit, true) }}" | ||||
|     etc_hosts: "{{ nginx_container_etc_hosts | default(omit, true) }}" | ||||
|     network: "{{ nginx_container_networks | default(omit, true) }}" | ||||
|     restart_policy: "{{ nginx_container_restart_policy }}" | ||||
|     state: "{{ nginx_container_state }}" | ||||
| @@ -1,10 +1,30 @@ | ||||
| --- | ||||
| - name: Check if state is supported | ||||
|   ansible.builtin.fail: | ||||
|     msg: >-2 | ||||
|       Unsupported state '{{ nginx_state }}'. Supported | ||||
|       states are {{ nginx_states | join(', ') }}. | ||||
|   when: nginx_state not in nginx_states | ||||
|  | ||||
| - name: Ensure base path '{{ nginx_base_path }}' exists | ||||
| - name: Check if deployment_method is supported | ||||
|   ansible.builtin.fail: | ||||
|     msg: >-2 | ||||
|       Unsupported state '{{ nginx_deployment_method }}'. Supported | ||||
|       states are {{ nginx_deployment_methods | join(', ') }}. | ||||
|   when: nginx_deployment_method not in nginx_deployment_methods | ||||
|  | ||||
| - name: Ensure nginx config file is {{ nginx_state }} | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ nginx_config_file }}" | ||||
|     state: "{{ nginx_state }}" | ||||
|   when: nginx_state == 'absent' | ||||
|  | ||||
| - name: Ensure base path '{{ nginx_base_path }}' is {{ nginx_state }} | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ nginx_base_path }}" | ||||
|     state: directory | ||||
|     mode: 0755 | ||||
|     mode: "0755" | ||||
|     state: >-2 | ||||
|       {{ (nginx_state == 'present') | ternary('directory', 'absent') }} | ||||
|  | ||||
| - name: Ensure nginx config file is templated | ||||
|   ansible.builtin.copy: | ||||
| @@ -13,25 +33,8 @@ | ||||
|     mode: 0640 | ||||
|   notify: | ||||
|     - restart-nginx | ||||
|   when: nginx_state == 'present' | ||||
|  | ||||
| - name: Ensure docker container image is present | ||||
|   community.docker.docker_image: | ||||
|     name: "{{ nginx_container_image_reference }}" | ||||
|     state: present | ||||
|     source: pull | ||||
|     force_source: "{{ nginx_container_image_tag is defined and nginx_container_image_tag | string != '' }}" | ||||
|  | ||||
| - name: Ensure docker container '{{ nginx_container_name }}' is running | ||||
|   community.docker.docker_container: | ||||
|     name: "{{ nginx_container_name }}" | ||||
|     image: "{{ nginx_container_image_reference }}" | ||||
|     env: "{{ nginx_container_env | default(omit, true) }}" | ||||
|     user: "{{ nginx_container_user | default(omit, true) }}" | ||||
|     ports: "{{ nginx_container_ports | default(omit, true) }}" | ||||
|     labels: "{{ nginx_container_labels | default(omit, true) }}" | ||||
|     volumes: "{{ nginx_container_volumes | default(omit, true) }}" | ||||
|     etc_hosts: "{{ nginx_container_etc_hosts | default(omit, true) }}" | ||||
|     networks: "{{ nginx_container_networks | default(omit, true) }}" | ||||
|     purge_networks: "{{ nginx_container_purge_networks | default(omit, true) }}" | ||||
|     restart_policy: "{{ nginx_container_restart_policy }}" | ||||
|     state: started | ||||
| - name: Deploy using {{ nginx_deployment_method }} | ||||
|   ansible.builtin.include_tasks: | ||||
|     file: "deploy-{{ nginx_deployment_method }}.yml" | ||||
|   | ||||
							
								
								
									
										7
									
								
								roles/nginx/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								roles/nginx/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| --- | ||||
| nginx_states: | ||||
|   - present | ||||
|   - absent | ||||
| nginx_deployment_methods: | ||||
|   - docker | ||||
|   - podman | ||||
							
								
								
									
										13
									
								
								roles/openssh/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								roles/openssh/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| # `finallycoffee.base.openssh` | ||||
|  | ||||
| Ansible role to manage and configure openssh and it's components (like `sshd`). | ||||
|  | ||||
| Currently supports `fedora` and `debian` linux distributions. | ||||
|  | ||||
| ## `sshd` | ||||
|  | ||||
| To configure `sshd`, see the [`defaults/main/sshd.yml`](defaults/main/sshd.yml), | ||||
| where snake\_cased config keys for `/etc/ssh/sshd_config` are available in | ||||
| the `openssh_sshd_config_` namespace. | ||||
|  | ||||
| To add your own config on top, simply use key-value syntax in `openssh_sshd_config`. | ||||
							
								
								
									
										3
									
								
								roles/openssh/defaults/main/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								roles/openssh/defaults/main/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| --- | ||||
| openssh_state: 'present' | ||||
| openssh_sshd_config_file: "/etc/ssh/sshd_config" | ||||
							
								
								
									
										8
									
								
								roles/openssh/defaults/main/packages.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								roles/openssh/defaults/main/packages.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| --- | ||||
| openssh_packages: | ||||
|   fedora: "{{ openssh_fedora_packages }}" | ||||
|   debian: "{{ openssh_debian_packages }}" | ||||
| openssh_fedora_packages: | ||||
|   - "openssh-server" | ||||
| openssh_debian_packages: | ||||
|   - "openssh-server" | ||||
							
								
								
									
										33
									
								
								roles/openssh/defaults/main/sshd.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								roles/openssh/defaults/main/sshd.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| --- | ||||
| openssh_sshd_enable: true | ||||
| openssh_sshd_config_pubkey_authentication: true | ||||
| openssh_sshd_config_password_authentication: false | ||||
| openssh_sshd_config_challenge_response_authentication: false | ||||
| openssh_sshd_config_permit_root_login: false | ||||
|  | ||||
| # Limits | ||||
| openssh_sshd_config_max_sessions: ~ | ||||
| openssh_sshd_config_max_startups: ~ | ||||
|  | ||||
| # Hardening | ||||
| openssh_sshd_config_protocol: 2 | ||||
| openssh_sshd_config_x11_forwarding: false | ||||
| openssh_sshd_config_allow_agent_forwarding: false | ||||
| openssh_sshd_config_allow_tcp_forwarding: false | ||||
|  | ||||
| openssh_sshd_default_config: | ||||
|   PubkeyAuthentication: "{{ openssh_sshd_config_pubkey_authentication }}" | ||||
|   PasswordAuthentication: "{{ openssh_sshd_config_password_authentication }}" | ||||
|   ChallengeResponseAuthentication: >-2 | ||||
|     {{ openssh_sshd_config_challenge_response_authentication }} | ||||
|   PermitRootLogin: "{{ openssh_sshd_config_permit_root_login }}" | ||||
|   MaxSessions: "{{ openssh_sshd_config_max_sessions }}" | ||||
|   MaxStartups: "{{ openssh_sshd_config_max_startups }}" | ||||
|   Protocol: "{{ openssh_sshd_config_protocol }}" | ||||
|   X11Forwarding: "{{ openssh_sshd_config_x11_forwarding }}" | ||||
|   AllowAgentForwarding: "{{ openssh_sshd_config_allow_agent_forwarding }}" | ||||
|   AllowTcpForwarding: "{{ openssh_sshd_config_allow_tcp_forwarding }}" | ||||
|  | ||||
| openssh_sshd_merged_config: >-2 | ||||
|   {{ openssh_sshd_default_config | default({}, true) | ||||
|      | combine(openssh_sshd_config | default({}, true)) }} | ||||
							
								
								
									
										2
									
								
								roles/openssh/defaults/main/systemd.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								roles/openssh/defaults/main/systemd.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| --- | ||||
| openssh_sshd_systemd_service_name: "sshd.service" | ||||
							
								
								
									
										7
									
								
								roles/openssh/handlers/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								roles/openssh/handlers/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| --- | ||||
| - name: Ensure sshd is reloaded | ||||
|   ansible.builtin.systemd_service: | ||||
|     name: "{{ openssh_sshd_systemd_service_name }}" | ||||
|     state: "reloaded" | ||||
|   when: ansible_facts['service_mgr'] == 'systemd' | ||||
|   listen: openssh_sshd_reload | ||||
							
								
								
									
										28
									
								
								roles/openssh/tasks/configure-sshd.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								roles/openssh/tasks/configure-sshd.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| --- | ||||
| - name: Configure sshd | ||||
|   ansible.builtin.lineinfile: | ||||
|     path: "{{ openssh_sshd_config_file }}" | ||||
|     regexp: "{{ openssh_sshd_config_regexp }}" | ||||
|     line: "{{ openssh_sshd_config_line }}" | ||||
|     firstmatch: true | ||||
|     state: present | ||||
|     validate: "sshd -Tf %s" | ||||
|   loop: "{{ openssh_sshd_merged_config | dict2items }}" | ||||
|   loop_control: | ||||
|     loop_var: "tuple" | ||||
|     label: "{{ tuple.key }}" | ||||
|   notify: | ||||
|     - openssh_sshd_reload | ||||
|   vars: | ||||
|     openssh_sshd_config_regexp: "^\\s*#?\\s*{{ tuple.key }}" | ||||
|     openssh_sshd_config_line: >-2 | ||||
|       {{ openssh_sshd_config_line_commented }}{{ tuple.key }} {{ openssh_sshd_config_value }} | ||||
|     openssh_sshd_config_value_is_none: "{{ tuple.value is none }}" | ||||
|     openssh_sshd_config_line_commented: >-2 | ||||
|       {{ openssh_sshd_config_value_is_none | ternary('#', '') }} | ||||
|     openssh_sshd_config_value: >-2 | ||||
|       {{ (tuple.value is boolean) | ternary( | ||||
|            tuple.value | ternary('yes', 'no'), | ||||
|            tuple.value | ||||
|          ) | ||||
|       }} | ||||
							
								
								
									
										16
									
								
								roles/openssh/tasks/install.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								roles/openssh/tasks/install.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| --- | ||||
| - name: Ensure openssh server package is {{ openssh_state }} (dnf) | ||||
|   ansible.builtin.dnf: | ||||
|     name: "{{ openssh_packages[ansible_distribution | lower] }}" | ||||
|     state: "{{ openssh_state }}" | ||||
|   when: | ||||
|     - ansible_facts['pkg_mgr'] in ['dnf', 'dnf5'] | ||||
|     - ansible_distribution | lower in openssh_packages.keys() | ||||
|  | ||||
| - name: Ensure openssh server package is {{ openssh_state }} (apt) | ||||
|   ansible.builtin.apt: | ||||
|     package: "{{ openssh_packages[ansible_distribution | lower] }}" | ||||
|     state: "{{ openssh_state }}" | ||||
|   when: | ||||
|     - ansible_facts['pkg_mgr'] in ['apt'] | ||||
|     - ansible_distribution | lower in openssh_packages.keys() | ||||
							
								
								
									
										15
									
								
								roles/openssh/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								roles/openssh/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| --- | ||||
| - name: Ensure 'openssh_state' is valid | ||||
|   ansible.builtin.fail: | ||||
|     msg: >-2 | ||||
|       Invalid value '{{ openssh_state }}' for 'openssh_state'. | ||||
|       Valid values are {{ openssh_states | join(', ') }}! | ||||
|   when: openssh_state not in openssh_states | ||||
|  | ||||
| - name: Ensure openssh is {{ openssh_state }} | ||||
|   ansible.builtin.include_tasks: | ||||
|     file: "install.yml" | ||||
|  | ||||
| - name: Ensure sshd is configured | ||||
|   ansible.builtin.include_tasks: | ||||
|     file: "configure-sshd.yml" | ||||
							
								
								
									
										4
									
								
								roles/openssh/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								roles/openssh/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| --- | ||||
| openssh_states: | ||||
|   - "present" | ||||
|   - "absent" | ||||
							
								
								
									
										25
									
								
								roles/powerdns_tsig_key/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								roles/powerdns_tsig_key/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| # `finallycoffee.base.powerdns_tsig_key` | ||||
|  | ||||
| Simple ansible role for ensuring a TSIG key is present in a given PowerDNS- | ||||
| instance. | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| The usage example below assumes `powerdns` is running in a container named `powerdns` (as supplied to `powerdns_tsig_key_container_name`. | ||||
|  | ||||
| ```yaml | ||||
| - hosts: "{{ target_hosts }}" | ||||
|   become: true | ||||
|   roles: | ||||
|     - role: finallycoffee.base.powerdns_tsig_key | ||||
|       vars: | ||||
|         powerdns_tsig_key_name: "nameofmykey" | ||||
|         powerdns_tsig_key_path: "/var/lib/myapp/tsig.key" | ||||
|         powernds_tsig_key_algo: "hmac-sha512" | ||||
|         powerdns_tsig_key_path_owner: "myappuser" | ||||
|         powerdns_tsig_key_path_group: "myappgroup" | ||||
|         powerdns_tsig_key_container_name: 'powerdns' | ||||
| ``` | ||||
|  | ||||
| > [!NOTE] | ||||
| > Support for non-docker deployments is pending. | ||||
							
								
								
									
										2
									
								
								roles/powerdns_tsig_key/defaults/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								roles/powerdns_tsig_key/defaults/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| --- | ||||
| powerdns_tsig_key_container_name: powerdns | ||||
							
								
								
									
										104
									
								
								roles/powerdns_tsig_key/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								roles/powerdns_tsig_key/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| --- | ||||
| - name: Ensure unix group '{{ powerdns_tsig_key_path_group }}' exists | ||||
|   ansible.builtin.group: | ||||
|     name: "{{ powerdns_tsig_key_path_group }}" | ||||
|     state: "present" | ||||
|     system: true | ||||
|   register: powerdns_tsig_key_path_group_info | ||||
|   when: powerdns_tsig_key_path_group is defined | ||||
|  | ||||
| - name: Ensure unix user '{{ powerdns_tsig_key_path_owner }}' exists | ||||
|   ansible.builtin.user: | ||||
|     name: "{{ powerdns_tsig_key_path_owner }}" | ||||
|     state: "present" | ||||
|     system: true | ||||
|     create_home: false | ||||
|     groups: "{{ powerdns_tsig_key_path_group is defined | ternary([powerdns_tsig_key_path_group], omit) }}" | ||||
|     append: "{{ powerdns_tsig_key_path_group is defined | ternary(true, omit) }}" | ||||
|   register: powerdns_tsig_key_path_owner_info | ||||
|   when: powerdns_tsig_key_path_owner is defined | ||||
|  | ||||
| - name: Check if TSIG key is already present | ||||
|   ansible.builtin.stat: | ||||
|     path: "{{ powerdns_tsig_key_path }}" | ||||
|   register: powerdns_tsig_key_info | ||||
|  | ||||
| - name: Ensure TSIG key directory is present | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ powerdns_tsig_key_path | dirname }}" | ||||
|     state: directory | ||||
|     owner: "{{ powerdns_tsig_key_path_owner | default(omit) }}" | ||||
|     group: "{{ powerdns_tsig_key_path_group | default(omit) }}" | ||||
|     mode: "u+rwX,g+rX" | ||||
|     recurse: true | ||||
|  | ||||
| - name: Ensure a TSIG key is configured and persisted | ||||
|   when: >- | ||||
|     not powerdns_tsig_key_info.stat.exists | ||||
|     or powerdns_tsig_key_info.stat.size == 0 | ||||
|   block: | ||||
|     - name: Ensure TSIG key is not already present | ||||
|       community.docker.docker_container_exec: | ||||
|         container: "{{ powerdns_tsig_key_container_name }}" | ||||
|         command: "pdnsutil list-tsig-keys" | ||||
|       delegate_to: "{{ powerdns_tsig_key_hostname }}" | ||||
|       register: powerdns_tsig_key_powerdns_info | ||||
|       changed_when: false | ||||
|       check_mode: false | ||||
|       become: true | ||||
|  | ||||
|     - name: Ensure TSIG key is generated in powerdns | ||||
|       community.docker.docker_container_exec: | ||||
|         container: "{{ powerdns_tsig_key_container_name }}" | ||||
|         command: "pdnsutil generate-tsig-key '{{ powerdns_tsig_key_name }}' '{{ powerdns_tsig_key_algo }}'" | ||||
|       when: >- | ||||
|         (powerdns_tsig_key_name ~ '. ' ~ powerdns_tsig_key_algo ~ '. ') | ||||
|         not in powerdns_tsig_key_powerdns_info.stdout | ||||
|       delegate_to: "{{ powerdns_tsig_key_hostname }}" | ||||
|       register: powerdns_tsig_key_powerdns_generated_tsig_key | ||||
|       throttle: 1 | ||||
|       become: true | ||||
|  | ||||
|     - name: Ensure PowerDNS is restarted | ||||
|       community.docker.docker_container: | ||||
|         name: "{{ powerdns_tsig_key_container_name }}" | ||||
|         state: started | ||||
|         restart: true | ||||
|       when: >- | ||||
|         (powerdns_tsig_key_name ~ '. ' ~ powerdns_tsig_key_algo ~ '. ') | ||||
|         not in powerdns_tsig_key_powerdns_info.stdout | ||||
|       delegate_to: "{{ powerdns_tsig_key_hostname }}" | ||||
|       throttle: 1 | ||||
|       become: true | ||||
|  | ||||
|     - name: Extract TSIG key into variable | ||||
|       ansible.builtin.set_fact: | ||||
|         powerdns_tsig_key_key: >- | ||||
|           {{ | ||||
|             (powerdns_tsig_key_powerdns_generated_tsig_key.stdout | trim | split(' ') | list | last) | ||||
|             if (powerdns_tsig_key_name ~ '. ' ~ powerdns_tsig_key_algo ~ '. ') | ||||
|             not in powerdns_tsig_key_powerdns_info.stdout | ||||
|             else (powerdns_generated_tsig_key | trim | split(' ') | list | last) | ||||
|           }} | ||||
|       vars: | ||||
|         powerdns_generated_tsig_key: >- | ||||
|           {% for line in powerdns_tsig_key_powerdns_info.stdout_lines %} | ||||
|           {% if powerdns_tsig_key_name in line %} | ||||
|           {{ line }} | ||||
|           {% endif %} | ||||
|           {% endfor %} | ||||
|  | ||||
|     - name: Ensure TSIG key is persisted into {{ powerdns_tsig_key_path }} | ||||
|       ansible.builtin.copy: | ||||
|         content: "{{ powerdns_tsig_key_key }}" | ||||
|         dest: "{{ powerdns_tsig_key_path }}" | ||||
|         owner: "{{ powerdns_tsig_key_path_owner | default(omit) }}" | ||||
|         group: "{{ powerdns_tsig_key_path_group | default(omit) }}" | ||||
|         mode: "0640" | ||||
|  | ||||
| - name: Ensure TSIG key permissions on {{ powerdns_tsig_key_path }} are correct | ||||
|   ansible.builtin.file: | ||||
|     path: "{{ powerdns_tsig_key_path }}" | ||||
|     owner: "{{ powerdns_tsig_key_path_owner | default(omit) }}" | ||||
|     group: "{{ powerdns_tsig_key_path_group | default(omit) }}" | ||||
|     mode: "u+rwX,g+rwX" | ||||
| @@ -10,18 +10,41 @@ restic_backup_stdin_command: ~ | ||||
| restic_backup_stdin_command_filename: ~ | ||||
|  | ||||
| restic_policy_keep_all_within: 1d | ||||
| restic_policy_keep_hourly: 6 | ||||
| restic_policy_keep_daily: 2 | ||||
| restic_policy_keep_weekly: 7 | ||||
| restic_policy_keep_monthly: 4 | ||||
| restic_policy_keep_hourly: 12 | ||||
| restic_policy_keep_daily: 7 | ||||
| restic_policy_keep_weekly: 6 | ||||
| restic_policy_keep_monthly: 6 | ||||
| restic_policy_keep_yearly: 5 | ||||
| restic_policy_backup_frequency: hourly | ||||
|  | ||||
| restic_base_environment: | ||||
|   RESTIC_JOBNAME: "{{ restic_job_name | default('unknown') }}" | ||||
|   RESTIC_FORGET_KEEP_WITHIN: "{{ restic_policy_keep_all_within }}" | ||||
|   RESTIC_FORGET_KEEP_HOURLY: "{{ restic_policy_keep_hourly }}" | ||||
|   RESTIC_FORGET_KEEP_DAILY: "{{ restic_policy_keep_daily }}" | ||||
|   RESTIC_FORGET_KEEP_WEEKLY: "{{ restic_policy_keep_weekly }}" | ||||
|   RESTIC_FORGET_KEEP_MONTHLY: "{{ restic_policy_keep_monthly }}" | ||||
|   RESTIC_FORGET_KEEP_YEARLY: "{{ restic_policy_keep_yearly }}" | ||||
|  | ||||
| restic_s3_environment: | ||||
|   AWS_ACCESS_KEY_ID: "{{ restic_s3_key_id }}" | ||||
|   AWS_SECRET_ACCESS_KEY: "{{ restic_s3_access_key }}" | ||||
|  | ||||
| restic_complete_environment: >- | ||||
|   {{ | ||||
|     restic_base_environment | ||||
|     | combine((restic_s3_environment | ||||
|       if (restic_s3_key_id and restic_s3_access_key) else {}) | default({})) | ||||
|     | combine(restic_environment | default({})) | ||||
|   }} | ||||
|  | ||||
| restic_policy: | ||||
|   keep_within: "{{ restic_policy_keep_all_within }}" | ||||
|   hourly: "{{ restic_policy_keep_hourly }}" | ||||
|   daily: "{{ restic_policy_keep_daily }}" | ||||
|   weekly: "{{ restic_policy_keep_weekly }}" | ||||
|   monthly: "{{ restic_policy_keep_monthly }}" | ||||
|   yearly: "{{ restic_policy_keep_yearly }}" | ||||
|   frequency: "{{ restic_policy_backup_frequency }}" | ||||
|  | ||||
| restic_user: root | ||||
|   | ||||
							
								
								
									
										37
									
								
								roles/restic/files/restic-backup-directories.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								roles/restic/files/restic-backup-directories.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| #!/usr/bin/env bash | ||||
|  | ||||
| set -euo pipefail | ||||
|  | ||||
| if [[ -n ${RESTIC_PRE_BACKUP_HOOK-} ]]; then | ||||
|     /bin/bash -c "$RESTIC_PRE_BACKUP_HOOK" | ||||
| fi | ||||
|  | ||||
| echo "List existing snapshots or attempt to initialize/unlock repository" | ||||
| restic snapshots || restic init || restic unlock | ||||
| sleep 1; | ||||
|  | ||||
| echo "Attempting to remove lock if present" | ||||
| restic unlock | ||||
| sleep 2 | ||||
|  | ||||
| echo "Start backup on ${@:1}" | ||||
| restic --verbose --retry-lock=${RESTIC_RETRY_LOCK:-5m} backup "${@:1}" | ||||
| sleep 2 | ||||
|  | ||||
| echo "Forget and prune old snapshots" | ||||
| restic forget --prune --retry-lock=${RESTIC_RETRY_LOCK:-5m} \ | ||||
|     --keep-within=${RESTIC_FORGET_KEEP_WITHIN:-1d} \ | ||||
|     --keep-hourly=${RESTIC_FORGET_KEEP_HOURLY:-6} \ | ||||
|     --keep-daily=${RESTIC_FORGET_KEEP_DAILY:-2} \ | ||||
|     --keep-weekly=${RESTIC_FORGET_KEEP_WEEKLY:-7} \ | ||||
|     --keep-monthly=${RESTIC_FORGET_KEEP_MONTHLY:-4} \ | ||||
|     --verbose | ||||
| sleep 2 | ||||
|  | ||||
| echo "Generate snapshot metrics" | ||||
| restic --json snapshots | /opt/restic-generate-snapshot-metrics.sh \ | ||||
|     > /var/lib/node_exporter/restic-snapshots-${RESTIC_JOBNAME:-unknown}.prom-src | ||||
| sleep 2 | ||||
|  | ||||
| echo "Check repository" | ||||
| restic check | ||||
							
								
								
									
										12
									
								
								roles/restic/files/restic-generate-snapshot-metrics.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								roles/restic/files/restic-generate-snapshot-metrics.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #!/usr/bin/env bash | ||||
|  | ||||
| RESTIC_JSON=$(</dev/stdin) | ||||
|  | ||||
| echo $RESTIC_JSON | jq -r '.[] | ||||
|     | { | ||||
|         "hostname": .hostname, | ||||
|         "username": .username, | ||||
| 	"short_id": .short_id, | ||||
|         "time": ((((.time | split(".")[0]) + "Z") | fromdate) - (3600 * (.time | split("+")[1] | split(":")[0] | tonumber + 1))), | ||||
|         "paths": .paths[] | ||||
|     } | "restic_snapshots{hostname=\"\(.hostname)\",username=\"\(.username)\",short_id=\"\(.short_id)\",paths=\"\(.paths)\"} \(.time)"' | ||||
| @@ -8,7 +8,7 @@ | ||||
|   when: restic_create_user | ||||
|  | ||||
| - name: Ensure either backup_paths or backup_stdin_command is populated | ||||
|   when: restic_backup_paths|length > 0 and restic_backup_stdin_command | ||||
|   when: restic_backup_paths|length > 0 and restic_backup_stdin_command and false | ||||
|   fail: | ||||
|     msg: "Setting both `restic_backup_paths` and `restic_backup_stdin_command` is not supported" | ||||
|  | ||||
|   | ||||
| @@ -2,27 +2,50 @@ | ||||
| Description={{ restic_job_description }} | ||||
|  | ||||
| [Service] | ||||
| Type=oneshot | ||||
| Type=simple | ||||
| User={{ restic_user }} | ||||
| WorkingDirectory={{ restic_systemd_working_directory }} | ||||
| SyslogIdentifier={{ restic_systemd_syslog_identifier }} | ||||
|  | ||||
| Environment=RESTIC_REPOSITORY={{ restic_repo_url }} | ||||
| Environment=RESTIC_PASSWORD={{ restic_repo_password }} | ||||
| {% if restic_s3_key_id and restic_s3_access_key %} | ||||
| Environment=AWS_ACCESS_KEY_ID={{ restic_s3_key_id }} | ||||
| Environment=AWS_SECRET_ACCESS_KEY={{ restic_s3_access_key }} | ||||
| {% endif %} | ||||
| {% for kv in restic_complete_environment | dict2items %} | ||||
| Environment={{ kv.key }}={{ kv.value }} | ||||
| {% endfor %} | ||||
|  | ||||
| {% if restic_init | default(true) %} | ||||
| ExecStartPre=-/bin/sh -c '/usr/bin/restic snapshots || /usr/bin/restic init' | ||||
| {% if restic_backup_stdin_command %} | ||||
| ExecStart=/bin/sh -c '{{ restic_backup_stdin_command }} | /usr/bin/restic backup --verbose --stdin --stdin-filename {{ restic_backup_stdin_command_filename }}' | ||||
| {% else %} | ||||
| ExecStart=/usr/bin/restic --verbose backup {{ restic_backup_paths | join(' ') }} | ||||
| {% endif %} | ||||
| ExecStartPost=/usr/bin/restic forget --prune --keep-within={{ restic_policy.keep_within }} --keep-hourly={{ restic_policy.hourly }} --keep-daily={{ restic_policy.daily }} --keep-weekly={{ restic_policy.weekly }} --keep-monthly={{ restic_policy.monthly }} | ||||
| ExecStartPost=-/usr/bin/restic snapshots | ||||
| ExecStartPost=/usr/bin/restic check | ||||
|  | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
| {% if restic_unlock_before_backup | default(false) %} | ||||
| ExecStartPre=-/bin/sh -c 'sleep 3 && /usr/bin/restic unlock' | ||||
| {% endif %} | ||||
| {% if restic_backup_pre_hook | default(false) %} | ||||
| ExecStartPre=-{{ restic_backup_pre_hook }} | ||||
| {% endif %} | ||||
| {% if restic_backup_stdin_command %} | ||||
| ExecStart=/bin/sh -c '{{ restic_backup_stdin_command }} | /usr/bin/restic backup \ | ||||
| 	--retry-lock {{ restic_retry_lock | default('5m') }} \ | ||||
| 	--verbose --stdin \ | ||||
| 	--stdin-filename {{ restic_backup_stdin_command_filename }}' | ||||
| {% else %} | ||||
| ExecStart=/opt/restic-backup-directories.sh {{ restic_backup_paths | join(' ') }} | ||||
| {% endif %} | ||||
| {% if restic_forget_prune | default(true) %} | ||||
| ExecStartPost=/usr/bin/restic forget --prune \ | ||||
| 	--retry-lock {{ restic_retry_lock | default('5m') }} \ | ||||
| 	--keep-within={{ restic_policy.keep_within }} \ | ||||
| 	--keep-hourly={{ restic_policy.hourly }} \ | ||||
| 	--keep-daily={{ restic_policy.daily }} \ | ||||
| 	--keep-weekly={{ restic_policy.weekly }} \ | ||||
| 	--keep-monthly={{ restic_policy.monthly }} \ | ||||
| 	--keep-yearly={{ restic_policy.yearly }} | ||||
| {% endif %} | ||||
| {% if restic_list_snapshots | default(true) %} | ||||
| ExecStartPost=-/usr/bin/restic snapshots --retry-lock {{ restic_retry_lock | default('5m') }} | ||||
| {% endif %} | ||||
| {% if restic_backup_post_hook | default(false) %} | ||||
| ExecStartPost=-{{ restic_backup_post_hook }} | ||||
| {% endif %} | ||||
| {% if restic_check | default(true) %} | ||||
| ExecStartPost=/usr/bin/restic check --retry-lock {{ restic_retry_lock | default('5m') }} | ||||
| {% endif %} | ||||
|   | ||||
| @@ -1,9 +1,8 @@ | ||||
| [Unit] | ||||
| Description=Run {{ restic_job_name }} | ||||
| Description=Run {{ restic_timer_description | default(restic_job_name) }} | ||||
|  | ||||
| [Timer] | ||||
| OnCalendar={{ restic_policy.frequency }} | ||||
| Persistent=True | ||||
| Unit={{ restic_systemd_unit_naming_scheme }}.service | ||||
|  | ||||
| [Install] | ||||
|   | ||||
							
								
								
									
										23
									
								
								roles/user/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								roles/user/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| # `finallycoffee.base.user` ansible role | ||||
|  | ||||
| Provision and manage user accounts on the remote host. Supports setting user | ||||
| home, gecos (display name) and shell. | ||||
|  | ||||
| Warning: if the users' home exists and is changed, the role will attempt to | ||||
| move the home directory. Set `move_home` to false on the user to disable this | ||||
| behaviour. | ||||
|  | ||||
| ## Examples | ||||
| ```yaml | ||||
| - hosts: all | ||||
|   roles: | ||||
|     - role: finallycoffee.base.user | ||||
|   vars: | ||||
|     users: | ||||
|       - name: root | ||||
|       - name: alice | ||||
|       - name: bob | ||||
|         state: present | ||||
|       - name: eve | ||||
|         state: absent | ||||
| ``` | ||||
							
								
								
									
										2
									
								
								roles/user/defaults/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								roles/user/defaults/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| --- | ||||
| users: [] | ||||
							
								
								
									
										41
									
								
								roles/user/tasks/configure-user.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								roles/user/tasks/configure-user.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| --- | ||||
| - name: Ensure user '{{ user.name }}' is {{ user_state }} | ||||
|   ansible.builtin.user: | ||||
|     name: "{{ user.name }}" | ||||
|     state: "{{ user_state }}" | ||||
|     system: "{{ user.system | default(false, true) }}" | ||||
|     shell: "{{ user.shell | default(omit, true) }}" | ||||
|     home: "{{ user.home | default(omit, true) }}" | ||||
|     create_home: "{{ user.create_home | default(true, true) }}" | ||||
|     move_home: "{{ user.move_home | default(true, true) }}" | ||||
|     skeleton: >-2 | ||||
|       {{ (user.create_home | default(true, true) and 'skeleton' in user) | ||||
|       | ternary(user.skeleton | default(''), omit) }} | ||||
|     comment: "{{ user.comment | default(user.gecos | default(omit, true), true) }}" | ||||
|   vars: | ||||
|     user_state: "{{ user.state | default('present', false) }}" | ||||
|  | ||||
| - name: Ensure SSH authorized keys for '{{ user.name }}' are {{ user_state }} | ||||
|   vars: | ||||
|     user_state: "{{ user.state | default('present', false) }}" | ||||
|   when: | ||||
|     - user_state == 'present' | ||||
|     - user.authorized_keys | default([]) | length > 0 | ||||
|   block: | ||||
|     - name: Ensure .ssh directory for user '{{ user.name }}' exists | ||||
|       ansible.builtin.file: | ||||
|         path: "{{ user.home | default('/home/' + user.name) + '/.ssh' }}" | ||||
|         state: "directory" | ||||
|         owner: "{{ user.name }}" | ||||
|         group: "{{ user.name }}" | ||||
|         mode: "0700" | ||||
|     - name: Ensure key is up to date | ||||
|       ansible.posix.authorized_key: | ||||
|         user: "{{ user.name }}" | ||||
|         state: "{{ key.state | default('present', true) }}" | ||||
|         key: "{{ key.type }} {{ key.key }}" | ||||
|         comment: "{{ user.name }}-{{ key.comment }}" | ||||
|       loop: "{{ user.authorized_keys }}" | ||||
|       loop_control: | ||||
|         loop_var: key | ||||
|         label: "{{ user.name }}-{{ key.comment }}" | ||||
							
								
								
									
										8
									
								
								roles/user/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								roles/user/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| --- | ||||
| - name: Ensure users are configured | ||||
|   ansible.builtin.include_tasks: | ||||
|     file: "configure-user.yml" | ||||
|   loop: "{{ users }}" | ||||
|   loop_control: | ||||
|     loop_var: user | ||||
|     label: "{{ user.name }}" | ||||
		Reference in New Issue
	
	Block a user