commit bd7bbf3392be9ab9597afee2d72b782698cc2322 Author: Elewyn Date: Thu Apr 9 14:46:59 2026 +0200 Initial commit - homelab infrastructure diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76c89bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +cat > ~/homelab/.gitignore << 'EOF' +.env +*.tfstate +*.tfstate.backup +.terraform/ diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg new file mode 100644 index 0000000..21230ff --- /dev/null +++ b/ansible/ansible.cfg @@ -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 diff --git a/ansible/inventory/group_vars/all/vault.yml b/ansible/inventory/group_vars/all/vault.yml new file mode 100644 index 0000000..caab6a3 --- /dev/null +++ b/ansible/inventory/group_vars/all/vault.yml @@ -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 diff --git a/ansible/inventory/hosts.yml b/ansible/inventory/hosts.yml new file mode 100644 index 0000000..eafbde5 --- /dev/null +++ b/ansible/inventory/hosts.yml @@ -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 diff --git a/ansible/playbooks/base.yml b/ansible/playbooks/base.yml new file mode 100644 index 0000000..d0a643d --- /dev/null +++ b/ansible/playbooks/base.yml @@ -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 diff --git a/ansible/playbooks/docker.yml b/ansible/playbooks/docker.yml new file mode 100644 index 0000000..9f756b0 --- /dev/null +++ b/ansible/playbooks/docker.yml @@ -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 diff --git a/ansible/playbooks/forgejo.yml b/ansible/playbooks/forgejo.yml new file mode 100644 index 0000000..6d7e3dd --- /dev/null +++ b/ansible/playbooks/forgejo.yml @@ -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 diff --git a/ansible/playbooks/gateway.yml b/ansible/playbooks/gateway.yml new file mode 100644 index 0000000..bde4a93 --- /dev/null +++ b/ansible/playbooks/gateway.yml @@ -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 diff --git a/ansible/playbooks/nextcloud.yml b/ansible/playbooks/nextcloud.yml new file mode 100644 index 0000000..d672c19 --- /dev/null +++ b/ansible/playbooks/nextcloud.yml @@ -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 diff --git a/ansible/playbooks/templates/forgejo.env.j2 b/ansible/playbooks/templates/forgejo.env.j2 new file mode 100644 index 0000000..69ce454 --- /dev/null +++ b/ansible/playbooks/templates/forgejo.env.j2 @@ -0,0 +1,2 @@ +FORGEJO_DB_PASSWORD={{ vault_forgejo_db_password }} +FORGEJO_DOMAIN={{ vault_forgejo_domain }} diff --git a/ansible/playbooks/templates/nextcloud.env.j2 b/ansible/playbooks/templates/nextcloud.env.j2 new file mode 100644 index 0000000..4a1b9ba --- /dev/null +++ b/ansible/playbooks/templates/nextcloud.env.j2 @@ -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 }} diff --git a/ansible/playbooks/templates/wg0.conf.j2 b/ansible/playbooks/templates/wg0.conf.j2 new file mode 100644 index 0000000..f3e072f --- /dev/null +++ b/ansible/playbooks/templates/wg0.conf.j2 @@ -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 diff --git a/ansible/playbooks/tools.yml b/ansible/playbooks/tools.yml new file mode 100644 index 0000000..5735863 --- /dev/null +++ b/ansible/playbooks/tools.yml @@ -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 diff --git a/ansible/site.yml b/ansible/site.yml new file mode 100644 index 0000000..06cfab9 --- /dev/null +++ b/ansible/site.yml @@ -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 diff --git a/docker/forgejo/.env.example b/docker/forgejo/.env.example new file mode 100644 index 0000000..ac90b49 --- /dev/null +++ b/docker/forgejo/.env.example @@ -0,0 +1,3 @@ +# Copier en .env et renseigner les valeurs +FORGEJO_DB_PASSWORD=change-moi +FORGEJO_DOMAIN=forgejo.local diff --git a/docker/forgejo/docker-compose.yml b/docker/forgejo/docker-compose.yml new file mode 100644 index 0000000..8dda2f6 --- /dev/null +++ b/docker/forgejo/docker-compose.yml @@ -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 diff --git a/docker/gateway/Caddyfile b/docker/gateway/Caddyfile new file mode 100644 index 0000000..05fcaaa --- /dev/null +++ b/docker/gateway/Caddyfile @@ -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 +} diff --git a/docker/nextcloud/.env.example b/docker/nextcloud/.env.example new file mode 100644 index 0000000..fc535bd --- /dev/null +++ b/docker/nextcloud/.env.example @@ -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 diff --git a/docker/nextcloud/custom.config.php b/docker/nextcloud/custom.config.php new file mode 100644 index 0000000..f7837fe --- /dev/null +++ b/docker/nextcloud/custom.config.php @@ -0,0 +1,4 @@ + false, +]; diff --git a/docker/nextcloud/docker-compose.yml b/docker/nextcloud/docker-compose.yml new file mode 100644 index 0000000..2809f89 --- /dev/null +++ b/docker/nextcloud/docker-compose.yml @@ -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: diff --git a/docker/tools/docker-compose.yml b/docker/tools/docker-compose.yml new file mode 100644 index 0000000..9494c71 --- /dev/null +++ b/docker/tools/docker-compose.yml @@ -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: diff --git a/homelab.md b/homelab.md new file mode 100644 index 0000000..7326c9d --- /dev/null +++ b/homelab.md @@ -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** | diff --git a/terraform/proxmox/.terraform.lock.hcl b/terraform/proxmox/.terraform.lock.hcl new file mode 100644 index 0000000..ebee056 --- /dev/null +++ b/terraform/proxmox/.terraform.lock.hcl @@ -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", + ] +} diff --git a/terraform/proxmox/main.tf b/terraform/proxmox/main.tf new file mode 100644 index 0000000..a0114fd --- /dev/null +++ b/terraform/proxmox/main.tf @@ -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 + ] + } +} diff --git a/terraform/proxmox/outputs.tf b/terraform/proxmox/outputs.tf new file mode 100644 index 0000000..681cc37 --- /dev/null +++ b/terraform/proxmox/outputs.tf @@ -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 + } +} diff --git a/terraform/proxmox/terraform.tfvars b/terraform/proxmox/terraform.tfvars new file mode 100644 index 0000000..8cc4e78 --- /dev/null +++ b/terraform/proxmox/terraform.tfvars @@ -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" + diff --git a/terraform/proxmox/variables.tf b/terraform/proxmox/variables.tf new file mode 100644 index 0000000..1f59a11 --- /dev/null +++ b/terraform/proxmox/variables.tf @@ -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 +}