Initial commit - homelab infrastructure

This commit is contained in:
Elewyn 2026-04-09 14:46:59 +02:00
commit bd7bbf3392
27 changed files with 1234 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
cat > ~/homelab/.gitignore << 'EOF'
.env
*.tfstate
*.tfstate.backup
.terraform/

11
ansible/ansible.cfg Normal file
View file

@ -0,0 +1,11 @@
[defaults]
inventory = inventory/hosts.yml
remote_user = ansible
private_key_file = ~/.ssh/homelab
host_key_checking = False
retry_files_enabled = False
[privilege_escalation]
become = True
become_method = sudo
become_user = root

View file

@ -0,0 +1,27 @@
$ANSIBLE_VAULT;1.1;AES256
64613363653338393162363864326531316465383137313239313439343664303939393164623533
6430616230323436323065323164346537373235306166300a646366316163383464376165633538
61343362613639343366353962353433323861626239376564663136323262323837333937356636
6133633932396336620a613237343731623432336530373334613737343063396339663862663762
33626662633865353634643036633333653133666235613737346161663766316465336563306165
36633033353132646233383765396266393232346235393033313134376164363736633565623631
35653235316562656232393331376136303135636363303832626236663936343939653835316437
34393439666365333739386139363861616231323463616666663231353433663164346339343136
65333337653330646463373834656131623165653832623738376430623131393838356364313366
36623534303966353965383365306265326630363161646231336639663966383233373433633366
31623635356234303938663362623232373739373966396230383562303436303736386163336463
35383238376637333934363034363134313162646563343666623062366230303466656635353964
32303432323666373962656638333838333933353163616330613765666539613932336338353033
30363031303134626131333731323334623735386438393930663261616435306664633837653635
36663362336231636461363331363033363434623763623131623338363964333638346463623839
39666536633936396235323738353731323361656166396134646462626134643530343636386238
33623864623437643132383130643962623762626130333536646131313031393333663662393733
62643466346330316463393833343931633332613161613963646432613832323963623465633330
36666466363130313536613861373665376633323432316337353431663665313762653663666135
66623734363836373166643732646338643532343762653937326161313265326364626233373538
33383235303531323966633839623763313637326231356165663365336231623564343734626639
37313838366237333562643334386631353730386334373539356430313334656339303536323431
35636431333932356535386461336138316432313337613463393965633733356164663866666463
30313366313531326338323632626363636431643631326139663966613065376163366231613238
66373130666461373566396334386534353139313239353163623735636461386162313134393837
3733663963653135323065356163316434323465613266643837

View file

@ -0,0 +1,28 @@
all:
vars:
ansible_python_interpreter: /usr/bin/python3
# User admin a creer sur toutes les VMs
admin_user: Elewyn
# IP du NAS QNAP pour les montages NFS
nas_ip: 192.168.1.208
children:
gateway:
hosts:
vm-gateway:
ansible_host: 192.168.1.254
forgejo:
hosts:
vm-forgejo:
ansible_host: 192.168.1.50
nextcloud:
hosts:
vm-nextcloud:
ansible_host: 192.168.1.51
tools:
hosts:
vm-tools:
ansible_host: 192.168.1.52

114
ansible/playbooks/base.yml Normal file
View file

@ -0,0 +1,114 @@
---
# Playbook de base : applique sur TOUTES les VMs
# - Mise a jour systeme
# - Creation user admin Elewyn
# - Hardening SSH
# - Installation qemu-guest-agent (integration Proxmox)
# - Paquets utilitaires
- name: Configuration de base des VMs
hosts: all
become: true
tasks:
# -- Mise a jour systeme --
- name: Mise a jour des paquets
ansible.builtin.dnf:
name: "*"
state: latest
update_cache: true
tags: [update]
# -- Installation paquets de base --
- name: Installation des paquets utilitaires
ansible.builtin.dnf:
name:
- qemu-guest-agent
- vim
- curl
- wget
- tar
- nfs-utils
- bash-completion
- python3
state: present
tags: [packages]
- name: Activation qemu-guest-agent
ansible.builtin.systemd:
name: qemu-guest-agent
state: started
enabled: true
tags: [packages]
# -- Creation utilisateur Elewyn --
- name: Creation du groupe {{ admin_user }}
ansible.builtin.group:
name: "{{ admin_user }}"
state: present
tags: [users]
- name: Creation de l'utilisateur {{ admin_user }}
ansible.builtin.user:
name: "{{ admin_user }}"
group: "{{ admin_user }}"
groups: wheel
shell: /bin/bash
create_home: true
password: "{{ vault_admin_password | password_hash('sha512') }}"
state: present
tags: [users]
- name: Cle SSH pour {{ admin_user }}
ansible.posix.authorized_key:
user: "{{ admin_user }}"
key: "{{ lookup('file', '~/.ssh/homelab.pub') }}"
state: present
tags: [users]
# wheel peut sudo sans mot de passe (deja par defaut sur Rocky, on s'assure)
- name: Sudo sans mot de passe pour wheel
ansible.builtin.lineinfile:
path: /etc/sudoers.d/wheel-nopasswd
line: "%wheel ALL=(ALL) NOPASSWD: ALL"
create: true
mode: "0440"
validate: "visudo -cf %s"
tags: [users]
# -- Hardening SSH --
- name: Desactiver l'authentification par mot de passe SSH
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: "^#?PasswordAuthentication"
line: "PasswordAuthentication no"
notify: restart sshd
tags: [ssh]
- name: Desactiver le login root SSH
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: "^#?PermitRootLogin"
line: "PermitRootLogin no"
notify: restart sshd
tags: [ssh]
# -- Firewalld --
- name: Installation firewalld
ansible.builtin.dnf:
name: firewalld
state: present
tags: [firewall]
- name: Activer firewalld
ansible.builtin.systemd:
name: firewalld
state: started
enabled: true
tags: [firewall]
handlers:
- name: restart sshd
ansible.builtin.systemd:
name: sshd
state: restarted

View file

@ -0,0 +1,94 @@
---
# Installation Docker + Docker Compose
# Cible : forgejo, nextcloud, tools (pas gateway)
- name: Installation Docker
hosts: forgejo:nextcloud:tools
become: true
tasks:
- name: Installation des prerequis
ansible.builtin.dnf:
name:
- dnf-utils
- device-mapper-persistent-data
- lvm2
state: present
tags: [docker]
- name: Ajout du repo Docker CE
ansible.builtin.yum_repository:
name: docker-ce
description: Docker CE Stable
baseurl: https://download.docker.com/linux/centos/$releasever/$basearch/stable
gpgcheck: true
gpgkey: https://download.docker.com/linux/centos/gpg
enabled: true
tags: [docker]
- name: Installation Docker CE + Compose plugin
ansible.builtin.dnf:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
state: present
tags: [docker]
- name: Chargement des modules kernel requis par Docker
community.general.modprobe:
name: "{{ item }}"
state: present
loop:
- overlay
- br_netfilter
tags: [docker]
- name: Persistance des modules kernel au reboot
ansible.builtin.copy:
dest: /etc/modules-load.d/docker.conf
content: |
overlay
br_netfilter
mode: "0644"
tags: [docker]
- name: Parametres sysctl requis par Docker
ansible.posix.sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
sysctl_set: true
reload: true
loop:
- { key: "net.bridge.bridge-nf-call-iptables", value: "1" }
- { key: "net.bridge.bridge-nf-call-ip6tables", value: "1" }
- { key: "net.ipv4.ip_forward", value: "1" }
tags: [docker]
- name: Demarrage containerd
ansible.builtin.systemd:
name: containerd
state: started
enabled: true
tags: [docker]
- name: Demarrage et activation Docker
ansible.builtin.systemd:
name: docker
state: started
enabled: true
tags: [docker]
- name: Ajout de {{ admin_user }} au groupe docker
ansible.builtin.user:
name: "{{ admin_user }}"
groups: docker
append: true
tags: [docker]
handlers:
- name: reload firewalld
ansible.builtin.systemd:
name: firewalld
state: reloaded

View file

@ -0,0 +1,63 @@
---
# VM Forgejo : forge logicielle legere
# Deploie Forgejo + PostgreSQL via Docker Compose
- name: Deploiement Forgejo
hosts: forgejo
become: true
tasks:
- name: Creation des repertoires Forgejo
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ admin_user }}"
group: "{{ admin_user }}"
mode: "0755"
loop:
- /opt/forgejo
- /opt/forgejo/data
- /opt/forgejo/postgres
tags: [forgejo]
- name: Deploiement docker-compose Forgejo
ansible.builtin.copy:
src: ../../docker/forgejo/docker-compose.yml
dest: /opt/forgejo/docker-compose.yml
owner: "{{ admin_user }}"
group: "{{ admin_user }}"
mode: "0644"
tags: [forgejo]
- name: Deploiement .env Forgejo
ansible.builtin.template:
src: forgejo.env.j2
dest: /opt/forgejo/.env
owner: "{{ admin_user }}"
group: "{{ admin_user }}"
mode: "0600"
tags: [forgejo]
- name: Demarrage Forgejo
community.docker.docker_compose_v2:
project_src: /opt/forgejo
state: present
tags: [forgejo]
# Port 3000 (web) + 2222 (SSH Git)
- name: Ouverture ports Forgejo
ansible.posix.firewalld:
port: "{{ item }}"
permanent: true
state: enabled
loop:
- 3000/tcp
- 2222/tcp
notify: reload firewalld
tags: [forgejo, firewall]
handlers:
- name: reload firewalld
ansible.builtin.systemd:
name: firewalld
state: reloaded

View file

@ -0,0 +1,113 @@
---
# VM Gateway : WireGuard + Caddy
# Point d'entree reseau depuis le VPS
- name: Configuration gateway
hosts: gateway
become: true
tasks:
# -- WireGuard --
- name: Installation WireGuard
ansible.builtin.dnf:
name:
- wireguard-tools
state: present
tags: [wireguard]
- name: Activation IP forwarding
ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: "1"
sysctl_set: true
reload: true
tags: [wireguard]
- name: Creation du repertoire WireGuard
ansible.builtin.file:
path: /etc/wireguard
state: directory
mode: "0700"
tags: [wireguard]
# La config WireGuard sera a personnaliser avec les cles
# generees et l'IP du VPS (phase 5)
- name: Deploiement config WireGuard (template)
ansible.builtin.template:
src: wg0.conf.j2
dest: /etc/wireguard/wg0.conf
mode: "0600"
notify: restart wireguard
when: wireguard_configured | default(false)
tags: [wireguard]
# -- Caddy --
- name: Installation dnf-plugins-core (requis pour copr)
ansible.builtin.dnf:
name: dnf-plugins-core
state: present
tags: [caddy]
- name: Activation du repo COPR Caddy
ansible.builtin.shell: dnf copr enable -y @caddy/caddy
args:
creates: /etc/yum.repos.d/_copr:copr.fedorainfracloud.org:group_caddy:caddy.repo
tags: [caddy]
- name: Installation Caddy
ansible.builtin.dnf:
name: caddy
state: present
tags: [caddy]
- name: Deploiement Caddyfile
ansible.builtin.copy:
src: ../../docker/gateway/Caddyfile
dest: /etc/caddy/Caddyfile
mode: "0644"
notify: restart caddy
tags: [caddy]
- name: Activation Caddy
ansible.builtin.systemd:
name: caddy
state: started
enabled: true
tags: [caddy]
# -- Firewall --
- name: Ouverture ports HTTP/HTTPS
ansible.posix.firewalld:
service: "{{ item }}"
permanent: true
state: enabled
loop:
- http
- https
notify: reload firewalld
tags: [firewall]
- name: Ouverture port WireGuard
ansible.posix.firewalld:
port: 51820/udp
permanent: true
state: enabled
notify: reload firewalld
tags: [firewall]
handlers:
- name: restart wireguard
ansible.builtin.systemd:
name: wg-quick@wg0
state: restarted
enabled: true
- name: restart caddy
ansible.builtin.systemd:
name: caddy
state: restarted
- name: reload firewalld
ansible.builtin.systemd:
name: firewalld
state: reloaded

View file

@ -0,0 +1,97 @@
---
# VM Nextcloud : cloud personnel
# Deploie Nextcloud + PostgreSQL via Docker Compose
# Les donnees utilisateur sont stockees sur le QNAP via NFS
- name: Deploiement Nextcloud
hosts: nextcloud
become: true
tasks:
# -- Montage NFS vers le QNAP pour les donnees --
- name: Creation du repertoire parent /mnt/nas
ansible.builtin.file:
path: /mnt/nas
state: directory
mode: "0755"
tags: [nfs]
- name: Creation du point de montage NFS
ansible.builtin.shell: mkdir -p /mnt/nas/nextcloud-data
args:
creates: /mnt/nas/nextcloud-data
tags: [nfs]
- name: Montage NFS QNAP pour les donnees Nextcloud
ansible.posix.mount:
src: "{{ nas_ip }}:/nextcloud-data"
path: /mnt/nas/nextcloud-data
fstype: nfs
opts: defaults,noatime
state: mounted
tags: [nfs]
# -- Deploiement Nextcloud --
- name: Creation des repertoires Nextcloud
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ admin_user }}"
group: "{{ admin_user }}"
mode: "0755"
loop:
- /opt/nextcloud
- /opt/nextcloud/postgres
tags: [nextcloud]
- name: Deploiement docker-compose Nextcloud
ansible.builtin.copy:
src: ../../docker/nextcloud/docker-compose.yml
dest: /opt/nextcloud/docker-compose.yml
owner: "{{ admin_user }}"
group: "{{ admin_user }}"
mode: "0644"
tags: [nextcloud]
- name: Deploiement .env Nextcloud
ansible.builtin.template:
src: nextcloud.env.j2
dest: /opt/nextcloud/.env
owner: "{{ admin_user }}"
group: "{{ admin_user }}"
mode: "0600"
tags: [nextcloud]
- name: Demarrage Nextcloud
community.docker.docker_compose_v2:
project_src: /opt/nextcloud
state: present
tags: [nextcloud]
- name: Config personnalisee Nextcloud (permissions NFS)
ansible.builtin.copy:
src: ../../docker/nextcloud/custom.config.php
dest: /opt/nextcloud/custom.config.php
mode: "0644"
tags: [nextcloud]
- name: Injection config dans le container Nextcloud
ansible.builtin.shell: >
docker cp /opt/nextcloud/custom.config.php
nextcloud:/var/www/html/config/custom.config.php
changed_when: false
tags: [nextcloud]
- name: Ouverture port Nextcloud
ansible.posix.firewalld:
port: 8080/tcp
permanent: true
state: enabled
notify: reload firewalld
tags: [nextcloud, firewall]
handlers:
- name: reload firewalld
ansible.builtin.systemd:
name: firewalld
state: reloaded

View file

@ -0,0 +1,2 @@
FORGEJO_DB_PASSWORD={{ vault_forgejo_db_password }}
FORGEJO_DOMAIN={{ vault_forgejo_domain }}

View file

@ -0,0 +1,4 @@
NEXTCLOUD_DB_PASSWORD={{ vault_nextcloud_db_password }}
NEXTCLOUD_ADMIN_USER={{ vault_nextcloud_admin_user }}
NEXTCLOUD_ADMIN_PASSWORD={{ vault_nextcloud_admin_password }}
NEXTCLOUD_DOMAIN={{ vault_nextcloud_domain }}

View file

@ -0,0 +1,14 @@
# WireGuard - A configurer a la phase 5 (VPS)
# Generer les cles : wg genkey | tee privatekey | wg pubkey > publickey
[Interface]
Address = 10.0.0.2/24
PrivateKey = {{ wireguard_private_key }}
ListenPort = 51820
[Peer]
# VPS
PublicKey = {{ wireguard_vps_public_key }}
Endpoint = {{ wireguard_vps_ip }}:51820
AllowedIPs = 10.0.0.1/32
PersistentKeepalive = 25

View file

@ -0,0 +1,46 @@
---
# VM Tools : petits utilitaires
# Stirling PDF (convertisseur de fichiers)
- name: Deploiement outils
hosts: tools
become: true
tasks:
- name: Creation du repertoire tools
ansible.builtin.file:
path: /opt/tools
state: directory
owner: "{{ admin_user }}"
group: "{{ admin_user }}"
mode: "0755"
tags: [tools]
- name: Deploiement docker-compose tools
ansible.builtin.copy:
src: ../../docker/tools/docker-compose.yml
dest: /opt/tools/docker-compose.yml
owner: "{{ admin_user }}"
group: "{{ admin_user }}"
mode: "0644"
tags: [tools]
- name: Demarrage des outils
community.docker.docker_compose_v2:
project_src: /opt/tools
state: present
tags: [tools]
- name: Ouverture port Stirling PDF
ansible.posix.firewalld:
port: 8081/tcp
permanent: true
state: enabled
notify: reload firewalld
tags: [tools, firewall]
handlers:
- name: reload firewalld
ansible.builtin.systemd:
name: firewalld
state: reloaded

24
ansible/site.yml Normal file
View file

@ -0,0 +1,24 @@
---
# Site.yml - Orchestre tous les playbooks dans l'ordre
# Usage :
# Tout deployer : ansible-playbook site.yml
# Une VM seule : ansible-playbook site.yml --limit forgejo
# Un role seul : ansible-playbook site.yml --tags docker
- name: Configuration de base (toutes les VMs)
ansible.builtin.import_playbook: playbooks/base.yml
- name: Installation Docker
ansible.builtin.import_playbook: playbooks/docker.yml
- name: Configuration gateway
ansible.builtin.import_playbook: playbooks/gateway.yml
- name: Deploiement Forgejo
ansible.builtin.import_playbook: playbooks/forgejo.yml
- name: Deploiement Nextcloud
ansible.builtin.import_playbook: playbooks/nextcloud.yml
- name: Deploiement outils
ansible.builtin.import_playbook: playbooks/tools.yml

View file

@ -0,0 +1,3 @@
# Copier en .env et renseigner les valeurs
FORGEJO_DB_PASSWORD=change-moi
FORGEJO_DOMAIN=forgejo.local

View file

@ -0,0 +1,43 @@
services:
forgejo:
image: codeberg.org/forgejo/forgejo:9
container_name: forgejo
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
- USER_UID=1000
- USER_GID=1000
# -- Base de donnees --
- FORGEJO__database__DB_TYPE=postgres
- FORGEJO__database__HOST=postgres:5432
- FORGEJO__database__NAME=forgejo
- FORGEJO__database__USER=forgejo
- FORGEJO__database__PASSWD=${FORGEJO_DB_PASSWORD}
# -- Serveur --
- FORGEJO__server__DOMAIN=${FORGEJO_DOMAIN:-forgejo.local}
- FORGEJO__server__SSH_DOMAIN=${FORGEJO_DOMAIN:-forgejo.local}
- FORGEJO__server__SSH_PORT=2222
- FORGEJO__server__ROOT_URL=http://${FORGEJO_DOMAIN:-forgejo.local}:3000/
ports:
- "3000:3000"
- "2222:22"
volumes:
- ./data:/data
postgres:
image: postgres:16-alpine
container_name: forgejo-db
restart: unless-stopped
environment:
- POSTGRES_DB=forgejo
- POSTGRES_USER=forgejo
- POSTGRES_PASSWORD=${FORGEJO_DB_PASSWORD}
volumes:
- ./postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U forgejo"]
interval: 10s
timeout: 5s
retries: 5

12
docker/gateway/Caddyfile Normal file
View file

@ -0,0 +1,12 @@
# Caddyfile - Reverse proxy interne
# A adapter avec ton NDD une fois achete (phase 5)
# Forgejo
:3000 {
reverse_proxy 192.168.1.50:3000
}
# Nextcloud
:8080 {
reverse_proxy 192.168.1.51:8080
}

View file

@ -0,0 +1,5 @@
# Copier en .env et renseigner les valeurs
NEXTCLOUD_DB_PASSWORD=change-moi
NEXTCLOUD_ADMIN_USER=Elewyn
NEXTCLOUD_ADMIN_PASSWORD=change-moi
NEXTCLOUD_DOMAIN=nextcloud.local

View file

@ -0,0 +1,4 @@
<?php
$CONFIG = [
'check_data_directory_permissions' => false,
];

View file

@ -0,0 +1,42 @@
services:
nextcloud:
image: nextcloud:30-apache
container_name: nextcloud
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
- POSTGRES_HOST=postgres
- POSTGRES_DB=nextcloud
- POSTGRES_USER=nextcloud
- POSTGRES_PASSWORD=${NEXTCLOUD_DB_PASSWORD}
- NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER:-admin}
- NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD}
- NEXTCLOUD_TRUSTED_DOMAINS=${NEXTCLOUD_DOMAIN:-nextcloud.local}
ports:
- "8080:80"
volumes:
# Donnees utilisateur sur le QNAP via NFS
- /mnt/nas/nextcloud-data:/var/www/html/data
# Config locale pour la performance
- nextcloud-app:/var/www/html
postgres:
image: postgres:16-alpine
container_name: nextcloud-db
restart: unless-stopped
environment:
- POSTGRES_DB=nextcloud
- POSTGRES_USER=nextcloud
- POSTGRES_PASSWORD=${NEXTCLOUD_DB_PASSWORD}
volumes:
- ./postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U nextcloud"]
interval: 10s
timeout: 5s
retries: 5
volumes:
nextcloud-app:

View file

@ -0,0 +1,14 @@
services:
stirling-pdf:
image: frooodle/s-pdf:latest
container_name: stirling-pdf
restart: unless-stopped
ports:
- "8081:8080"
volumes:
- stirling-data:/usr/share/tessdata
environment:
- DOCKER_ENABLE_SECURITY=false
volumes:
stirling-data:

223
homelab.md Normal file
View file

@ -0,0 +1,223 @@
# Projet Homelab
## Infrastructure physique
### HPE ML110 - Proxmox (192.168.1.242)
- **CPU** : 16 x Intel Xeon Silver 4110 @ 2.10GHz (1 Socket)
- **RAM** : 32 Go (upgrade en commande : +16 Go HP PC4 1RX4 2666 MHz → 48 Go total)
- **CPU upgrade en commande** : Intel Xeon Gold 5120 (14 cores / 28 threads @ 2.20GHz)
- **Boot** : EFI
- **Kernel** : Linux 6.5.11-8-pve
- **Reseau** : vmbr0 (Linux bridge)
- **Disques** :
- /dev/sda - 2 To
- /dev/sda1 - biosboot (1 Mo)
- /dev/sda2 - EFI (1 Go)
- /dev/sda3 - LVM 31 Go (local + local-lvm)
- /dev/sda4 - ext4 104 Go "ISO"
- /dev/sda5 - LVM 1.86 To "VMS"
### QNAP TS-431P2 (192.168.1.208)
- **CPU** : Alpine AL-314 (ARM Cortex-A15 quad-core)
- **RAM** : 8 Go
- **Disques** : 4 baies, RAID 1, disque remplace + RAID reconstruit
- **OS** : QTS (on ne touche pas)
- **Services actuels** : Plex, bots Discord, NFS/SMB
### Reseau
- Box Orange : 192.168.1.1 (gateway + DNS)
- Masque : /24
- Pas de VLAN (reseau basique)
---
## VMs existantes
| VM | RAM | vCPU | Disk | Role |
|----|-----|------|------|------|
| VM-DEDICATED | 20 Go | 8 | 40 Go | Serveurs de jeu |
---
## VMs a creer (Terraform - provider bpg/proxmox)
| VM | VMID | IP | RAM | vCPU | Disk | Role |
|----|------|----|-----|------|------|------|
| gateway | 200 | 192.168.1.254 | 512 Mo | 1 | 8 Go | WireGuard + Caddy (reverse proxy) |
| forgejo | 201 | 192.168.1.50 | 1 Go | 2 | 20 Go | Forge logicielle - http://192.168.1.50:3000 |
| nextcloud | 202 | 192.168.1.51 | 4 Go | 3 | 20 Go | Cloud personnel - http://192.168.1.51:8080 |
| tools | 203 | 192.168.1.52 | 2 Go | 2 | 10 Go | Stirling PDF - http://192.168.1.52:8081 |
| **Total** | | | **5.5 Go** | **7** | **58 Go** | |
| **Reste libre** | | | **~4.5 Go** | | | Reserve k3s |
Template cloud-init : Rocky Linux 9 (VMID 9000)
---
## Repartition du stockage
| Donnee | Emplacement | Raison |
|--------|-------------|--------|
| OS des VMs + disques virtuels | ML110 (LVM "VMS") | Performance I/O |
| BDD PostgreSQL (Forgejo, Nextcloud) | ML110 (local) | BDD sur NFS = lent et risque |
| Fichiers Nextcloud (data utilisateur) | QNAP via NFS | Centralise, sauvegardable |
| Saves serveurs de jeu | QNAP via NFS | Backups |
| Backups VMs (vzdump) | QNAP via NFS | Proxmox backup natif |
| Media (Plex) | QNAP (local) | Deja en place |
---
## QNAP - Shares NFS a creer
| Share | Usage | Acces restreint a |
|-------|-------|--------------------|
| nextcloud-data | Donnees Nextcloud | 192.168.1.51 |
| backups | Backups Proxmox | 192.168.1.242 |
| game-saves | Saves serveurs de jeu | 192.168.1.x (VM dedicated) |
---
## Poste de pilotage
- **PC Gaming Windows 11** : VSCode + Claude Code, WSL2 Debian
- **Laptop Linux Mint** : alternative (non disponible actuellement)
- **WSL2 Debian** : Terraform, Ansible, kubectl, Git, cles SSH
---
## Architecture reseau cible (avec VPS)
```
Internet --> [VPS Hetzner CX22 ~4 EUR/mois]
| Caddy (reverse proxy + TLS Let's Encrypt)
| CrowdSec
|
WireGuard tunnel (10.0.0.0/24)
|
[VM gateway - 192.168.1.254]
|
+------+-------+-------+
| | | |
Forgejo Nextcloud Plex Tools
(.50) (.51) (QNAP) (.52)
```
---
## Stack technique
| Outil | Usage |
|-------|-------|
| **Terraform** (bpg/proxmox) | Provisionnement des VMs |
| **Ansible** | Configuration des VMs |
| **Docker Compose** | Deploiement des services |
| **WireGuard** | VPN entre VPS et homelab |
| **Caddy** | Reverse proxy + TLS auto |
| **Forgejo** | Forge logicielle (syntaxe GitHub Actions) |
| **Nextcloud** | Cloud personnel |
| **Stirling PDF** | Convertisseur de fichiers |
| **k3s** | Kubernetes (phase future) |
---
## Arborescence du repo
```
~/homelab/
├── .env # Secrets Terraform (jamais commit)
├── .gitignore
├── terraform/proxmox/
│ ├── main.tf # 4 VMs via for_each
│ ├── variables.tf
│ ├── outputs.tf
│ └── terraform.tfvars
├── ansible/
│ ├── ansible.cfg
│ ├── inventory/hosts.yml
│ ├── templates/wg0.conf.j2
│ ├── site.yml # Orchestre tout
│ └── playbooks/
│ ├── base.yml # User Elewyn, SSH hardening, packages
│ ├── docker.yml # Docker sur forgejo/nextcloud/tools
│ ├── gateway.yml # WireGuard + Caddy
│ ├── forgejo.yml # Forgejo + PostgreSQL
│ ├── nextcloud.yml # Nextcloud + montage NFS QNAP
│ └── tools.yml # Stirling PDF
└── docker/
├── gateway/Caddyfile
├── forgejo/
│ ├── docker-compose.yml
│ └── .env.example
├── nextcloud/
│ ├── docker-compose.yml
│ └── .env.example
└── tools/docker-compose.yml
```
---
## Users sur les VMs
| User | Role | Auth |
|------|------|------|
| ansible | Deploiement Ansible (cloud-init) | Cle SSH homelab |
| Elewyn | Admin (sudo via wheel) | Cle SSH homelab |
| root | Desactive en SSH | - |
---
## Plan d'action
### Phase 1 - Fondations (FAIT)
- [x] WSL2 Debian installe
- [x] Terraform, Ansible, Git installes
- [x] Cle SSH homelab generee
- [x] Template cloud-init Rocky 9 (VMID 9000)
- [x] Token API Proxmox (terraform@pam!provider)
- [x] Fichiers Terraform + Ansible ecrits
### Phase 2 - Provisionnement VMs
- [x] terraform apply (creer les 4 VMs)
- [x] Verifier acces SSH aux VMs (ping + ansible ping OK)
### Phase 3 - Configuration (Ansible)
- [x] base.yml (users, SSH hardening, firewalld, qemu-agent)
- [x] Installer collections Ansible (ansible.posix, community.docker, community.general)
- [x] Ansible Vault (secrets BDD chiffres)
- [x] ansible-playbook site.yml (docker, services)
### Phase 4 - Services
- [x] Forgejo + PostgreSQL deploye
- [x] Stirling PDF deploye
- [x] Nextcloud + PostgreSQL deploye
- [x] NFS QNAP monte (nextcloud-data, backups crees sur QNAP)
### Phase 5 - Exposition externe
- [ ] Acheter NDD (~7 EUR/an)
- [ ] Louer VPS Hetzner CX22 (~4 EUR/mois)
- [ ] WireGuard VPS <-> gateway
- [ ] Caddy reverse proxy + TLS
- [ ] DNS Cloudflare
### Phase 6 - QNAP
- [x] Creer shares NFS (nextcloud-data)
- [x] Creer share NFS backups
- [x] Configurer backups vzdump Proxmox -> NFS (storage qnap-backups, schedule nuit)
- [x] Remplacer disque HS + RAID reconstruit
### Phase 7 - Kubernetes (futur)
- [ ] VM k3s single-node (6 Go RAM)
- [ ] Migration progressive des services
- [ ] ArgoCD (GitOps)
- [ ] Monitoring (Grafana/Loki/Prometheus)
---
## Budget
| Poste | Cout |
|-------|------|
| NDD .fr | ~7 EUR/an |
| VPS Hetzner CX22 | ~48 EUR/an |
| Disque QNAP remplacement | ~20-30 EUR (une fois) |
| **Total premiere annee** | **~80 EUR** |

View file

@ -0,0 +1,24 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/bpg/proxmox" {
version = "0.99.0"
constraints = "~> 0.78"
hashes = [
"h1:UyNgTFGjDo0Kb9uIM/XrhO0F09eALXyaRqLsFM16XKo=",
"zh:2ac2a659c1d9ceb180337ccc2ad86427383420230b2cd298f821cba9cbf5645c",
"zh:2f8145d697bd4efbc1ffd3346d686866e104b21303dec3ff2ce0f9f501ddc9be",
"zh:2fd459efb1b6658891e290111ca85d906eef42fe27c21af96b95442da8d461de",
"zh:3365c9be6501a9018b6b0dd599b48bdef4277f6509b299dde5f25d559d774068",
"zh:4088709a948886a22d92f98ba0782481001e9b94a3027cb3a3b39a7ba40be8d2",
"zh:537e9c663bc03bc416e615d94c54ebc6a0cb8453029f12344eeefa14fe2a20db",
"zh:5871bc9d5d3c3ec7f32b67a63e8127e29e7e92f6c1a9b8d1f98f2f6e9be263ab",
"zh:680239bd34a8a1c874a87a48d1062a69338bbd59fddf3c41e605e0fc2a714842",
"zh:6fd3445e460361d5ef5d6bf34c731ca7dc84ea7c2e39a2de50546f0f175f089a",
"zh:e3d0b208f7b982dc265c7daa77b3cb9ace9789b4ed4ad55107f03a6624be6beb",
"zh:e406403644ab5ad85478bfe25db9eeabd28a8c926bf580ba1a0ff37fdf61a05d",
"zh:f020a472d78da34c3ebabed07e9019da778e107e215e2e66f4f2e99b033af304",
"zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597",
"zh:f7afb6b0486c09b2b8e36576e9d64ca36f31940fac574740ecf8ccdee9febb6c",
]
}

122
terraform/proxmox/main.tf Normal file
View file

@ -0,0 +1,122 @@
# Provider Proxmox (bpg) - plus moderne, pas le bug user list de telmate
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "~> 0.78"
}
}
}
provider "proxmox" {
endpoint = var.proxmox_url
api_token = var.proxmox_api_token
insecure = true
ssh {
agent = false
}
}
# --- Locals : configuration centralisee des VMs ---
locals {
vms = {
gateway = {
vmid = 200
cores = 1
memory = 512
balloon = 256
disk = 10
ip = var.gateway_ip
}
forgejo = {
vmid = 201
cores = 2
memory = 1024
balloon = 512
disk = 20
ip = var.forgejo_ip
}
nextcloud = {
vmid = 202
cores = 3
memory = 4096
balloon = 2048
disk = 20
ip = var.nextcloud_ip
}
tools = {
vmid = 203
cores = 2
memory = 2048
balloon = 1024
disk = 10
ip = var.tools_ip
}
}
}
# --- VMs generees dynamiquement par clonage du template ---
resource "proxmox_virtual_environment_vm" "vm" {
for_each = local.vms
name = each.key
node_name = var.proxmox_node
vm_id = each.value.vmid
clone {
vm_id = var.template_vmid
}
scsi_hardware = "virtio-scsi-single"
cpu {
cores = each.value.cores
sockets = 1
type = "x86-64-v2-AES"
}
memory {
dedicated = each.value.memory
floating = each.value.balloon
}
agent {
enabled = true
timeout = "10s"
}
disk {
interface = "scsi0"
size = each.value.disk
datastore_id = var.storage_name
}
network_device {
bridge = var.network_bridge
model = "virtio"
}
initialization {
ip_config {
ipv4 {
address = "${each.value.ip}/24"
gateway = var.network_gateway
}
}
dns {
servers = [var.dns_server]
}
user_account {
username = var.ci_user
keys = [var.ssh_public_key]
}
}
lifecycle {
ignore_changes = [
initialization,
network_device
]
}
}

View file

@ -0,0 +1,6 @@
output "vm_ips" {
description = "IPs des VMs creees"
value = {
for name, vm in proxmox_virtual_environment_vm.vm : name => local.vms[name].ip
}
}

View file

@ -0,0 +1,10 @@
proxmox_node = "SRV-PROXMOX"
storage_name = "VMS"
network_bridge = "vmbr0"
network_gateway = "192.168.1.1"
dns_server = "192.168.1.1"
gateway_ip = "192.168.1.254"
forgejo_ip = "192.168.1.50"
nextcloud_ip = "192.168.1.51"
tools_ip = "192.168.1.52"

View file

@ -0,0 +1,84 @@
# --- Proxmox ---
variable "proxmox_url" {
description = "URL de l'API Proxmox (ex: https://192.168.1.242:8006)"
type = string
}
variable "proxmox_api_token" {
description = "Token API au format user@realm!tokenid=secret"
type = string
sensitive = true
}
variable "proxmox_node" {
description = "Nom du node Proxmox"
type = string
default = "SRV-PROXMOX"
}
variable "template_vmid" {
description = "VMID du template cloud-init a cloner"
type = number
default = 9000
}
variable "storage_name" {
description = "Nom du stockage pour les disques VM"
type = string
default = "VMS"
}
# --- Reseau ---
variable "network_bridge" {
description = "Bridge Proxmox"
type = string
default = "vmbr0"
}
variable "network_gateway" {
description = "Gateway du reseau (box)"
type = string
default = "192.168.1.1"
}
variable "dns_server" {
description = "Serveur DNS"
type = string
default = "192.168.1.1"
}
variable "gateway_ip" {
description = "IP de la VM gateway"
type = string
default = "192.168.1.254"
}
variable "forgejo_ip" {
description = "IP de la VM Forgejo"
type = string
default = "192.168.1.50"
}
variable "nextcloud_ip" {
description = "IP de la VM Nextcloud"
type = string
default = "192.168.1.51"
}
variable "tools_ip" {
description = "IP de la VM tools"
type = string
default = "192.168.1.52"
}
# --- Cloud-init ---
variable "ci_user" {
description = "Utilisateur cree par cloud-init"
type = string
default = "ansible"
}
variable "ssh_public_key" {
description = "Cle publique SSH injectee par cloud-init"
type = string
}