Initial commit of whatever junk this is

This commit is contained in:
Aaron Johnson 2026-03-05 16:50:06 -06:00
commit 0ad709f59f
17 changed files with 611 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.DS_Store

33
group_vars/all.yml Normal file
View File

@ -0,0 +1,33 @@
---
################################################################################
# Created Date: Sunday December 21st 2025
# Author: Aaron Johnson aaron.johnson@akamai.com
# -----
# Last Modified: Tuesday, 24th February 2026
# Modified By: the engineer formerly known as Aaron Johnson
# -----
# Copyright (c) 2025 Contact Author for details
# -----
# USAGE/DOCUMENTATION:
#
# -----
# INSTALLATION
#
# -----
# HISTORY:
# Date By Comments
# ---------- --- ---------------------------------------------------------
################################################################################
acme_user: acme
acme_home: /var/lib/acme
acme_sh_path: /var/lib/acme/.acme.sh/acme.sh
apache_user: apache
# apache_group: apache
cert_base_dir: /etc/pki/tls
cert_dir: "{{ cert_base_dir }}/{{ inventory_hostname }}"
# Change if needed
apache_service: httpd

54
inventory/hosts.yml Normal file
View File

@ -0,0 +1,54 @@
---
################################################################################
# Created Date: Sunday December 21st 2025
# Author: Aaron Johnson aaron.johnson@akamai.com
# -----
# Last Modified: Tuesday, 24th February 2026
# Modified By: the engineer formerly known as Aaron Johnson
# -----
# Copyright (c) 2025 Contact Author for details
# -----
# USAGE/DOCUMENTATION:
#
# -----
# INSTALLATION
#
# -----
# HISTORY:
# Date By Comments
# ---------- --- ---------------------------------------------------------
################################################################################
all:
hosts:
keycloak.bart.bslnaj.net:
keycloak.lisa.bslnaj.net:
# keycloak.maggie.bslnaj.net:
# nextcloud.bart.bslnaj.net:
# nextcloud.lisa.bslnaj.net:
# nextcloud.maggie.bslnaj.net:
sharedplex.bart.bslnaj.net:
ourplex.bart.bslnaj.net:
plex.maggie.bslnaj.net:
acme_hosts:
hosts:
# keycloak.bart.bslnaj.net:
# keycloak.lisa.bslnaj.net:
# keycloak.maggie.bslnaj.net:
# nextcloud.bart.bslnaj.net:
# nextcloud.lisa.bslnaj.net:
# nextcloud.maggie.bslnaj.net:
sharedplex.bart.bslnaj.net:
ourplex.bart.bslnaj.net:
plex.maggie.bslnaj.net:
web_servers:
hosts:
keycloak.bart.bslnaj.net:
keycloak.lisa.bslnaj.net:
# keycloak.maggie.bslnaj.net:
# nextcloud.bart.bslnaj.net:
# nextcloud.lisa.bslnaj.net:
# nextcloud.maggie.bslnaj.net:

View File

@ -0,0 +1,49 @@
---
################################################################################
# Created Date: Sunday December 21st 2025
# Author: Aaron Johnson aaron.johnson@akamai.com
# -----
# Last Modified: Tuesday, 24th February 2026
# Modified By: the engineer formerly known as Aaron Johnson
# -----
# Copyright (c) 2025 Contact Author for details
# -----
# USAGE/DOCUMENTATION:
#
# -----
# INSTALLATION
#
# -----
# HISTORY:
# Date By Comments
# ---------- --- ---------------------------------------------------------
################################################################################
- name: Issue certificate
shell: |
. {{ acme_home }}/godaddy.env
export ACME_DEFAULT_DNS_TIMEOUT=180
export ACME_RETRY=10
export ACME_SLEEP=15
{{ acme_home }}/.acme.sh/acme.sh --server letsencrypt --issue --dns dns_gd -d {{ inventory_hostname }}
become_user: "{{ acme_user }}"
args:
creates: "{{ acme_home }}/.acme.sh/{{ inventory_hostname }}_ecc/fullchain.cer"
when: hostvars[inventory_hostname].gd_key is defined
- name: Install renewal service
template:
src: acme-renew.service.j2
dest: /etc/systemd/system/acme-renew.service
- name: Install renewal timer
template:
src: acme-renew.timer.j2
dest: /etc/systemd/system/acme-renew.timer
- name: Enable renewal timer
systemd:
name: acme-renew.timer
enabled: yes
state: started

47
roles/acme/tasks/acme.yml Normal file
View File

@ -0,0 +1,47 @@
---
################################################################################
# Created Date: Sunday December 21st 2025
# Author: Aaron Johnson aaron.johnson@akamai.com
# -----
# Last Modified: Tuesday, 24th February 2026
# Modified By: the engineer formerly known as Aaron Johnson
# -----
# Copyright (c) 2025 Contact Author for details
# -----
# USAGE/DOCUMENTATION:
#
# -----
# INSTALLATION
#
# -----
# HISTORY:
# Date By Comments
# ---------- --- ---------------------------------------------------------
################################################################################
- name: Install acme.sh as acme user
become_user: "{{ acme_user }}"
shell: |
curl https://get.acme.sh | sh
args:
creates: "{{ acme_home }}/.acme.sh/acme.sh"
- name: Check if GoDaddy env file exists on remote hosts
stat:
path: "{{ acme_home }}/godaddy.env"
register: godaddy_env_file
- name: Create GoDaddy env file
copy:
dest: "{{ acme_home }}/godaddy.env"
content: |
export GD_Key={{ hostvars[inventory_hostname].gd_key }}
export GD_Secret={{ hostvars[inventory_hostname].gd_secret }}
owner: "{{ acme_user }}"
group: "{{ acme_user }}"
mode: "0400"
when:
- hostvars[inventory_hostname].gd_key is defined
- gd_env_stat.stat.exists is not defined or not gd_env_stat.stat.exists

24
roles/acme/tasks/main.yml Normal file
View File

@ -0,0 +1,24 @@
---
################################################################################
# Created Date: Sunday December 21st 2025
# Author: Aaron Johnson aaron.johnson@akamai.com
# -----
# Last Modified: Tuesday, 24th February 2026
# Modified By: the engineer formerly known as Aaron Johnson
# -----
# Copyright (c) 2025 Contact Author for details
# -----
# USAGE/DOCUMENTATION:
#
# -----
# INSTALLATION
#
# -----
# HISTORY:
# Date By Comments
# ---------- --- ---------------------------------------------------------
################################################################################
- import_tasks: users.yml
- import_tasks: acme.yml
- import_tasks: systemd.yml

View File

@ -0,0 +1,59 @@
---
################################################################################
# Created Date: Sunday December 21st 2025
# Author: Aaron Johnson aaron.johnson@akamai.com
# -----
# Last Modified: Sunday, 21st December 2025
# Modified By: the engineer formerly known as Aaron Johnson
# -----
# Copyright (c) 2025 Contact Author for details
# -----
# USAGE/DOCUMENTATION:
#
# -----
# INSTALLATION
#
# -----
# HISTORY:
# Date By Comments
# ---------- --- ---------------------------------------------------------
################################################################################
- name: Issue certificate
shell: |
source {{ acme_home }}/godaddy.env
export ACME_DEFAULT_DNS_TIMEOUT=180
export ACME_RETRY=10
export ACME_SLEEP=15
{{ acme_sh_path }} --server letsencrypt --issue --dns dns_gd -d {{ inventory_hostname }}
become_user: "{{ acme_user }}"
args:
creates: "{{ acme_home }}/.acme.sh/{{ inventory_hostname }}_ecc/fullchain.cer"
- name: Install certificate
become_user: "{{ acme_user }}"
shell: |
{{ acme_sh_path }} --install-cert \
-d {{ inventory_hostname }} \
--key-file {{ cert_dir }}/privkey.pem \
--fullchain-file {{ cert_dir }}/fullchain.pem \
--reloadcmd "chmod 640 {{ cert_dir }}/privkey.pem {{ cert_dir }}/fullchain.pem && sudo /bin/systemctl reload {{ apache_service }}"
args:
creates: "{{ cert_dir }}/fullchain.pem"
- name: Install renewal service
template:
src: acme-renew.service.j2
dest: /etc/systemd/system/acme-renew.service
- name: Install renewal timer
template:
src: acme-renew.timer.j2
dest: /etc/systemd/system/acme-renew.timer
- name: Enable renewal timer
systemd:
name: acme-renew.timer
enabled: yes
state: started

View File

@ -0,0 +1,36 @@
---
################################################################################
# Created Date: Sunday December 21st 2025
# Author: Aaron Johnson aaron.johnson@akamai.com
# -----
# Last Modified: Tuesday, 24th February 2026
# Modified By: the engineer formerly known as Aaron Johnson
# -----
# Copyright (c) 2025 Contact Author for details
# -----
# USAGE/DOCUMENTATION:
#
# -----
# INSTALLATION
#
# -----
# HISTORY:
# Date By Comments
# ---------- --- ---------------------------------------------------------
################################################################################
- name: Create acme system user
user:
name: "{{ acme_user }}"
home: "{{ acme_home }}"
shell: /sbin/nologin
system: yes
create_home: yes
- name: Create certificate directory
file:
path: "{{ cert_dir }}"
state: directory
owner: "{{ acme_user }}"
group: "{{ apache_group | default(omit) }}"
mode: "2750"

View File

@ -0,0 +1,28 @@
/*
* Created Date: Sunday December 21st 2025
* Author: Aaron Johnson aaron.johnson@akamai.com
* -----
* Last Modified: Sunday, 21st December 2025
* Modified By: the engineer formerly known as Aaron Johnson
* -----
* Copyright (c) 2025 Contact Author for details
* -----
* USAGE/DOCUMENTATION:
*
* -----
* INSTALLATION
*
* -----
* HISTORY:
* Date By Comments
* ---------- --- ---------------------------------------------------------
*/
[Unit]
Description=ACME certificate renewal
[Service]
User={{ acme_user }}
Group={{ acme_user }}
EnvironmentFile={{ acme_home }}/godaddy.env
ExecStart={{ acme_sh_path }} --renew --dns dns_gd

View File

@ -0,0 +1,30 @@
/*
* Created Date: Sunday December 21st 2025
* Author: Aaron Johnson aaron.johnson@akamai.com
* -----
* Last Modified: Sunday, 21st December 2025
* Modified By: the engineer formerly known as Aaron Johnson
* -----
* Copyright (c) 2025 Contact Author for details
* -----
* USAGE/DOCUMENTATION:
*
* -----
* INSTALLATION
*
* -----
* HISTORY:
* Date By Comments
* ---------- --- ---------------------------------------------------------
*/
[Unit]
Description=Daily ACME renewal check
[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=timers.target

View File

@ -0,0 +1,2 @@
Defaults:acme secure_path=/bin:/usr/bin
acme ALL=(root) NOPASSWD: /bin/systemctl reload httpd

View File

@ -0,0 +1,42 @@
---
################################################################################
# Created Date: Sunday December 21st 2025
# Author: Aaron Johnson aaron.johnson@akamai.com
# -----
# Last Modified: Sunday, 21st December 2025
# Modified By: the engineer formerly known as Aaron Johnson
# -----
# Copyright (c) 2025 Contact Author for details
# -----
# USAGE/DOCUMENTATION:
#
# -----
# INSTALLATION
#
# -----
# HISTORY:
# Date By Comments
# ---------- --- ---------------------------------------------------------
################################################################################
- name: Allow acme user to reload apache without password
copy:
dest: /etc/sudoers.d/acme-apache
content: |
{{ acme_user }} ALL=NOPASSWD: /bin/systemctl reload {{ apache_service }}
mode: "0440"
owner: root
group: root
- name: Allow Apache to read cert directory (SELinux)
command: semanage fcontext -a -t httpd_config_t "{{ cert_dir }}(/.*)?"
args:
creates: "/etc/selinux/targeted/contexts/files/file_contexts.local"
# - name: Apply SELinux labels
# command: restorecon -Rv "{{ cert_dir }}"
- name: Apply SELinux labels only if incorrect
command: restorecon -Rv "{{ cert_dir }}"
register: restorecon_result
changed_when: restorecon_result.stdout_lines | select("search", "Relabeled") | list | length > 0

View File

@ -0,0 +1,32 @@
---
################################################################################
# Created Date: Sunday December 21st 2025
# Author: Aaron Johnson aaron.johnson@akamai.com
# -----
# Last Modified: Tuesday, 24th February 2026
# Modified By: the engineer formerly known as Aaron Johnson
# -----
# Copyright (c) 2025 Contact Author for details
# -----
# USAGE/DOCUMENTATION:
#
# -----
# INSTALLATION
#
# -----
# HISTORY:
# Date By Comments
# ---------- --- ---------------------------------------------------------
################################################################################
- name: Install certificate
become_user: "{{ acme_user }}"
shell: |
{{ acme_sh_path }} --install-cert \
-d {{ inventory_hostname }} \
--key-file {{ cert_dir }}/privkey.pem \
--fullchain-file {{ cert_dir }}/fullchain.pem \
--reloadcmd "chmod 640 {{ cert_dir }}/privkey.pem {{ cert_dir }}/fullchain.pem && sudo /bin/systemctl reload {{ apache_service }}"
args:
creates: "{{ cert_dir }}/fullchain.pem"

View File

@ -0,0 +1,23 @@
---
################################################################################
# Created Date: Sunday December 21st 2025
# Author: Aaron Johnson aaron.johnson@akamai.com
# -----
# Last Modified: Tuesday, 24th February 2026
# Modified By: the engineer formerly known as Aaron Johnson
# -----
# Copyright (c) 2025 Contact Author for details
# -----
# USAGE/DOCUMENTATION:
#
# -----
# INSTALLATION
#
# -----
# HISTORY:
# Date By Comments
# ---------- --- ---------------------------------------------------------
################################################################################
- import_tasks: apache.yml
- import_tasks: installCert.yml

View File

@ -0,0 +1,39 @@
---
################################################################################
# Created Date: Sunday December 21st 2025
# Author: Aaron Johnson aaron.johnson@akamai.com
# -----
# Last Modified: Tuesday, 24th February 2026
# Modified By: the engineer formerly known as Aaron Johnson
# -----
# Copyright (c) 2025 Contact Author for details
# -----
# USAGE/DOCUMENTATION:
#
# -----
# INSTALLATION
#
# -----
# HISTORY:
# Date By Comments
# ---------- --- ---------------------------------------------------------
################################################################################
- name: Install certificate and generate PKCS#12 for Plex
become_user: "{{ acme_user }}"
shell: |
{{ acme_sh_path }} --install-cert \
-d {{ inventory_hostname }} \
--key-file {{ cert_dir }}/privkey.pem \
--fullchain-file {{ cert_dir }}/fullchain.pem \
--reloadcmd "
chmod 640 {{ cert_dir }}/privkey.pem {{ cert_dir }}/fullchain.pem &&
openssl pkcs12 -export \
-inkey {{ cert_dir }}/privkey.pem \
-in {{ cert_dir }}/fullchain.pem \
-out {{ cert_dir }}/{{ inventory_hostname }}.p12 \
-password pass:{{ pkcs12_password | default('ChangeMe123!') }} &&
sudo systemctl restart {{ plex_service | default('plex') }}
"
args:
creates: "{{ cert_dir }}/{{ inventory_hostname }}.p12"

22
roles/plex/tasks/main.yml Normal file
View File

@ -0,0 +1,22 @@
---
################################################################################
# Created Date: Sunday December 21st 2025
# Author: Aaron Johnson aaron.johnson@akamai.com
# -----
# Last Modified: Tuesday, 24th February 2026
# Modified By: the engineer formerly known as Aaron Johnson
# -----
# Copyright (c) 2025 Contact Author for details
# -----
# USAGE/DOCUMENTATION:
#
# -----
# INSTALLATION
#
# -----
# HISTORY:
# Date By Comments
# ---------- --- ---------------------------------------------------------
################################################################################
- import_tasks: installCert.yml

90
site.yml Normal file
View File

@ -0,0 +1,90 @@
---
# -----------------------------
# Play 1: Check remote env files
# -----------------------------
- name: Check if GoDaddy env file exists on remote hosts
hosts: acme_hosts
gather_facts: false
tasks:
- name: Stat godaddy.env
stat:
path: "{{ acme_home }}/godaddy.env"
register: gd_env_stat
tags: acme
# -----------------------------
# Play 2: Fetch secrets from 1Password (control node)
# Only runs if any remote host is missing the env file
# -----------------------------
- name: Fetch GoDaddy API credentials from 1Password if needed
hosts: acme_hosts
# connection: local
# delegate_to: localhost
run_once: true
gather_facts: false
vars:
op_account: my.1password.com
op_item_title: godaddy-api
tasks:
- name: Determine if fetch is needed
run_once: true
delegate_to: localhost
set_fact:
fetch_godaddy_creds: >-
{{
groups['acme_hosts']
| map('extract', hostvars)
| selectattr('gd_env_stat.stat.exists','equalto',false)
| list
| length > 0
}}
- name: Get GD_Key and GD_Secret from 1Password
run_once: true
delegate_to: localhost
command: op item get "{{ op_item_title }}" --account {{ op_account }} --fields username,credential --reveal
register: godaddy_creds_raw
changed_when: false
when: fetch_godaddy_creds
- name: Split GD_Key and GD_Secret
run_once: true
# delegate_to: localhost
set_fact:
gd_key: "{{ godaddy_creds_raw.stdout.split(',')[0] }}"
gd_secret: "{{ godaddy_creds_raw.stdout.split(',')[1] }}"
when: fetch_godaddy_creds
- name: Add GD_Key and GD_Secret to hostvars for remote hosts
run_once: true
# delegate_to: localhost
add_host:
name: "{{ item }}"
gd_key: "{{ gd_key }}"
gd_secret: "{{ gd_secret }}"
loop: "{{ groups['all'] }}"
when: fetch_godaddy_creds
tags: acme
# -----------------------------
# Play 2: Run role on remote hosts
# -----------------------------
- name: ACME + Apache TLS bootstrap
hosts: all
become: true
roles:
- apache
- name: ACME
hosts: all
become: true
roles:
- acme
tags: acme
- name: plex
hosts: all
become: true
roles:
- plex
tags: plex