feat: initial infra repo
- docker-services: 14 generic compose templates (portainer, npm, vaultwarden, gitea, jotty, nextcloud-aio, rustdesk, audiobookshelf, calibre-web, plex, erugo, jswiki, speedtest, watchtower) - install-scripts: bash scripts for Ubuntu server bootstrap, docker, portainer, npm, backup, restore - ansible: 4 playbooks (bootstrap, deploy-services, update-services, security-hardening) + roles skeleton - hermes-install: Hermes CLI setup on macOS - macos-setup: Brewfile + osx-defaults + fish-config - home-nas-tools: metadata index for NAS projects (no binaries) - docs: architecture, security, contributing - README, LICENSE (MIT), .gitignore
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
# 🎭 Ansible Playbooks
|
||||
|
||||
> Ansible role a playbooks pro nasazení a správu home infrastruktury.
|
||||
> Idempotentní, testovatelné, čitelné.
|
||||
|
||||
## 📁 Struktura
|
||||
|
||||
```
|
||||
ansible/
|
||||
├── README.md # Tento soubor
|
||||
├── ansible.cfg # Konfigurace
|
||||
├── requirements.yml # Ansible Galaxy role
|
||||
├── inventory.example.yml # Ukázkový inventory
|
||||
├── playbooks/
|
||||
│ ├── bootstrap-server.yml # Úvodní setup nového serveru
|
||||
│ ├── deploy-services.yml # Nasaď všechny Docker služby
|
||||
│ ├── update-services.yml # Update všech služeb
|
||||
│ └── security-hardening.yml # Bezpečnostní hardening
|
||||
├── roles/
|
||||
│ ├── common/ # Společné úkoly (update, packages, users)
|
||||
│ ├── docker/ # Docker + compose
|
||||
│ ├── portainer/ # Portainer compose
|
||||
│ ├── nginx-proxy/ # Nginx Proxy Manager
|
||||
│ ├── vaultwarden/ # Vaultwarden
|
||||
│ ├── gitea/ # Gitea
|
||||
│ └── ...
|
||||
└── group_vars/
|
||||
└── all.yml.example # Společné proměnné
|
||||
```
|
||||
|
||||
## 🚀 Quickstart
|
||||
|
||||
```bash
|
||||
# 1. Příprava
|
||||
pip install ansible
|
||||
ansible-galaxy install -r requirements.yml
|
||||
|
||||
# 2. Inventory
|
||||
cp inventory.example.yml inventory.yml
|
||||
nano inventory.yml # nastavit IP a SSH user
|
||||
|
||||
# 3. Test připojení
|
||||
ansible all -m ping
|
||||
|
||||
# 4. Bootstrap
|
||||
ansible-playbook -i inventory.yml playbooks/bootstrap-server.yml
|
||||
|
||||
# 5. Nasaď služby
|
||||
ansible-playbook -i inventory.yml playbooks/deploy-services.yml --extra-vars "services=[portainer,vaultwarden,gitea]"
|
||||
|
||||
# 6. Update
|
||||
ansible-playbook -i inventory.yml playbooks/update-services.yml
|
||||
```
|
||||
|
||||
## 🎯 Playbooky
|
||||
|
||||
### `bootstrap-server.yml`
|
||||
- Update OS
|
||||
- Setup deploy user (UID 1000)
|
||||
- SSH hardening
|
||||
- UFW firewall
|
||||
- fail2ban
|
||||
- Základní utility
|
||||
|
||||
### `deploy-services.yml`
|
||||
- Nasaď vybrané Docker služby
|
||||
- Idempotentní – můžeš spustit víckrát
|
||||
|
||||
### `update-services.yml`
|
||||
- `docker compose pull && up -d` pro všechny služby
|
||||
- Watchtower notifikace
|
||||
|
||||
### `security-hardening.yml`
|
||||
- Sysctl hardening
|
||||
- AppArmor profily
|
||||
- Auditd pravidla
|
||||
- Automatické security updaty
|
||||
|
||||
## 🔐 Bezpečnost
|
||||
|
||||
- **Bez hesel v inventory** – použij `ansible-vault` nebo SSH klíče
|
||||
- **Privilege escalation** – `become: true` jen kde potřeba
|
||||
- **Idempotence** – každý task kontroluje stav
|
||||
|
||||
## 📚 Dokumentace
|
||||
|
||||
- [Ansible docs](https://docs.ansible.com/)
|
||||
- [Best practices](https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html)
|
||||
@@ -0,0 +1,21 @@
|
||||
[defaults]
|
||||
inventory = inventory.yml
|
||||
roles_path = roles
|
||||
host_key_checking = False
|
||||
retry_files_enabled = False
|
||||
stdout_callback = yaml
|
||||
forks = 10
|
||||
gathering = smart
|
||||
fact_caching = jsonfile
|
||||
fact_caching_connection = local
|
||||
fact_caching_timeout = 86400
|
||||
|
||||
[privilege_escalation]
|
||||
become = True
|
||||
become_method = sudo
|
||||
become_ask_pass = False
|
||||
|
||||
[ssh_connection]
|
||||
pipelining = True
|
||||
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o ServerAliveInterval=30
|
||||
control_path = /tmp/ansible-ssh-%%h-%%p-%%r
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
# Společné proměnné pro všechny playbooks
|
||||
# Zkopíruj na group_vars/all.yml a doplň
|
||||
|
||||
# Docker
|
||||
docker_user: deploy
|
||||
docker_uid: 1000
|
||||
docker_gid: 1000
|
||||
docker_install_compose_plugin: true
|
||||
|
||||
# Služby k nasazení (seznam názvů adresářů v docker-services/)
|
||||
services_to_deploy:
|
||||
- portainer
|
||||
- nginx-proxy-manager
|
||||
- vaultwarden
|
||||
- gitea
|
||||
- jotty
|
||||
- nextcloud-aio
|
||||
- rustdesk
|
||||
- audiobookshelf
|
||||
- calibre-web
|
||||
- plex
|
||||
- erugo
|
||||
- jswiki
|
||||
- speedtest
|
||||
- watchtower
|
||||
|
||||
# NPM admin (po prvním loginu změnit)
|
||||
npm_default_email: admin@example.com
|
||||
npm_default_password: changeme
|
||||
|
||||
# Gitea admin (po instalaci)
|
||||
gitea_admin_user: admin
|
||||
gitea_admin_password: changeme
|
||||
gitea_admin_email: admin@example.com
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
# Ukázkový inventory – zkopíruj na inventory.yml a uprav
|
||||
all:
|
||||
vars:
|
||||
# SSH uživatel (deploy user, UID 1000)
|
||||
ansible_user: deploy
|
||||
# Python na cílovém systému
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
# Timezone
|
||||
timezone: Europe/Prague
|
||||
|
||||
children:
|
||||
# Skupina pro všechny home servery
|
||||
homeservers:
|
||||
hosts:
|
||||
homeserver-01:
|
||||
ansible_host: 192.168.0.148
|
||||
# homeserver-02:
|
||||
# ansible_host: 192.168.0.149
|
||||
|
||||
# Skupina pro produkční servery
|
||||
production:
|
||||
hosts:
|
||||
homeserver-01:
|
||||
ansible_host: 192.168.0.148
|
||||
|
||||
# Skupina pro test
|
||||
staging: []
|
||||
@@ -0,0 +1,145 @@
|
||||
---
|
||||
# Bootstrap nového Ubuntu serveru
|
||||
# - aktualizace systému
|
||||
# - deploy user (UID 1000)
|
||||
# - SSH hardening
|
||||
# - UFW firewall
|
||||
# - fail2ban
|
||||
# - Docker
|
||||
#
|
||||
# Použití:
|
||||
# ansible-playbook -i inventory.yml playbooks/bootstrap-server.yml
|
||||
#
|
||||
- name: Bootstrap home server
|
||||
hosts: homeservers
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
vars:
|
||||
deploy_user: deploy
|
||||
deploy_uid: 1000
|
||||
ssh_port: 22
|
||||
deploy_ssh_key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
|
||||
|
||||
tasks:
|
||||
- name: Update apt cache
|
||||
ansible.builtin.apt:
|
||||
update_cache: true
|
||||
cache_valid_time: 3600
|
||||
|
||||
- name: Upgrade all packages
|
||||
ansible.builtin.apt:
|
||||
upgrade: dist
|
||||
update_cache: true
|
||||
|
||||
- name: Install base packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- curl
|
||||
- wget
|
||||
- git
|
||||
- vim
|
||||
- htop
|
||||
- ca-certificates
|
||||
- gnupg
|
||||
- ufw
|
||||
- fail2ban
|
||||
- bash-completion
|
||||
state: present
|
||||
|
||||
- name: Set timezone
|
||||
community.general.timezone:
|
||||
name: "{{ timezone }}"
|
||||
|
||||
- name: Create deploy user
|
||||
ansible.builtin.user:
|
||||
name: "{{ deploy_user }}"
|
||||
uid: "{{ deploy_uid }}"
|
||||
shell: /bin/bash
|
||||
create_home: true
|
||||
password: ""
|
||||
|
||||
- name: Setup authorized_keys for deploy user
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ deploy_user }}"
|
||||
state: present
|
||||
key: "{{ deploy_ssh_key }}"
|
||||
|
||||
- name: Allow deploy user sudo without password
|
||||
ansible.builtin.copy:
|
||||
dest: "/etc/sudoers.d/{{ deploy_user }}"
|
||||
content: "{{ deploy_user }} ALL=(ALL) NOPASSWD:ALL\n"
|
||||
mode: "0440"
|
||||
|
||||
- name: SSH hardening
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: "^#?{{ item.key }}"
|
||||
line: "{{ item.key }} {{ item.value }}"
|
||||
state: present
|
||||
loop:
|
||||
- { key: "Port", value: "{{ ssh_port }}" }
|
||||
- { key: "PermitRootLogin", value: "no" }
|
||||
- { key: "PasswordAuthentication", value: "no" }
|
||||
- { key: "X11Forwarding", value: "no" }
|
||||
- { key: "ClientAliveInterval", value: "300" }
|
||||
- { key: "ClientAliveCountMax", value: "2" }
|
||||
notify: Restart ssh
|
||||
|
||||
- name: Configure UFW defaults
|
||||
community.general.ufw:
|
||||
direction: incoming
|
||||
policy: deny
|
||||
notify: Reload ufw
|
||||
|
||||
- name: Allow SSH through UFW
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ ssh_port }}"
|
||||
proto: tcp
|
||||
|
||||
- name: Allow HTTP and HTTPS through UFW
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ item }}"
|
||||
proto: tcp
|
||||
loop:
|
||||
- "80"
|
||||
- "443"
|
||||
|
||||
- name: Enable UFW
|
||||
community.general.ufw:
|
||||
state: enabled
|
||||
|
||||
- name: Configure fail2ban
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/fail2ban/jail.local
|
||||
content: |
|
||||
[DEFAULT]
|
||||
bantime = 1h
|
||||
findtime = 10m
|
||||
maxretry = 5
|
||||
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = {{ ssh_port }}
|
||||
filter = sshd
|
||||
logpath = /var/log/auth.log
|
||||
maxretry = 3
|
||||
mode: "0644"
|
||||
notify: Restart fail2ban
|
||||
|
||||
handlers:
|
||||
- name: Restart ssh
|
||||
ansible.builtin.service:
|
||||
name: sshd
|
||||
state: restarted
|
||||
|
||||
- name: Reload ufw
|
||||
community.general.ufw:
|
||||
state: reloaded
|
||||
|
||||
- name: Restart fail2ban
|
||||
ansible.builtin.service:
|
||||
name: fail2ban
|
||||
state: restarted
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
# Nasaď Docker služby z docker-services/ adresáře
|
||||
#
|
||||
# Použití:
|
||||
# ansible-playbook -i inventory.yml playbooks/deploy-services.yml
|
||||
# ansible-playbook -i inventory.yml playbooks/deploy-services.yml --extra-vars "services=[portainer,vaultwarden]"
|
||||
#
|
||||
- name: Deploy Docker services
|
||||
hosts: homeservers
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
vars:
|
||||
services_repo: https://github.com/<user>/mates-infra.git
|
||||
services_path: "/home/{{ docker_user }}/docker-services"
|
||||
services: "{{ services_to_deploy | default(['portainer', 'vaultwarden', 'gitea']) }}"
|
||||
|
||||
tasks:
|
||||
- name: Clone services repo
|
||||
ansible.builtin.git:
|
||||
repo: "{{ services_repo }}"
|
||||
dest: "{{ services_path }}"
|
||||
force: false
|
||||
update: true
|
||||
become_user: "{{ docker_user }}"
|
||||
|
||||
- name: Create .env from example
|
||||
ansible.builtin.copy:
|
||||
src: "{{ services_path }}/{{ item }}/.env.example"
|
||||
dest: "{{ services_path }}/{{ item }}/.env"
|
||||
remote_src: true
|
||||
force: false
|
||||
loop: "{{ services }}"
|
||||
become_user: "{{ docker_user }}"
|
||||
|
||||
- name: Deploy services
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ services_path }}/{{ item }}"
|
||||
state: present
|
||||
pull: missing
|
||||
loop: "{{ services }}"
|
||||
become_user: "{{ docker_user }}"
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
# Security hardening – sysctl, AppArmor, auditd, auto-updates
|
||||
#
|
||||
# Použití:
|
||||
# ansible-playbook -i inventory.yml playbooks/security-hardening.yml
|
||||
#
|
||||
- name: Security hardening
|
||||
hosts: homeservers
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Kernel hardening via sysctl
|
||||
ansible.posix.sysctl:
|
||||
name: "{{ item.key }}"
|
||||
value: "{{ item.value }}"
|
||||
state: present
|
||||
reload: true
|
||||
loop:
|
||||
- { key: "net.ipv4.conf.all.rp_filter", value: "1" }
|
||||
- { key: "net.ipv4.conf.default.rp_filter", value: "1" }
|
||||
- { key: "net.ipv4.icmp_echo_ignore_broadcasts", value: "1" }
|
||||
- { key: "net.ipv4.conf.all.accept_redirects", value: "0" }
|
||||
- { key: "net.ipv4.conf.default.accept_redirects", value: "0" }
|
||||
- { key: "net.ipv6.conf.all.accept_redirects", value: "0" }
|
||||
- { key: "net.ipv4.conf.all.send_redirects", value: "0" }
|
||||
- { key: "net.ipv4.conf.all.accept_source_route", value: "0" }
|
||||
- { key: "net.ipv4.conf.default.accept_source_route", value: "0" }
|
||||
- { key: "net.ipv4.tcp_syncookies", value: "1" }
|
||||
- { key: "net.ipv4.conf.all.log_martians", value: "1" }
|
||||
|
||||
- name: Install unattended-upgrades
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- unattended-upgrades
|
||||
- apt-listchanges
|
||||
state: present
|
||||
|
||||
- name: Enable automatic security updates
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/apt/apt.conf.d/20auto-upgrades
|
||||
content: |
|
||||
APT::Periodic::Update-Package-Lists "1";
|
||||
APT::Periodic::Unattended-Upgrade "1";
|
||||
APT::Periodic::AutocleanInterval "7";
|
||||
mode: "0644"
|
||||
|
||||
- name: Install auditd
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- auditd
|
||||
- audispd-plugins
|
||||
state: present
|
||||
|
||||
- name: Enable auditd
|
||||
ansible.builtin.service:
|
||||
name: auditd
|
||||
state: started
|
||||
enabled: true
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
# Update všech Docker služeb (docker compose pull + up -d)
|
||||
#
|
||||
# Použití:
|
||||
# ansible-playbook -i inventory.yml playbooks/update-services.yml
|
||||
#
|
||||
- name: Update Docker services
|
||||
hosts: homeservers
|
||||
become: true
|
||||
|
||||
vars:
|
||||
services_path: "/home/{{ docker_user }}/docker-services"
|
||||
services: "{{ services_to_deploy | default([]) }}"
|
||||
|
||||
tasks:
|
||||
- name: Pull and restart services
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ services_path }}/{{ item }}"
|
||||
state: present
|
||||
pull: always
|
||||
loop: "{{ services }}"
|
||||
become_user: "{{ docker_user }}"
|
||||
Reference in New Issue
Block a user