Ansible: Update

This commit is contained in:
Christer Waren
2026-09-14 14:24:46 +03:00
parent a5245f1c7f
commit 5b2cb89de5
20 changed files with 13 additions and 13 deletions
+16
View File
@@ -0,0 +1,16 @@
[defaults]
inventory = inventories/matte
hash_behaviour = merge
gathering = smart
transport = local
display_skipped_hosts = false
interpreter_python = auto_silent
localhost_warning = false
collections_path = collections:~/.ansible/collections
inject_facts_as_vars = false
force_handlers = true
action_warnings = false
inventory_unparsed_warning = false
[inventory]
host_pattern_mismatch = ignore
@@ -0,0 +1,6 @@
$ANSIBLE_VAULT;1.2;AES256;matte
32633665346332373139643132343562643638376130636264343537303564366137646531623065
3864666233643335613436363933316132336261663932360a306266616465383335316535393438
32303836343431396162613631666338343134313966316461383461323638613438333361663537
6233626238383034350a633663336539366434373438306639346330643062633631653762366432
3638
@@ -0,0 +1,6 @@
$ANSIBLE_VAULT;1.2;AES256;matte
30316461346436346435646231303134356437653433343730343262353261666562376539663462
6562333566646234306231343661626231633730313262390a666533303464383832306538333937
64303938393364343462653463653263306463303664353965623332386362646132393564623238
3362353766333538360a306539303331646634383539653861373362303135663963626663306430
6164
+9
View File
@@ -0,0 +1,9 @@
---
matte:
children:
arcadiamc:
hosts:
rainbow.devices.waren.io:
vars:
ansible_user: wxl62975
ansible_ssh_common_args: "-o StrictHostKeyChecking=accept-new -o LogLevel=error"
+4
View File
@@ -0,0 +1,4 @@
---
collections:
- community.general
- containers.podman
+31
View File
@@ -0,0 +1,31 @@
---
- name: "Tasks"
hosts: all
module_defaults:
ansible.builtin.gather_facts:
gather_timeout: 10
tasks:
- name: "Installer"
import_tasks: tasks/installer.yml
vars:
ansible_python_interpreter: "{{ ansible_facts.user_dir }}/.venv/ansible/bin/python3"
tags:
- installer
- never
- name: "Maintenance"
import_tasks: tasks/maintenance.yml
vars:
ansible_python_interpreter: "{{ ansible_facts.user_dir }}/.venv/ansible/bin/python3"
tags:
- maintenance
- never
- name: "Deployer"
import_tasks: tasks/deployer.yml
vars:
ansible_python_interpreter: "{{ ansible_facts.user_dir }}/.venv/ansible/bin/python3"
tags:
- deployer
- never
+89
View File
@@ -0,0 +1,89 @@
---
- name: "Deployer - Certbot - Renew Certificates"
containers.podman.podman_container:
name: certbot
image: "docker.io/certbot/certbot:latest"
state: started
network: host
volumes:
- "{{ ansible_facts.user_dir }}/data/certbot:/etc/letsencrypt"
command: "renew"
detach: false
register: task
changed_when:
- task.stdout.find("No renewals were attempted.") == -1
tags:
- certbot
- tls
- name: "Deployer - Certbot - Copy Certificates"
ansible.builtin.copy:
src: "~/data/certbot/live/{{ cert }}/"
dest: "~/data/certificates/{{ cert }}/"
follow: true
loop: "{{ certs }}"
loop_control:
label: "{{ cert }}"
loop_var: "cert"
vars:
certs:
- arcadiamc
register: task
tags:
- certbot
- tls
# - name: "Deployer - Minecraft - Build Image"
# containers.podman.podman_image:
# name: arcadiamc/openjdk
# tag: latest
# path: "{{ ansible_facts.user_dir }}/data/minecraft"
# build:
# file: Dockerfile
# format: docker
# cache: off
# force: on
# tags:
# - minecraft
# - name: "Deployer - Minecraft - Create Container"
# containers.podman.podman_container:
# name: minecraft
# image: "arcadiamc/openjdk:latest"
# state: started
# recreate: on
# network: host
# volumes:
# - "{{ ansible_facts.user_dir }}/data/minecraft:/usr/src/app"
# workdir: /usr/src/app
# command: "java -Xms1G -Xmx8G -jar paper-1.21.4-232.jar"
# restart_policy: unless-stopped
# tags:
# - minecraft
# - name: "Deployer - SSH - Build Image"
# containers.podman.podman_image:
# name: matte/ssh
# tag: latest
# path: "{{ ansible_facts.user_dir }}/data/ssh"
# build:
# file: Dockerfile
# format: docker
# cache: off
# force: on
# tags:
# - ssh
# - name: "Deployer - SSH - Create Container"
# containers.podman.podman_container:
# name: ssh
# image: "matte/ssh:latest"
# state: started
# recreate: on
# network: host
# volumes:
# - "{{ ansible_facts.user_dir }}/data:/root/data"
# - "{{ ansible_facts.user_dir }}/data/ssh/keys:/etc/ssh/keys"
# restart_policy: unless-stopped
# tags:
# - ssh
+130
View File
@@ -0,0 +1,130 @@
---
- name: "Installer - Ansible - Python Library"
ansible.builtin.pip:
name: ansible
state: latest
extra_args: --upgrade
virtualenv: ~/.venv/ansible
virtualenv_command: "python3 -m venv"
tags:
- ansible
- name: "Installer : Ansible : Create Folder"
ansible.builtin.file:
path: ~/bin
state: directory
tags:
- ansible
- name: "Installer : Ansible : Create Symbolic Links"
ansible.builtin.file:
src: ~/.venv/ansible/bin/{{ binary }}
dest: ~/bin/{{ binary }}
state: link
vars:
binaries:
- ansible
- ansible-community
- ansible-config
- ansible-console
- ansible-doc
- ansible-galaxy
- ansible-inventory
- ansible-playbook
- ansible-pull
- ansible-test
- ansible-vault
loop: "{{ binaries }}"
loop_control:
label: "{{ binary }}"
loop_var: "binary"
tags:
- ansible
- name: "Installer - Ansible - Dependencies / Python Libraries"
ansible.builtin.pip:
name: "{{ library }}"
state: latest
extra_args: --upgrade
virtualenv: ~/.venv/ansible
virtualenv_command: "python3 -m venv"
vars:
libraries:
- cryptography
- dnspython
- hvac
- jmespath
- netaddr
- pexpect
loop: "{{ libraries }}"
loop_control:
label: "{{ library }}"
loop_var: "library"
- name: "Installer : Certbot : Auth Hook - Create Folder"
ansible.builtin.file:
path: ~/data/certbot/auth-hooks
state: directory
tags:
- certbot
- name: "Installer : Certbot : Auth Hook - Copy File"
ansible.builtin.copy:
src: "./templates/certbot/auth-hooks/acme-dns.py"
dest: "~/data/certbot/auth-hooks/acme-dns.py"
mode: '700'
force: true
tags:
- certbot
- name: "Installer : Certbot : Create Certificates"
containers.podman.podman_container:
name: certbot
image: "docker.io/certbot/certbot:latest"
state: started
network: host
volumes:
- "{{ ansible_facts.user_dir }}/data/certbot:/etc/letsencrypt"
command: "certonly --cert-name {{ cert.name }} --manual --preferred-challenges dns-01 --email {{ cert.email }} --server https://acme-v02.api.letsencrypt.org/directory --agree-tos -n --manual-auth-hook /etc/letsencrypt/auth-hooks/acme-dns.py --debug-challenges --key-type rsa -d {{ cert.domains | join(' -d ') }}"
detach: false
register: task
changed_when:
- task.stdout.find("Certificate not yet due for renewal; no action taken.") == -1
loop: "{{ certs }}"
loop_control:
label: "{{ cert.name }}"
loop_var: "cert"
vars:
certs:
- name: arcadiamc
email: mattez02.contact@gmail.com
domains:
- arcadiamc.wgi.fi
tags:
- certbot
- name: "Installer : Schedule : Maintenance"
ansible.builtin.cron:
name: "Matte - Infra - Maintenance"
hour: "*/3"
minute: "0"
job: "~/.venv/ansible/bin/ansible-pull -U ssh://git@github.com/MatteZ02/infra -d ~/.ansible/pull/matte/infra --accept-host-key --private-key ~/.ssh/keys/matte/infra --vault-password-file ~/.ansible/vault/matte.yml tasks.yml -t maintenance"
tags:
- cron
- name: "Installer : Schedule : Deployer"
ansible.builtin.cron:
name: "Matte - Infra - Deployer"
minute: "*/5"
job: "~/.venv/ansible/bin/ansible-pull -U ssh://git@github.com/MatteZ02/infra -d ~/.ansible/pull/matte/infra --accept-host-key --private-key ~/.ssh/keys/matte/infra --vault-password-file ~/.ansible/vault/matte.yml tasks.yml -t deployer"
tags:
- cron
- name: "Installer : Schedule : Backup"
ansible.builtin.cron:
name: "Matte - Infra - Backup"
hour: "3"
minute: "30"
job: "~/.venv/ansible/bin/ansible-pull -U ssh://git@github.com/MatteZ02/infra -d ~/.ansible/pull/matte/infra --accept-host-key --private-key ~/.ssh/keys/matte/infra --vault-password-file ~/.ansible/vault/matte.yml tasks.yml -t backup"
tags:
- cron
+36
View File
@@ -0,0 +1,36 @@
---
- name: "Installer - Ansible - Dependencies / Python Libraries"
ansible.builtin.pip:
name: "{{ library }}"
state: latest
extra_args: --upgrade
virtualenv: ~/.venv/ansible
virtualenv_command: "python3 -m venv"
vars:
libraries:
- cryptography
- dnspython
- hvac
- jmespath
- netaddr
- pexpect
loop: "{{ libraries }}"
loop_control:
label: "{{ library }}"
loop_var: "library"
- name: "Maintenance : Ansible : Update"
ansible.builtin.pip:
name: ansible
state: latest
extra_args: --upgrade
virtualenv: ~/.venv/ansible
virtualenv_command: "python3 -m venv"
- name: "Maintenance : Podman : Prune"
containers.podman.podman_prune:
container: yes
image: yes
image_filters:
dangling_only: no
volume: yes
View File
@@ -0,0 +1,155 @@
#!/usr/bin/env python3
import json
import os
import requests
import sys
### EDIT THESE: Configuration values ###
# URL to acme-dns instance
ACMEDNS_URL = "https://acme-challenge.waren.io"
# Path for acme-dns credential storage
STORAGE_PATH = "/etc/letsencrypt/acmedns.json"
# Whitelist for address ranges to allow the updates from
# Example: ALLOW_FROM = ["192.168.10.0/24", "::1/128"]
ALLOW_FROM = []
# Force re-registration. Overwrites the already existing acme-dns accounts.
FORCE_REGISTER = False
### DO NOT EDIT BELOW THIS POINT ###
### HERE BE DRAGONS ###
DOMAIN = os.environ["CERTBOT_DOMAIN"]
if DOMAIN.startswith("*."):
DOMAIN = DOMAIN[2:]
VALIDATION_DOMAIN = "_acme-challenge."+DOMAIN
VALIDATION_TOKEN = os.environ["CERTBOT_VALIDATION"]
class AcmeDnsClient(object):
"""
Handles the communication with ACME-DNS API
"""
def __init__(self, acmedns_url):
self.acmedns_url = acmedns_url
def register_account(self, allowfrom):
"""Registers a new ACME-DNS account"""
if allowfrom:
# Include whitelisted networks to the registration call
reg_data = {"allowfrom": allowfrom}
res = requests.post(self.acmedns_url+"/register",
data=json.dumps(reg_data))
else:
res = requests.post(self.acmedns_url+"/register")
if res.status_code == 201:
# The request was successful
return res.json()
else:
# Encountered an error
msg = ("Encountered an error while trying to register a new acme-dns "
"account. HTTP status {}, Response body: {}")
print(msg.format(res.status_code, res.text))
sys.exit(1)
def update_txt_record(self, account, txt):
"""Updates the TXT challenge record to ACME-DNS subdomain."""
update = {"subdomain": account['subdomain'], "txt": txt}
headers = {"X-Api-User": account['username'],
"X-Api-Key": account['password'],
"Content-Type": "application/json"}
res = requests.post(self.acmedns_url+"/update",
headers=headers,
data=json.dumps(update))
if res.status_code == 200:
# Successful update
return
else:
msg = ("Encountered an error while trying to update TXT record in "
"acme-dns. \n"
"------- Request headers:\n{}\n"
"------- Request body:\n{}\n"
"------- Response HTTP status: {}\n"
"------- Response body: {}")
s_headers = json.dumps(headers, indent=2, sort_keys=True)
s_update = json.dumps(update, indent=2, sort_keys=True)
s_body = json.dumps(res.json(), indent=2, sort_keys=True)
print(msg.format(s_headers, s_update, res.status_code, s_body))
sys.exit(1)
class Storage(object):
def __init__(self, storagepath):
self.storagepath = storagepath
self._data = self.load()
def load(self):
"""Reads the storage content from the disk to a dict structure"""
data = dict()
filedata = ""
try:
with open(self.storagepath, 'r') as fh:
filedata = fh.read()
except IOError as e:
if os.path.isfile(self.storagepath):
# Only error out if file exists, but cannot be read
print("ERROR: Storage file exists but cannot be read")
sys.exit(1)
try:
data = json.loads(filedata)
except ValueError:
if len(filedata) > 0:
# Storage file is corrupted
print("ERROR: Storage JSON is corrupted")
sys.exit(1)
return data
def save(self):
"""Saves the storage content to disk"""
serialized = json.dumps(self._data)
try:
with os.fdopen(os.open(self.storagepath,
os.O_WRONLY | os.O_CREAT, 0o600), 'w') as fh:
fh.truncate()
fh.write(serialized)
except IOError as e:
print("ERROR: Could not write storage file.")
sys.exit(1)
def put(self, key, value):
"""Puts the configuration value to storage and sanitize it"""
# If wildcard domain, remove the wildcard part as this will use the
# same validation record name as the base domain
if key.startswith("*."):
key = key[2:]
self._data[key] = value
def fetch(self, key):
"""Gets configuration value from storage"""
try:
return self._data[key]
except KeyError:
return None
if __name__ == "__main__":
# Init
client = AcmeDnsClient(ACMEDNS_URL)
storage = Storage(STORAGE_PATH)
# Check if an account already exists in storage
account = storage.fetch(DOMAIN)
if FORCE_REGISTER or not account:
# Create and save the new account
account = client.register_account(ALLOW_FROM)
storage.put(DOMAIN, account)
storage.save()
# Display the notification for the user to update the main zone
msg = "Please add the following CNAME record to your main DNS zone:\n{}"
cname = "{} CNAME {}.".format(VALIDATION_DOMAIN, account["fulldomain"])
print(msg.format(cname))
# Update the TXT record in acme-dns instance
client.update_txt_record(account, VALIDATION_TOKEN)
@@ -0,0 +1 @@
*
+4
View File
@@ -0,0 +1,4 @@
FROM docker.io/library/openjdk:21
WORKDIR /usr/src/app
RUN microdnf install git
+1
View File
@@ -0,0 +1 @@
keys
+22
View File
@@ -0,0 +1,22 @@
FROM docker.io/library/debian:latest
RUN apt update && \
apt install -y openssh-server rsync git python3-pip python3-venv jq git curl lsb-release nano man
RUN apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN rm -rf /etc/ssh/ssh_host* && \
mkdir -p /run/sshd
COPY entrypoint.sh /
RUN chmod +x entrypoint.sh
COPY sshd_config /etc/ssh/sshd_config
RUN python3 -m venv /opt/ansible && \
/opt/ansible/bin/pip3 install ansible && \
/opt/ansible/bin/pip3 install cryptography dnspython hvac jmespath netaddr pexpect
CMD ./entrypoint.sh
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
if [[ ! -f /etc/ssh/keys/ssh_host_rsa_key ]]
then
ssh-keygen -b 4096 -f /etc/ssh/keys/ssh_host_rsa_key -t rsa -N ""
fi
if [[ ! -f /etc/ssh/keys/ssh_host_ed25519_key ]]
then
ssh-keygen -b 4096 -f /etc/ssh/keys/ssh_host_ed25519_key -t ed25519 -N ""
fi
if [[ ! -f /etc/ssh/keys/authorized_keys ]]
then
touch /etc/ssh/keys/authorized_keys
fi
cat /etc/ssh/keys/authorized_keys > ~/.ssh/authorized_keys
/usr/sbin/sshd -D
@@ -0,0 +1,2 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPW5phGhwAG8dmT+sR0uF1gRc0X9xXZiiFxvKUEsPk1N cwchristerw
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC38o1SJu8FRWwGHU9AejwgRRDDV/VEDAyBXvYlEXxyqqNsFWQ42ZYjCRBEprSbvD3sJT65NNdqH+Uv6iV2PBS8+TDQ2oQif+0Ta7hP6V0oOqpOO9/ZAGYFnVs3Mu42/Ya1Lqim1C82ylW63Cmw4GyctkY2+lIaSpP1CpLvFuVR9U0f+AXjpzuy4VXZVKRXs75YGbYkyOoIQ/NZa9ZRcMa19j7Mm2QWDyjlk2i9/GVC/8riJ4MwI1kwiUe+4LFKssghgTsRHjBm5bpgUgEPF+nnNGX0p6RArbaYxd/vLoGWHoO8k4UoElLQERlm8dnXF6yrJkltGBlFjS4o9XANYNEuP23JVROm2ucPmFfH4xVQCRPE/32fQjHRSZUK5W/qO3bNsxCjYhsB6GZWohtktGxnySZBq3I2ziNKr7CwMECUbeew3QxwOC1jmF88k8uR7yCLBEej8hJ2bCwzMYWSdjO/uvqPkQ0GsEj5LkLHc9cfJXHEXTa1Rh8bv2SVCPp5K98= matte
+24
View File
@@ -0,0 +1,24 @@
Port 25590
HostKey /etc/ssh/keys/ssh_host_rsa_key
HostKey /etc/ssh/keys/ssh_host_ed25519_key
SyslogFacility AUTHPRIV
LogLevel VERBOSE
PermitRootLogin prohibit-password
MaxAuthTries 2
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PermitEmptyPasswords no
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes
AllowAgentForwarding no
AllowTcpForwarding yes
X11Forwarding no
TCPKeepAlive yes
Compression no
ClientAliveCountMax 2
UseDNS no
PermitTunnel yes
PermitOpen localhost:27017
PrintMotd no
Subsystem sftp /usr/lib/openssh/sftp-server