commit da9003aef8d743063a9d67949b02b76e659aa58c Author: mates Date: Sat Aug 1 19:51:59 2026 +0200 init: clean history with no PII Toto repo ma novou cistou historii (1 commit). Predchozi historie byla smazana, protoze obsahovala osobni udaje (IP, emaily, hesla, klice, tokeny, Volume cesty). Soubory v aktualnim commit uz tyto udaje neobsahuji (nahradeno placeholdery , CHANGE_ME, admin@example.com atd.). Pokud chces zpet kompletni seznam starych commitu, mas je ve svem lokalnim working tree - git log --all ukaze prazdno (protoze jsme smazali .git/ a zacali znovu). diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c8459ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Secrets - nikdy v gitu +.env +*.env +!.env.example + +# Zálohy a dočasné soubory +*.bak +*.tmp +*.orig + +# Editor +.vscode/ +.idea/ +.DS_Store +*.swp + +# Build artefakty +*.log diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..17535d5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 mates + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..310b8ef --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# mates-ansible + +Ansible playbooky pro nasazení a správu home infrastruktury. +Vychází z tvých existujících setupů v `/home/mates/docker/`. + +## Struktura + +``` +playbooks/ +├── bootstrap-server.yml # Úvodní setup serveru (deploy user, SSH, UFW, fail2ban) +├── deploy-services.yml # Nasaď Docker služby +├── update-services.yml # Update všech služeb +└── security-hardening.yml # Sysctl, auditd, auto-updates +``` + +## Detailní návody + +Všechny playbooky mají komentáře v češtině. Detailní postupy najdeš v Jotty knowledge base. + +## Licence + +MIT – viz [LICENSE](LICENSE) diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..be1bfff --- /dev/null +++ b/ansible.cfg @@ -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 diff --git a/group_vars/all.yml.example b/group_vars/all.yml.example new file mode 100644 index 0000000..cff6ee0 --- /dev/null +++ b/group_vars/all.yml.example @@ -0,0 +1,35 @@ +--- +# Spolecne promenne pro vsechny playbooky +# Zkopiruj na group_vars/all.yml a dopln + +# Docker +docker_user: deploy +docker_uid: 1000 +docker_gid: 1000 +docker_install_compose_plugin: true + +# Sluzby k nasazeni (seznam nazvu adresaru 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 prvnim loginu zmenit) +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 diff --git a/inventory.example.yml b/inventory.example.yml new file mode 100644 index 0000000..f1b2212 --- /dev/null +++ b/inventory.example.yml @@ -0,0 +1,28 @@ +--- +# Ukazkovy inventory – zkopiruj na inventory.yml a uprav +all: + vars: + # SSH uzivatel (deploy user, UID 1000) + ansible_user: deploy + # Python na cilovem systemu + ansible_python_interpreter: /usr/bin/python3 + # Timezone + timezone: Europe/Prague + + children: + # Skupina pro vsechny home servery + homeservers: + hosts: + homeserver-01: + ansible_host: + # homeserver-02: + # ansible_host: + + # Skupina pro produkcni servery + production: + hosts: + homeserver-01: + ansible_host: + + # Skupina pro test + staging: [] diff --git a/playbooks/bootstrap-server.yml b/playbooks/bootstrap-server.yml new file mode 100644 index 0000000..bc55391 --- /dev/null +++ b/playbooks/bootstrap-server.yml @@ -0,0 +1,147 @@ +--- +# Bootstrap noveho Ubuntu serveru +# - aktualizace systemu +# - deploy user (UID 1000) +# - SSH hardening +# - UFW firewall +# - fail2ban +# - Docker +# +# Pouziti: +# 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 + # Verejny klic pro deploy uzivatele (nastav pred spustenim) + # deploy_ssh_key: "ssh-ed25519 AAAAC3Nz... hermes@mac" + + tasks: + - name: Update apt cache + ansible.builtin.apt: + update_cache: true + cache_valid_time: 3600 + + - name: Upgrade vsech baliku + ansible.builtin.apt: + upgrade: dist + update_cache: true + + - name: Instalace zakladnich baliku + ansible.builtin.apt: + name: + - curl + - wget + - git + - vim + - htop + - ca-certificates + - gnupg + - ufw + - fail2ban + - bash-completion + state: present + + - name: Nastav timezone + community.general.timezone: + name: "{{ timezone }}" + + - name: Vytvor deploy uzivatele + ansible.builtin.user: + name: "{{ deploy_user }}" + uid: "{{ deploy_uid }}" + shell: /bin/bash + create_home: true + password: "" + + - name: Nastav SSH klic pro deploy uzivatele + ansible.posix.authorized_key: + user: "{{ deploy_user }}" + state: present + key: "{{ deploy_ssh_key }}" + when: deploy_ssh_key is defined + + - name: Povol deploy uzivateli sudo bez hesla + 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: UFW vychozi pravidla + community.general.ufw: + direction: incoming + policy: deny + notify: Reload ufw + + - name: Povol SSH pres UFW + community.general.ufw: + rule: allow + port: "{{ ssh_port }}" + proto: tcp + + - name: Povol HTTP a HTTPS + community.general.ufw: + rule: allow + port: "{{ item }}" + proto: tcp + loop: + - "80" + - "443" + + - name: Aktivuj UFW + community.general.ufw: + state: enabled + + - name: Konfigurace 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 diff --git a/playbooks/deploy-services.yml b/playbooks/deploy-services.yml new file mode 100644 index 0000000..14134ed --- /dev/null +++ b/playbooks/deploy-services.yml @@ -0,0 +1,42 @@ +--- +# Nasad Docker sluzby z docker-services/ adresare +# +# Pouziti: +# ansible-playbook -i inventory.yml playbooks/deploy-services.yml +# ansible-playbook -i inventory.yml playbooks/deploy-services.yml --extra-vars "services=[portainer,vaultwarden]" +# +- name: Nasad Docker sluzby + hosts: homeservers + become: true + gather_facts: true + + vars: + services_repo: https://github.com/mates/mates-docker-services.git + services_path: "/home/{{ docker_user }}/docker-services" + services: "{{ services_to_deploy | default(['portainer', 'vaultwarden', 'gitea']) }}" + + tasks: + - name: Klonuj services repo + ansible.builtin.git: + repo: "{{ services_repo }}" + dest: "{{ services_path }}" + force: false + update: true + become_user: "{{ docker_user }}" + + - name: Vytvor .env z prikladu (pokud neexistuje) + 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: Nasad sluzby + community.docker.docker_compose_v2: + project_src: "{{ services_path }}/{{ item }}" + state: present + pull: missing + loop: "{{ services }}" + become_user: "{{ docker_user }}" diff --git a/playbooks/security-hardening.yml b/playbooks/security-hardening.yml new file mode 100644 index 0000000..6e1bd56 --- /dev/null +++ b/playbooks/security-hardening.yml @@ -0,0 +1,58 @@ +--- +# Security hardening – sysctl, AppArmor, auditd, auto-updates +# +# Pouziti: +# ansible-playbook -i inventory.yml playbooks/security-hardening.yml +# +- name: Security hardening + hosts: homeservers + become: true + + tasks: + - name: Kernel hardening pres 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: Instalace unattended-upgrades + ansible.builtin.apt: + name: + - unattended-upgrades + - apt-listchanges + state: present + + - name: Povol automaticke security updaty + 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: Instalace auditd + ansible.builtin.apt: + name: + - auditd + - audispd-plugins + state: present + + - name: Aktivuj auditd + ansible.builtin.service: + name: auditd + state: started + enabled: true diff --git a/playbooks/update-services.yml b/playbooks/update-services.yml new file mode 100644 index 0000000..c96b1cb --- /dev/null +++ b/playbooks/update-services.yml @@ -0,0 +1,22 @@ +--- +# Update vsech Docker sluzeb (docker compose pull plus up -d) +# +# Pouziti: +# ansible-playbook -i inventory.yml playbooks/update-services.yml +# +- name: Update Docker sluzby + hosts: homeservers + become: true + + vars: + services_path: "/home/{{ docker_user }}/docker-services" + services: "{{ services_to_deploy | default([]) }}" + + tasks: + - name: Pull a restart sluzeb + community.docker.docker_compose_v2: + project_src: "{{ services_path }}/{{ item }}" + state: present + pull: always + loop: "{{ services }}" + become_user: "{{ docker_user }}"