Initial Commit

This commit is contained in:
2025-09-28 17:43:23 +10:00
commit 0d81d80e28
14 changed files with 375 additions and 0 deletions

39
.gitignore vendored Normal file
View File

@ -0,0 +1,39 @@
# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
crash.*.log
# Exclude override files as they are usually for local changes
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negation
# !example_override.tf
# Exclude Terraform variable files that may contain sensitive data
**/*.tfvars
**/*.tfvars.json
# Ignore CLI configuration files
.terraformrc
terraform.rc
# Ignore lock files (optional: keep if you want dependency locking in VCS)
.terraform.lock.hcl
# IDE and OS specific files
.DS_Store
Thumbs.db
*.swp
*.bak
*.tmp
*.idea/
.vscode/

21
LICENSE.md Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Matthew McKinnon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

26
README.md Normal file
View File

@ -0,0 +1,26 @@
![Header Image](https://miro.medium.com/v2/resize:fit:4000/1*16DgdobhWUUXKzF4fwjOdw.png)
## 📖 Overview
This repository provisions and manages the GitHub Self-Hosted Runner for the Comprofix Homelab Infrastructure. This repo is designed to be run manually to build and setup the runner when requuired.
Built using Infrastructure as Code (IaC) with [OpenTofu](https://opentofu.org/) and [Ansible](https://ansible.com)
---
## 🚀 Features
- Declarative infrastructure management with OpenTofu and Ansible
- Remote state stored in PostgreSQL backend
- Secure injection of secrets into `terraform.auto.tfvars`
---
## 🔄 Usage
1. Checkout repo
2. From the opentofu folder, generate `terraform.auto.tfvars` using the supplied ```prepareEnv.sh```
3. Run `tofu init`, `tofu fmt`, `tofu validate`
4. Execute `tofu plan`
5. If successful, run `tofu apply`
6. From the ansible folder, run `ansible-playbook main.yml`

2
ansible/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
roles/
.env

21
ansible/ansible.cfg Normal file
View File

@ -0,0 +1,21 @@
[defaults]
inventory = inventory/hosts.ini
deprecation_warnings = False
host_key_checking = False
interpreter_python = auto_silent
pipelining = True
display_args_to_stdout = True
remote_user = root
forks = 10
roles_path = ./roles:~/.ansible/roles:/usr/share/ansible/roles
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s

24
ansible/ghshr.yml Normal file
View File

@ -0,0 +1,24 @@
---
- name: Prepare all servers
hosts: all
become: true
pre_tasks:
- name: Setup base system
import_tasks: tasks/base.yml
tags: base_setup
tasks:
- name: Create ghshr folders
file:
path: "/opt/actions-runner"
state: directory
- name: Download ans extract the GitHub Actions Runner
ansible.builtin.unarchive:
src: https://github.com/actions/runner/releases/download/v2.328.0/actions-runner-linux-x64-2.328.0.tar.gz
dest: /opt/actions-runner
remote_src: yes

View File

@ -0,0 +1,14 @@
---
install_packages:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
- vim
- git
- htop
- jq
- mc
- net-tools
- unattended-upgrades

View File

@ -0,0 +1,3 @@
[ghshr]
ghshr.comprofix.xyz

3
ansible/main.yml Normal file
View File

@ -0,0 +1,3 @@
---
- import_playbook: ghshr.yml

53
ansible/tasks/base.yml Normal file
View File

@ -0,0 +1,53 @@
---
- name: Ensure debian-archive-keyring is installed
apt:
name: debian-archive-keyring
state: present
update_cache: yes
become: yes
- name: Update APT cache
apt:
update_cache: yes
force_apt_get: yes
when: ansible_distribution in ['Debian', 'Ubuntu']
- name: Upgrade all installed packages to latest
apt:
name: "*"
state: latest
force_apt_get: yes
become: yes
when: ansible_distribution in ['Debian', 'Ubuntu']
- name: Dist-upgrade packages (handle removals and replacements)
apt:
upgrade: dist
force_apt_get: yes
become: yes
when: ansible_distribution in ['Debian', 'Ubuntu']
- name: Install required packages
apt:
name: "{{ install_packages }}"
state: present
become: yes
when: ansible_distribution in ['Debian', 'Ubuntu']
register: apt_result
- name: Find all EXTERNALLY-MANAGED files under /usr/lib/python*
find:
paths: /usr/lib
patterns: "EXTERNALLY-MANAGED"
file_type: file
recurse: yes
register: externally_managed_files
become: yes
- name: Delete EXTERNALLY-MANAGED files
file:
path: "{{ item.path }}"
state: absent
loop: "{{ externally_managed_files.files }}"
when: externally_managed_files.matched > 0
become: yes

17
ansible/tasks/pull_image.yml Executable file
View File

@ -0,0 +1,17 @@
---
# Reusable snippet for pulling Docker images
- name: Set {{ image_name }} image reference
set_fact:
"{{ image_var }}_image_ref": "{{ image_ref }}"
- name: Ensure {{ image_name }} image is pulled
community.docker.docker_image:
name: "{{ image_ref }}"
source: pull
register: pulled_image
- name: Save image result under dynamic key
set_fact:
container_images: >-
{{ container_images | default({}) | combine({ image_var: pulled_image }) }}

40
opentofu/200-github.tf Normal file
View File

@ -0,0 +1,40 @@
resource "proxmox_lxc" "ghshr" {
target_node = "pve"
vmid = "200"
hostname = "ghshr"
ostemplate = "local:vztmpl/debian-13-standard_13.1-1_amd64.tar.zst"
password = var.ci_password
unprivileged = false
ostype = "debian"
onboot = true
start = true
startup = "order=1000"
ssh_public_keys = <<EOF
${var.ssh_key}
EOF
memory = "4096"
swap = "512"
rootfs {
storage = "local-zfs"
size = "8G"
}
features {
fuse = true
nesting = true
mount = "nfs;cifs"
}
network {
name = "eth0"
bridge = "vmbr0"
ip = "10.10.10.8/24"
gw = "10.10.10.1"
tag = 10
}
}

34
opentofu/prepareEnv.sh Executable file
View File

@ -0,0 +1,34 @@
#!/bin/bash
bw config server https://vault.comprofix.com
bw login
export BW_SESSION=$(bw unlock --raw)
bw sync
echo "Please wait while we prepare terraform.auto.tfvars"
proxmox_api_url=$(bw get --session $BW_SESSION uri proxmox_api)
proxmox_api_token_id=$(bw get --session $BW_SESSION username f295a859-154a-482d-8129-c6ec6e06131e)
proxmox_api_token_secret=$(bw get --session $BW_SESSION password f295a859-154a-482d-8129-c6ec6e06131e)
ci_user=$(bw get --session $BW_SESSION username ci_details)
ci_password=$(bw get --session $BW_SESSION password ci_details)
ssh_key=$(bw get --session $BW_SESSION notes ssh_public_key_main)
passphrase=$(bw get --session $BW_SESSION password state_passphrase)
tfusername=$(bw get --session $BW_SESSION username tofu_postgres)
tfpassword=$(bw get --session $BW_SESSION password tofu_postgres)
tfurl=$(bw get --session $BW_SESSION uri tofu_postgres)
echo 'proxmox_api_url = "'$proxmox_api_url'"' > terraform.auto.tfvars
echo 'proxmox_api_token_id = "'$proxmox_api_token_id'"' >> terraform.auto.tfvars
echo 'proxmox_api_token_secret = "'$proxmox_api_token_secret'"' >> terraform.auto.tfvars
echo 'ci_user = "'$ci_user'"' >> terraform.auto.tfvars
echo 'ci_password = "'$ci_password'"' >> terraform.auto.tfvars
echo 'ssh_key = "'$ssh_key'"' >> terraform.auto.tfvars
echo 'passphrase = "'$passphrase'"' >> terraform.auto.tfvars
export PG_CONN_STR="postgres://$tfusername:$tfpassword@$tfurl"

78
opentofu/provider.tf Normal file
View File

@ -0,0 +1,78 @@
terraform {
required_providers {
proxmox = {
source = "telmate/proxmox"
version = "3.0.2-rc04"
}
bitwarden = {
source = "maxlaverse/bitwarden"
version = ">= 0.13.6"
}
}
backend "pg" {}
encryption {
key_provider "pbkdf2" "mykey" {
passphrase = var.passphrase
key_length = 32
salt_length = 16
hash_function = "sha256"
}
method "aes_gcm" "secure_method" {
keys = key_provider.pbkdf2.mykey
}
state {
method = method.aes_gcm.secure_method
enforced = true
}
}
}
variable "ci_user" {
type = string
sensitive = true
}
variable "ci_password" {
type = string
sensitive = true
}
variable "proxmox_api_url" {
type = string
sensitive = true
}
variable "proxmox_api_token_id" {
type = string
sensitive = true
}
variable "proxmox_api_token_secret" {
type = string
sensitive = true
}
variable "ssh_key" {
type = string
sensitive = true
}
variable "passphrase" {
type = string
sensitive = true
}
provider "proxmox" {
pm_api_url = var.proxmox_api_url
pm_user = "root@pam"
pm_password = var.proxmox_api_token_secret
pm_timeout = 3600
pm_parallel = 2 # Fix VM HDD lock timeout
# Optional: Skip TLS Verification
pm_tls_insecure = true
}