commit 9df999f89f9358fefe10ecf1ce6b06a5820415d1 Author: xbazzi Date: Mon Jul 7 19:55:34 2025 -0600 initial commit diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b1e5280 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "ansible.python.interpreterPath": "/run/current-system/sw/bin/python", + "ansible.validation.lint.path": "", + "ansible.validation.lint.enabled": false +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7f6c6df --- /dev/null +++ b/README.md @@ -0,0 +1,150 @@ +# Ansible Starter Kit + +Easy way for my homies to start up an ansible project for their homelab or local machine. + +# Prerequisites + +The ideal setup for a homelab is to have: + +- The same username/password in all vms/hosts so you don't have to type a different password for each host +- SSH key auth on each host so you don't have to type a password at all + +# Ansible basics + +I'll explain the basic units of an ansible project. For this example we're going to assume you want to mount +an NFS share in all your hosts. + +From bottom to top we have: + +## Tasks + +A task is the lowest unit in an ansible project. + +A task could be: + +- Creating a directory +- Installing an os package (curl, docker, nfs-common, etc) +- Starting a docker container + +### Examples + +```yml +- name: Add SSH key for remote user + ansible.posix.authorized_key: + user: javi + state: present + key: "{{ lookup('file', '/home/javi/.ssh/homelab_keypair_ed25519.pub') }}" +``` + +```yml +- name: Ensure NFS client is installed + ansible.builtin.package: + name: nfs-common + state: present + become: true +``` + +## Role + +A role is a self contained, re-usable unit that will give a meaningful result. Think of it like a class in a program. + +Roles have a list of tasks, as well as variables and files associated with those tasks. All contained in a folder. + +A role can look like: + +``` +roles/ + portainer/ + ├── defaults/ + │ └── main.yml # Default variables + ├── files/ + │ └── ... # Static files to be copied (e.g., configs, scripts) + ├── handlers/ + │ └── main.yml # Handlers (e.g., service restart) + ├── meta/ + │ └── main.yml # Role metadata (e.g., dependencies) + ├── tasks/ + │ └── main.yml # Main list of tasks to execute + ├── templates/ + │ └── ... # Jinja2 templates + ├── vars/ + │ └── main.yml # Non-overridable variables + └── README.md # Optional: Document what this role does + sshkey/ + ├── defaults/ + │ └── main.yml # Default variables + ├── files/ + │ └── ... # Static files to be copied (e.g., configs, scripts) + ├── handlers/ + │ └── main.yml # Handlers (e.g., service restart) + ├── meta/ + │ └── main.yml # Role metadata (e.g., dependencies) + ├── tasks/ + │ └── main.yml # Main list of tasks to execute + ├── templates/ + │ └── ... # Jinja2 templates + ├── vars/ + │ └── main.yml # Non-overridable variables + └── README.md # Optional: Document what this role does + +``` + +However, only the `defaults` and `tasks` subfolders and subsequent `main.yml` are required. Everything else is optional. + +A role can be: + +- Mounting a share +- Deploying a docker container with all its requirements like a database and mapped volumes + +# Playbook + +A playbook combines roles and task to create a final state in a host or group of hosts. + +A playbook can be: + +- Make sure all your hosts have sshkey auth, portainer, docker and an nfs share mounted. +- Deploy a suite of apps to a host or multiple hosts + +## Example + +```yml +--- +- name: Deploy apps to apps-1 node + hosts: apps + become: true + roles: + - role: apps/kan + vars: + port: 7070 + - role: apps/memos + vars: + port: 7071 + - role: apps/vaultwarden + vars: + port: 7072 + - role: apps/erugo + vars: + port: 7073 + - role: apps/tianji + vars: + port: 7074 + - role: apps/stirling-pdf o + vars: + port: 7075 + - role: apps/dumbware-todo + vars: + port: 7076 + pin: 8989 + - role: apps/dumbware-drop + vars: + port: 7077 + pin: "8989" +``` + +# Setup + +1. Clone this repo `git clone git@gitgud.foo:javif89/ansible-starter-kit.git [your project name]` +2. Run `setup.sh` to set up the vault password and become password +3. Set up your hosts in `hosts.yml` +4. Start making your roles and playbooks + diff --git a/ansible.cfg b/ansible.cfg new file mode 100755 index 0000000..6f499f1 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,4 @@ +[defaults] +inventory = hosts.yml +roles_path = ./roles +vault_password_file = ~/.ansible-vault-key diff --git a/group_vars/all.yml b/group_vars/all.yml new file mode 100755 index 0000000..897e3d0 --- /dev/null +++ b/group_vars/all.yml @@ -0,0 +1,47 @@ +project_root: "{{ inventory_dir }}" # Useful hack for easier file paths +# Postgres +# If you have a main database instance all your +# services will use (which you should), put +# the info here so it's accessible to all +# your playbooks. +pg_host: prod1.lan.xbazzi.com +pg_port: 5432 +pg_user: postgres +pg_password: password + +docker_dir: /data/docker + +# Local paths +docker_stacks: "{{ project_root }}/docker" +assets: "{{ project_root }}/assets" + +# Remote paths +remote_stacks: "/home/javi/docker" +remote_app_mounts: "/home/docker" +# Sudo password for your servers +# Sudo password for your servers +ansible_become_pass: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 64633661313934656164656164623432366339636334653736383330363662313861633338626437 + 3335386363316431653536373264393537626234306633340a353764396630343664646434636463 + 65613232303363373237626163306261306266363336336130666331376133653733393864663335 + 6335663333336139350a346233373231323238376364663761663461383331366264396665383364 + 6264 + +# TrueNAS iSCSI stuff +iscsi_target_ip: nas.lan.xbazzi.com # TrueNAS IP +iscsi_target_iqn: iqn.2005-10.org.freenas.ctl:pve-iscsi + +# Alma new VM provisioning +hostname: "prod3" +timezone: "America/Denver" + +network_config: + interface: "ens18" + address: "" + netmask: "255.255.252.0" + gateway: "10.133.7.103" + dns: ["10.133.7.1"] + +nfs_mounts: + - { src: "nas:/mnt/media", path: "/mnt/media", opts: "defaults,nfsvers=4" } diff --git a/group_vars/cluster_prep.yml b/group_vars/cluster_prep.yml new file mode 100644 index 0000000..96d1b46 --- /dev/null +++ b/group_vars/cluster_prep.yml @@ -0,0 +1,2 @@ +iscsi_target_ip: nas.lan.xbazzi.com # TrueNAS IP +iscsi_target_iqn: iqn.2005-10.org.freenas.ctl:pve-iscsi \ No newline at end of file diff --git a/host_vars/pve1.yml b/host_vars/pve1.yml new file mode 100644 index 0000000..04dae01 --- /dev/null +++ b/host_vars/pve1.yml @@ -0,0 +1,6 @@ +vmbr0_ip: 10.69.0.21 +vmbr1_1337_ip: 10.133.7.21 +vmbr1_666_ip: 10.66.6.21 +cluster_iface: "vmbr1.999" +cluster_ip: 10.99.9.1 + diff --git a/host_vars/pve2.yml b/host_vars/pve2.yml new file mode 100644 index 0000000..050cc53 --- /dev/null +++ b/host_vars/pve2.yml @@ -0,0 +1,5 @@ +vmbr0_ip: 10.69.0.22 +vmbr1_1337_ip: 10.133.7.22 +vmbr1_666_ip: 10.66.6.22 +cluster_iface: "vmbr1.999" +cluster_ip: 10.99.9.2 diff --git a/host_vars/pve3.yml b/host_vars/pve3.yml new file mode 100644 index 0000000..2dd916e --- /dev/null +++ b/host_vars/pve3.yml @@ -0,0 +1,5 @@ +vmbr0_ip: 10.69.0.23 +vmbr1_1337_ip: 10.133.7.23 +vmbr1_666_ip: 10.66.6.23 +cluster_iface: "vmbr1.999" +cluster_ip: 10.99.9.3 diff --git a/hosts.yml b/hosts.yml new file mode 100755 index 0000000..abad0fd --- /dev/null +++ b/hosts.yml @@ -0,0 +1,28 @@ +--- +all: + children: + pve-nodes: + hosts: + pve1: + ansible_host: pve1 + pve2: + ansible_host: pve2 + pve3: + ansible_host: pve3 + vms: + hosts: + prod1: + ansible_host: prod1 + prod2: + ansible_host: prod2 + school: + ansible_host: school + new-vm: + ansible_host: 10.133.7.240 + # alma-provision: + # hosts: + # new-alma-vm: + # ansible_host: + # vars: + # ansible_user: root + diff --git a/playbooks/backup-pve.yml b/playbooks/backup-pve.yml new file mode 100644 index 0000000..73866bf --- /dev/null +++ b/playbooks/backup-pve.yml @@ -0,0 +1,6 @@ +--- +- name: Backup /etc/pve from all Proxmox nodes + hosts: pve-nodes + become: yes + roles: + - role: utility/pve_backup diff --git a/playbooks/backups/pve1.tar.gz b/playbooks/backups/pve1.tar.gz new file mode 100644 index 0000000..ab4d6f5 Binary files /dev/null and b/playbooks/backups/pve1.tar.gz differ diff --git a/playbooks/backups/pve2.tar.gz b/playbooks/backups/pve2.tar.gz new file mode 100644 index 0000000..b1c2430 Binary files /dev/null and b/playbooks/backups/pve2.tar.gz differ diff --git a/playbooks/backups/pve3.tar.gz b/playbooks/backups/pve3.tar.gz new file mode 100644 index 0000000..5aaac1f Binary files /dev/null and b/playbooks/backups/pve3.tar.gz differ diff --git a/playbooks/configure-pve.yml b/playbooks/configure-pve.yml new file mode 100644 index 0000000..c2c531a --- /dev/null +++ b/playbooks/configure-pve.yml @@ -0,0 +1,6 @@ +--- +- name: Configure networking for a new node + hosts: pve-nodes + become: true + roles: + - role: pve/setup_networking \ No newline at end of file diff --git a/playbooks/deploy-dbgate.yml b/playbooks/deploy-dbgate.yml new file mode 100644 index 0000000..6a70f57 --- /dev/null +++ b/playbooks/deploy-dbgate.yml @@ -0,0 +1,15 @@ +--- +- name: Deploy DBGate container + hosts: prod1 + become: true + roles: + - role: apps/dbgate + vars: + directory: "pg-dev" + container_name: "postgres-dev" + port: 7000 + - role: services/postgres + vars: + directory: "pg-beta" + container_name: "postgres-beta" + port: 7001 \ No newline at end of file diff --git a/playbooks/deploy-postgres.yml b/playbooks/deploy-postgres.yml new file mode 100644 index 0000000..2ed19f5 --- /dev/null +++ b/playbooks/deploy-postgres.yml @@ -0,0 +1,16 @@ +--- +- name: Deploy PostgreSQL container + hosts: prod1 + become: true + roles: + - role: services/postgres + - role: services/postgres + vars: + directory: "pg-dev" + container_name: "postgres-dev" + port: 7000 + - role: services/postgres + vars: + directory: "pg-beta" + container_name: "postgres-beta" + port: 7001 \ No newline at end of file diff --git a/playbooks/example.yml b/playbooks/example.yml new file mode 100644 index 0000000..d0cce9d --- /dev/null +++ b/playbooks/example.yml @@ -0,0 +1,6 @@ +--- +- name: Example playbook + hosts: vms + become: true + roles: + - role: server/sshkey \ No newline at end of file diff --git a/playbooks/prep-pve-for-cluster.yml b/playbooks/prep-pve-for-cluster.yml new file mode 100644 index 0000000..b4d3bc5 --- /dev/null +++ b/playbooks/prep-pve-for-cluster.yml @@ -0,0 +1,5 @@ +- name: Prep all Proxmox nodes for clustering + hosts: pve-nodes + become: yes + roles: + - role: utility/cluster_prep diff --git a/playbooks/provision-alma.yml b/playbooks/provision-alma.yml new file mode 100644 index 0000000..0b5045d --- /dev/null +++ b/playbooks/provision-alma.yml @@ -0,0 +1,8 @@ +- name: Provision AlmaLinux 10 VM + hosts: new-vm + become: yes + roles: + - role: provision/alma/common + # - role: provision/alma/network + # - role: provision/alma/nfs + # - role: provision/alma/docker diff --git a/roles/app/database/defaults/main.yml b/roles/app/database/defaults/main.yml new file mode 100755 index 0000000..e69de29 diff --git a/roles/app/database/tasks/main.yml b/roles/app/database/tasks/main.yml new file mode 100755 index 0000000..1fb4cf8 --- /dev/null +++ b/roles/app/database/tasks/main.yml @@ -0,0 +1,20 @@ +--- +- name: Create app database + ansible.builtin.include_role: + name: postgres/database + vars: + database: "{{ app_name }}" + +- name: Create app db user + ansible.builtin.include_role: + name: postgres/user + vars: + user: "{{ app_name }}" + password: "password" + +- name: Give app user full priviledges on DB + ansible.builtin.include_role: + name: postgres/priviledges + vars: + database: "{{ app_name }}" + user: "{{ app_name }}" \ No newline at end of file diff --git a/roles/apps/dbgate/defaults/main.yml b/roles/apps/dbgate/defaults/main.yml new file mode 100644 index 0000000..8676a24 --- /dev/null +++ b/roles/apps/dbgate/defaults/main.yml @@ -0,0 +1,4 @@ +docker_dir: "/data/docker/dbgate" +port: "6001" +app_port: "3000" +container_name: "postgres" \ No newline at end of file diff --git a/roles/apps/dbgate/tasks/main.yml b/roles/apps/dbgate/tasks/main.yml new file mode 100644 index 0000000..706d28a --- /dev/null +++ b/roles/apps/dbgate/tasks/main.yml @@ -0,0 +1,19 @@ +- name: Create docker folder + ansible.builtin.file: + dest: "{{ docker_dir }}" + state: directory + mode: '0770' + +- name: Put up the postgres container + community.docker.docker_container: + name: "{{container_name}}" + image: postgres:17.4 + restart_policy: always + state: started + pull: true + ports: + - "{{ port }}:{{ app_port }}" + env: + CONNECTIONS: postgres_con + volumes: + - "dbgate-data:/root/.dbgate" \ No newline at end of file diff --git a/roles/docker/install/defaults/main.yml b/roles/docker/install/defaults/main.yml new file mode 100755 index 0000000..e69de29 diff --git a/roles/docker/install/tasks/main.yml b/roles/docker/install/tasks/main.yml new file mode 100755 index 0000000..86b2c7c --- /dev/null +++ b/roles/docker/install/tasks/main.yml @@ -0,0 +1,45 @@ +--- +- name: Update apt cache + ansible.builtin.apt: + update_cache: yes + +- name: Install prerequisite packages + ansible.builtin.apt: + name: + - ca-certificates + - curl + state: present + +- name: Create apt keyrings directory + ansible.builtin.file: + path: /etc/apt/keyrings + state: directory + mode: '0755' + +- name: Download Docker GPG key + ansible.builtin.get_url: + url: "https://download.docker.com/linux/ubuntu/gpg" + dest: /etc/apt/keyrings/docker.asc + mode: '0644' + +- name: Add Docker apt repository + ansible.builtin.apt_repository: + repo: "deb [arch={{ docker_arch }} signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable" + filename: docker + state: present + vars: + docker_arch: "{{ ansible_architecture | regex_replace('x86_64', 'amd64') }}" + +- name: Update apt cache after adding Docker repository + ansible.builtin.apt: + update_cache: true + +- name: Install Docker packages + ansible.builtin.apt: + name: + - docker-ce + - docker-ce-cli + - containerd.io + - docker-buildx-plugin + - docker-compose-plugin + state: present \ No newline at end of file diff --git a/roles/docker/portainer/defaults/main.yml b/roles/docker/portainer/defaults/main.yml new file mode 100755 index 0000000..e69de29 diff --git a/roles/docker/portainer/tasks/main.yml b/roles/docker/portainer/tasks/main.yml new file mode 100755 index 0000000..a18e91d --- /dev/null +++ b/roles/docker/portainer/tasks/main.yml @@ -0,0 +1,22 @@ +- name: Pull Portainer Agent image + become: true + community.docker.docker_image: + name: portainer/agent + tag: latest + source: pull + +- name: Deploy Portainer Agent container + become: true + community.docker.docker_container: + name: portainer_agent + image: portainer/agent + pull: false # we already pulled above + state: started + restart_policy: always + ports: + - "9001:9001" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /var/lib/docker/volumes:/var/lib/docker/volumes + - /:/host + timeout: 120 # wait up to 2m for it to come up diff --git a/roles/docker/stack/defaults/main.yml b/roles/docker/stack/defaults/main.yml new file mode 100755 index 0000000..6b7f1a5 --- /dev/null +++ b/roles/docker/stack/defaults/main.yml @@ -0,0 +1,2 @@ +apps: [] +stack_name: "willneverexist" \ No newline at end of file diff --git a/roles/docker/stack/tasks/main.yml b/roles/docker/stack/tasks/main.yml new file mode 100755 index 0000000..ab0863b --- /dev/null +++ b/roles/docker/stack/tasks/main.yml @@ -0,0 +1,27 @@ +--- +- name: Create app mount directories + ansible.builtin.file: + path: "{{ remote_app_mounts }}/{{ item }}" + state: directory + mode: '0777' + loop: "{{ apps }}" + +- name: Create stack directory + ansible.builtin.file: + path: "{{ remote_stacks }}/{{ stack_name }}" + state: directory + mode: '0777' + +- name: Copy docker-compose.yml to server + ansible.builtin.copy: + src: '{{ docker_stacks }}/{{ stack_name }}/docker-compose.yml' + dest: '{{ remote_stacks }}/{{ stack_name }}/docker-compose.yml' + owner: javi + group: javi + mode: '0777' + +- name: Start up the containers + ansible.builtin.command: docker compose up -d + become: true + args: + chdir: "{{ remote_stacks }}/{{ stack_name }}" \ No newline at end of file diff --git a/roles/postgres/database/defaults/main.yml b/roles/postgres/database/defaults/main.yml new file mode 100755 index 0000000..e69de29 diff --git a/roles/postgres/database/tasks/main.yml b/roles/postgres/database/tasks/main.yml new file mode 100755 index 0000000..b54e09e --- /dev/null +++ b/roles/postgres/database/tasks/main.yml @@ -0,0 +1,10 @@ +--- +- name: Create database + delegate_to: localhost + community.postgresql.postgresql_db: + name: "{{ database }}" + state: present + login_host: "{{ pg_host }}" + login_port: "{{ pg_port }}" + login_user: "{{ pg_user }}" + login_password: "{{ pg_password }}" diff --git a/roles/postgres/priviledges/defaults/main.yml b/roles/postgres/priviledges/defaults/main.yml new file mode 100755 index 0000000..84dc138 --- /dev/null +++ b/roles/postgres/priviledges/defaults/main.yml @@ -0,0 +1 @@ +priviledges: ALL \ No newline at end of file diff --git a/roles/postgres/priviledges/tasks/main.yml b/roles/postgres/priviledges/tasks/main.yml new file mode 100755 index 0000000..b47041a --- /dev/null +++ b/roles/postgres/priviledges/tasks/main.yml @@ -0,0 +1,28 @@ +--- +- name: Grant database-level privileges on "{{ database }}" + delegate_to: localhost + community.postgresql.postgresql_privs: + db: "{{ database }}" + type: database + objs: "{{ database }}" + privs: "CREATE" + role: "{{ user }}" + state: present + login_host: "{{ pg_host }}" + login_port: "{{ pg_port }}" + login_user: "{{ pg_user }}" + login_password: "{{ pg_password }}" + +- name: Give user full priviledges on database + delegate_to: localhost + community.postgresql.postgresql_privs: + db: "{{ database }}" + type: schema + objs: public + privs: "{{ priviledges }}" + role: "{{ user }}" + state: present + login_host: "{{ pg_host }}" + login_port: "{{ pg_port }}" + login_user: "{{ pg_user }}" + login_password: "{{ pg_password }}" \ No newline at end of file diff --git a/roles/postgres/user/defaults/main.yml b/roles/postgres/user/defaults/main.yml new file mode 100755 index 0000000..3fab93f --- /dev/null +++ b/roles/postgres/user/defaults/main.yml @@ -0,0 +1 @@ +password: "password" \ No newline at end of file diff --git a/roles/postgres/user/tasks/main.yml b/roles/postgres/user/tasks/main.yml new file mode 100755 index 0000000..bbdd928 --- /dev/null +++ b/roles/postgres/user/tasks/main.yml @@ -0,0 +1,11 @@ +--- +- name: Create postgres user + delegate_to: localhost + community.postgresql.postgresql_user: + name: "{{ user }}" + password: "{{ password }}" + state: present + login_host: "{{ pg_host }}" + login_port: "{{ pg_port }}" + login_user: "{{ pg_user }}" + login_password: "{{ pg_password }}" \ No newline at end of file diff --git a/roles/provision/alma/common/defaults/main.yml b/roles/provision/alma/common/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/common/handlers/main.yml b/roles/provision/alma/common/handlers/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/common/tasks/main.yml b/roles/provision/alma/common/tasks/main.yml new file mode 100644 index 0000000..b698c89 --- /dev/null +++ b/roles/provision/alma/common/tasks/main.yml @@ -0,0 +1,26 @@ +--- +- name: Set system timezone + ansible.builtin.command: timedatectl set-timezone "America/Denver" + register: output + changed_when: output.rc != 0 + +- name: Set hostname + ansible.builtin.hostname: + name: "{{ hostname }}" + +- name: Set repo + ansible.builtin.dnf: + +- name: Install baseline packages + ansible.builtin.dnf: + name: + - '@Development tools' + - vim + - curl + - git + - bash-completion + - firewalld + # - fastfetch + # - btop + state: present + update_cache: true diff --git a/roles/provision/alma/common/templates/main.yml b/roles/provision/alma/common/templates/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/docker/defaults/main.yml b/roles/provision/alma/docker/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/docker/handlers/main.yml b/roles/provision/alma/docker/handlers/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/docker/tasks/main.yml b/roles/provision/alma/docker/tasks/main.yml new file mode 100644 index 0000000..301767e --- /dev/null +++ b/roles/provision/alma/docker/tasks/main.yml @@ -0,0 +1,18 @@ +- name: Add Docker repository + ansible.builtin.get_url: + url: https://download.docker.com/linux/centos/docker-ce.repo + dest: /etc/yum.repos.d/docker-ce.repo + +- name: Install Docker packages + ansible.builtin.dnf: + name: + - docker-ce + - docker-ce-cli + - containerd.io + state: present + +- name: Enable and start Docker + ansible.builtin.service: + name: docker + enabled: yes + state: started diff --git a/roles/provision/alma/docker/templates/main.yml b/roles/provision/alma/docker/templates/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/firewall/defaults/main.yml b/roles/provision/alma/firewall/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/firewall/handlers/main.yml b/roles/provision/alma/firewall/handlers/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/firewall/tasks/main.yml b/roles/provision/alma/firewall/tasks/main.yml new file mode 100644 index 0000000..497319d --- /dev/null +++ b/roles/provision/alma/firewall/tasks/main.yml @@ -0,0 +1,5 @@ +- name: Enable and start firewalld + ansible.builtin.service: + name: firewalld + enabled: yes + state: started diff --git a/roles/provision/alma/firewall/templates/main.yml b/roles/provision/alma/firewall/templates/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/network/defaults/main.yml b/roles/provision/alma/network/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/network/handlers/main.yml b/roles/provision/alma/network/handlers/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/network/tasks/main.yml b/roles/provision/alma/network/tasks/main.yml new file mode 100644 index 0000000..fa5538f --- /dev/null +++ b/roles/provision/alma/network/tasks/main.yml @@ -0,0 +1,12 @@ +- name: Configure static network + ansible.builtin.template: + src: ifcfg-template.j2 + dest: "/etc/sysconfig/network-scripts/ifcfg-{{ network_config.interface }}" + notify: Restart network + +- name: Ensure NetworkManager is enabled + ansible.builtin.service: + name: NetworkManager + enabled: true + state: restarted + diff --git a/roles/provision/alma/network/templates/ifcfg-template.j2 b/roles/provision/alma/network/templates/ifcfg-template.j2 new file mode 100644 index 0000000..89715b7 --- /dev/null +++ b/roles/provision/alma/network/templates/ifcfg-template.j2 @@ -0,0 +1,6 @@ +DEVICE={{ network_config.interface }} +BOOTPROTO=none +ONBOOT=yes +IPADDR={{ network_config.address }} +NETMASK={{ network_config.netmask }} +GATEWAY={{ network_config.gateway }} diff --git a/roles/provision/alma/network/templates/main.yml b/roles/provision/alma/network/templates/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/nfs/defaults/main.yml b/roles/provision/alma/nfs/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/nfs/handlers/main.yml b/roles/provision/alma/nfs/handlers/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/provision/alma/nfs/tasks/main.yml b/roles/provision/alma/nfs/tasks/main.yml new file mode 100644 index 0000000..ef40666 --- /dev/null +++ b/roles/provision/alma/nfs/tasks/main.yml @@ -0,0 +1,22 @@ +- name: Install NFS client + ansible.builtin.dnf: + name: nfs-utils + state: present + +- name: Create mount points + ansible.builtin.file: + path: "{{ item.path }}" + state: directory + owner: root + group: root + mode: '0755' + loop: "{{ nfs_mounts }}" + +- name: Mount NFS shares + ansible.posix.mount: + src: "{{ item.src }}" + path: "{{ item.path }}" + fstype: nfs + opts: "{{ item.opts }}" + state: mounted + loop: "{{ nfs_mounts }}" diff --git a/roles/provision/alma/nfs/templates/main.yml b/roles/provision/alma/nfs/templates/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/pve/cluster_prep/defaults/main.yml b/roles/pve/cluster_prep/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/pve/cluster_prep/tasks/main.yml b/roles/pve/cluster_prep/tasks/main.yml new file mode 100644 index 0000000..c179b51 --- /dev/null +++ b/roles/pve/cluster_prep/tasks/main.yml @@ -0,0 +1,108 @@ +--- +# - name: Set hostname +# hostname: +# name: "{{ inventory_hostname }}" +# - name: Disable Proxmox Enterprise repo +# lineinfile: +# path: /etc/apt/sources.list.d/pve-enterprise.list +# regexp: '^deb' +# line: '# deb ...' +# state: present +# ignore_errors: yes # In case the file doesn't exist + +- name: Find all sources.list.d files + find: + paths: /etc/apt/sources.list.d + patterns: "*.list" + file_type: file + register: list_files + +- name: Comment out any line with 'enterprise' in each file + lineinfile: + path: "{{ item.path }}" + regexp: '^(?!#).*enterprise' + line: '# \g<0>' + backrefs: yes + state: present + loop: "{{ list_files.files }}" + +- name: Overwrite sources.list with Proxmox-recommended repos + copy: + dest: /etc/apt/sources.list + content: | + deb http://ftp.debian.org/debian bookworm main contrib + deb http://ftp.debian.org/debian bookworm-updates main contrib + + # Proxmox VE pve-no-subscription repository provided by proxmox.com, + # NOT recommended for production use + deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription + + # security updates + deb http://security.debian.org/debian-security bookworm-security main contrib + mode: '0644' + +- name: Add Proxmox no-subscription repo to sources.list.d + copy: + dest: /etc/apt/sources.list.d/pve-no-subscription.list + content: | + deb http://download.proxmox.com/debian/pve bullseye pve-no-subscription + owner: root + group: root + mode: '0644' + + +- name: Update apt cache + apt: + update_cache: yes + + +- name: Update /etc/hosts with all PVE nodes + template: + src: hosts.j2 + dest: /etc/hosts + mode: "0644" + +- name: Ensure search domain and nameserver set properly + template: + src: resolv.j2 + dest: /etc/resolv.conf + mode: "0644" + +- name: Ensure chrony is installed + apt: + name: chrony + state: present + update_cache: yes + +- name: Enable and start chronyd + service: + name: chrony + state: started + enabled: yes + +- name: Discover iSCSI targets from TrueNAS + shell: | + iscsiadm -m discovery -t st -p {{ iscsi_target_ip }} + register: iscsi_discovery + changed_when: false + +- name: Login to discovered iSCSI target (unauthenticated) + shell: | + iscsiadm -m node -T {{ iscsi_target_iqn }} -p {{ iscsi_target_ip }} --login + register: iscsi_login + changed_when: "'Login to' in iscsi_login.stdout or 'already present' in iscsi_login.stdout" + +- name: Make iSCSI login persistent across reboots + shell: | + iscsiadm -m node -T {{ iscsi_target_iqn }} -p {{ iscsi_target_ip }} --op update -n node.startup -v automatic + changed_when: false + +# - name: Ensure vg_ha exists +# command: vgs vg_ha +# register: vg_result +# failed_when: vg_result.rc != 0 +# changed_when: false + +# - name: Debug VG presence +# debug: +# msg: "VG 'vg_ha' found on {{ inventory_hostname }}" diff --git a/roles/pve/cluster_prep/templates/hosts.j2 b/roles/pve/cluster_prep/templates/hosts.j2 new file mode 100644 index 0000000..a65756c --- /dev/null +++ b/roles/pve/cluster_prep/templates/hosts.j2 @@ -0,0 +1,6 @@ +127.0.0.1 localhost +{% for host in groups['pve-nodes'] %} +{{ hostvars[host]['ansible_default_ipv4']['address'] }} {{ host }} {{ host }}.lan.xbazzi.com +{{ hostvars[host]['cluster_ip'] }} {{ host }}-cluster +{% endfor %} + diff --git a/roles/pve/cluster_prep/templates/resolv.j2 b/roles/pve/cluster_prep/templates/resolv.j2 new file mode 100644 index 0000000..e2b3300 --- /dev/null +++ b/roles/pve/cluster_prep/templates/resolv.j2 @@ -0,0 +1,2 @@ +search lan.xbazzi.com +nameserver 10.133.7.1 \ No newline at end of file diff --git a/roles/pve/pve_backup/defaults/main.yml b/roles/pve/pve_backup/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/pve/pve_backup/tasks/main.yml b/roles/pve/pve_backup/tasks/main.yml new file mode 100644 index 0000000..64fabe3 --- /dev/null +++ b/roles/pve/pve_backup/tasks/main.yml @@ -0,0 +1,26 @@ +--- +- name: Copy backup script to PVE node + template: + src: backup_pve_config.sh.j2 + dest: /home/xbazzi/backup_pve.sh + mode: '0755' + +- name: Run backup script + shell: /home/xbazzi/backup_pve.sh + +- name: Find most recent backup directory + shell: "ls -td /home/xbazzi/pve_backup_* | head -1" + register: latest_backup_dir + changed_when: false + +- name: Archive backup folder + archive: + path: "{{ latest_backup_dir.stdout }}" + dest: "{{ latest_backup_dir.stdout }}.tar.gz" + format: gz + +- name: Fetch backup archive to control machine + fetch: + src: "{{ latest_backup_dir.stdout }}.tar.gz" + dest: "backups/{{ inventory_hostname }}.tar.gz" + flat: yes diff --git a/roles/pve/pve_backup/templates/backup_pve_config.sh.j2 b/roles/pve/pve_backup/templates/backup_pve_config.sh.j2 new file mode 100644 index 0000000..0e1d96e --- /dev/null +++ b/roles/pve/pve_backup/templates/backup_pve_config.sh.j2 @@ -0,0 +1,23 @@ +#!/bin/bash +set -e + +TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S") +BACKUP_DIR="/home/xbazzi/pve_backup_${TIMESTAMP}" +NODE_NAME=$(hostname) + +echo "🔒 Creating backup directory at $BACKUP_DIR..." +mkdir -p "$BACKUP_DIR" + +echo "📁 Backing up /etc/pve..." +cp -a /etc/pve "$BACKUP_DIR/etc_pve" + +echo "📄 Saving VM and container config files..." +mkdir -p "$BACKUP_DIR/qemu-server" "$BACKUP_DIR/lxc" +cp -a /etc/pve/qemu-server/*.conf "$BACKUP_DIR/qemu-server/" 2>/dev/null || true +cp -a /etc/pve/lxc/*.conf "$BACKUP_DIR/lxc/" 2>/dev/null || true + +echo "💽 Saving storage.cfg..." +cp -a /etc/pve/storage.cfg "$BACKUP_DIR/" 2>/dev/null || true + +echo "📦 Backup complete on $NODE_NAME." +echo "🗃️ Location: $BACKUP_DIR" diff --git a/roles/pve/setup_networking/defaults/main.yml b/roles/pve/setup_networking/defaults/main.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/pve/setup_networking/handlers/main.yml b/roles/pve/setup_networking/handlers/main.yml new file mode 100644 index 0000000..eb3f4ff --- /dev/null +++ b/roles/pve/setup_networking/handlers/main.yml @@ -0,0 +1,5 @@ +# - name: Restart networking +# ansible.builtin.systemd: +# name: networking +# state: restarted + \ No newline at end of file diff --git a/roles/pve/setup_networking/tasks/main.yml b/roles/pve/setup_networking/tasks/main.yml new file mode 100644 index 0000000..91d62bc --- /dev/null +++ b/roles/pve/setup_networking/tasks/main.yml @@ -0,0 +1,46 @@ +--- +- name: Set up network interfaces for new PVE node + template: + src: interfaces-xbazzi.j2 + # dest: /etc/network/interfaces.d/interfaces-xbazzi + dest: /etc/network/interfaces + owner: root + group: root + mode: "0644" + +- name: Apply correct permissions to interfaces.d + file: + path: "/etc/network/interfaces.d" + owner: root + group: root + mode: '0644' + +- name: Find all files in the directory + ansible.builtin.find: + paths: /etc/network/interfaces.d/ + file_type: file + register: files_to_delete + +- name: Delete all files + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ files_to_delete.files }}" + +- name: Update /etc/hosts with all PVE nodes + template: + src: hosts.j2 + dest: /etc/hosts + mode: "0644" + +- name: Ensure search domain and nameserver set properly + template: + src: resolv.j2 + dest: /etc/resolv.conf + mode: "0644" + + +- name: Restart networking + ansible.builtin.systemd: + name: networking + state: restarted diff --git a/roles/pve/setup_networking/templates/hosts.j2 b/roles/pve/setup_networking/templates/hosts.j2 new file mode 100644 index 0000000..a65756c --- /dev/null +++ b/roles/pve/setup_networking/templates/hosts.j2 @@ -0,0 +1,6 @@ +127.0.0.1 localhost +{% for host in groups['pve-nodes'] %} +{{ hostvars[host]['ansible_default_ipv4']['address'] }} {{ host }} {{ host }}.lan.xbazzi.com +{{ hostvars[host]['cluster_ip'] }} {{ host }}-cluster +{% endfor %} + diff --git a/roles/pve/setup_networking/templates/interfaces-xbazzi.j2 b/roles/pve/setup_networking/templates/interfaces-xbazzi.j2 new file mode 100644 index 0000000..e3f38ff --- /dev/null +++ b/roles/pve/setup_networking/templates/interfaces-xbazzi.j2 @@ -0,0 +1,48 @@ +auto eno1 +iface eno1 inet manual + mtu 1500 + +auto enp1s0f0 +iface enp1s0f0 inet manual + mtu 9000 + +iface enp1s0f1 inet manual + mtu 9000 + +# Mgmt interface +auto vmbr0 +iface vmbr0 inet static + address {{ vmbr0_ip }}/22 + bridge-ports eno1 + bridge-stp off + bridge-fd 0 + mtu 1500 + +auto vmbr1 +iface vmbr1 inet manual + bridge-ports enp1s0f0 + bridge-stp off + bridge-fd 0 + bridge-vlan-aware yes + bridge-vids 2-4094 + mtu 9000 + +# Prod interface +auto vmbr1.1337 +iface vmbr1.1337 inet static + address {{ vmbr1_1337_ip }}/22 + gateway 10.133.7.1 + mtu 9000 + +# DMZ interface +auto vmbr1.666 +iface vmbr1.666 inet static + address {{ vmbr1_666_ip }}/22 + mtu 1500 + +# Cluster network +auto {{ cluster_iface }} +iface {{ cluster_iface }} inet static + address {{ cluster_ip }}/28 + mtu 1500 + diff --git a/roles/pve/setup_networking/templates/resolv.j2 b/roles/pve/setup_networking/templates/resolv.j2 new file mode 100644 index 0000000..e2b3300 --- /dev/null +++ b/roles/pve/setup_networking/templates/resolv.j2 @@ -0,0 +1,2 @@ +search lan.xbazzi.com +nameserver 10.133.7.1 \ No newline at end of file diff --git a/roles/server/ftp/defaults/main.yml b/roles/server/ftp/defaults/main.yml new file mode 100755 index 0000000..e69de29 diff --git a/roles/server/ftp/tasks/main.yml b/roles/server/ftp/tasks/main.yml new file mode 100755 index 0000000..a710115 --- /dev/null +++ b/roles/server/ftp/tasks/main.yml @@ -0,0 +1,23 @@ +--- +- name: Update apt cache + ansible.builtin.apt: + update_cache: true + +- name: Install proftpd package + ansible.builtin.apt: + name: proftpd + state: present + +- name: Ensure proftpd is enabled and started + ansible.builtin.service: + name: proftpd + state: started + enabled: true + become: true + +- name: Allow FTP through UFW firewall (if UFW is enabled) + community.general.ufw: + rule: allow + port: 21 + proto: tcp + ignore_errors: false \ No newline at end of file diff --git a/roles/server/sshkey/defaults/main.yml b/roles/server/sshkey/defaults/main.yml new file mode 100755 index 0000000..e69de29 diff --git a/roles/server/sshkey/tasks/main.yml b/roles/server/sshkey/tasks/main.yml new file mode 100755 index 0000000..fbb2fa0 --- /dev/null +++ b/roles/server/sshkey/tasks/main.yml @@ -0,0 +1,6 @@ +--- +- name: Add SSH key for remote user + ansible.posix.authorized_key: + user: javi + state: present + key: "{{ lookup('file', '/home/javi/.ssh/homelab_keypair_ed25519.pub') }}" \ No newline at end of file diff --git a/roles/services/postgres/defaults/main.yml b/roles/services/postgres/defaults/main.yml new file mode 100644 index 0000000..8ca4c28 --- /dev/null +++ b/roles/services/postgres/defaults/main.yml @@ -0,0 +1,5 @@ +directory: "postgres" +default_user: "postgres" +default_password: "password" +port: "5432" +container_name: "postgres" \ No newline at end of file diff --git a/roles/services/postgres/tasks/main.yml b/roles/services/postgres/tasks/main.yml new file mode 100644 index 0000000..7df3f06 --- /dev/null +++ b/roles/services/postgres/tasks/main.yml @@ -0,0 +1,23 @@ +- name: Create data folder + ansible.builtin.file: + dest: "{{ docker_dir }}/{{ directory }}" + state: directory + owner: root + group: docker + mode: '0770' + recurse: yes + +- name: Put up the postgres container + community.docker.docker_container: + name: "{{container_name}}" + image: postgres:17.4 + restart_policy: always + state: started + pull: true + ports: + - "{{ port }}:5432" + env: + POSTGRES_USER: "{{ default_user }}" + POSTGRES_PASSWORD: "{{ default_password }}" + volumes: + - "{{ docker_dir }}/{{ directory }}/data:/var/lib/postgresql/data/" \ No newline at end of file diff --git a/roles/util/mount_nfs/defaults/main.yml b/roles/util/mount_nfs/defaults/main.yml new file mode 100755 index 0000000..771dc8d --- /dev/null +++ b/roles/util/mount_nfs/defaults/main.yml @@ -0,0 +1,3 @@ +mount_host: "{{ hostvars['nas'].ansible_host }}" +share: "/mnt/ALEXANDRIA/" +mount_path: "/mnt/unspecifiedshare" \ No newline at end of file diff --git a/roles/util/mount_nfs/tasks/main.yml b/roles/util/mount_nfs/tasks/main.yml new file mode 100755 index 0000000..9173c44 --- /dev/null +++ b/roles/util/mount_nfs/tasks/main.yml @@ -0,0 +1,21 @@ +--- +- name: Ensure NFS client is installed + ansible.builtin.package: + name: nfs-common + state: present + become: true + +- name: Create mount point directory + ansible.builtin.file: + path: "{{ mount_path }}" + state: directory + mode: '0777' + become: true + +- name: Mount share + ansible.posix.mount: + src: "{{ mount_host }}:{{ share }}" + path: "{{ mount_path }}" + fstype: nfs + state: mounted + become: true \ No newline at end of file diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..bd30ef7 --- /dev/null +++ b/setup.sh @@ -0,0 +1,28 @@ +echo "**Deleting .git so you can start your own repo" +rm -rf .git +echo "***Ansible vault password***" +echo "This is used to encrypt/descrypt secrets in your vault" +echo "We'll save it to a file in ~/.[file name] so it doesn't have to be typed every time" +echo +read -p "File name (no . prefix): " ansible_vault_pass_filename +read -s -p "Password: " ansible_vault_pass + +echo $ansible_vault_pass > ~/.$ansible_vault_pass_filename +echo "vault_password_file = ~/.$ansible_vault_pass_filename" >>ansible.cfg + +echo +echo + +echo "***Ansible become password***" +echo "A lot of actions need sudo. This password will be stored in group_vars/all.yml encrypted" +read -s -p "Password: " ansible_become_pass +echo "# Sudo password for your servers" >>./group_vars/all.yml +ansible-vault encrypt_string "$ansible_become_pass" --name 'ansible_become_pass' >>./group_vars/all.yml + +echo +echo + +echo "Setup complete" +echo "You can delete setup.sh since running it again would cause issues" +echo "Config for vault password was output to ./ansible.cfg" +echo "Config for sudo (become) password was output to ./group_vars/all.yml"