# 02 - Test Ansible Bootstrap Bu asamanin amaci Terraform ile olusturulan test makinelerini Linux, hardening, Docker ve Swarm acisindan hazir hale getirmektir. DB yazilimi kurulumu bu asamanin disindadir. ## Hedef Makineler | Host | Rol | | --- | --- | | `iklim-app-01` | Swarm manager + app worker | | `iklim-db-01` | Manuel DB kurulumu icin OS-hardening uygulanmis DB node | ## Onerilen Dosya Yapisi ```text ansible/ ansible.cfg inventory/ generated/ test.yml group_vars/ all.yml test.yml playbooks/ test-bootstrap.yml roles/ base/ hardening/ docker/ swarm/ node_dirs/ storagebox/ ``` ## Base Role Tum test node'larina uygulanir: - `dnf update` - temel paketler (sirasıyla kurulur): - `epel-release` — fail2ban ve davfs2 bu repo'dan gelir; once kurulur - `curl` - `wget` - `git` - `jq` - `tar` - `unzip` - `bash-completion` - `gettext` — envsubst icin; CI/CD deploy pipeline'larinda gerekli - `tree` - `ca-certificates` - `fail2ban` - `chrony` - `python3` - `python3-pip` - timezone: `Europe/Istanbul` - hostname ayari - sistem reboot gerekiyorsa kontrollu reboot ## Security Hardening Role Tum test node'larina uygulanir: - SSH password login kapatilir. - Root SSH login kapatilir. - Sadece SSH key ile login kalir. - `PermitEmptyPasswords no` - `MaxAuthTries 3` - `fail2ban` SSH jail aktif edilir. - `dnf-automatic` ile otomatik guvenlik guncellestirmeleri aktif edilir. - `firewalld` default: - incoming: deny (drop zone) - outgoing: allow - Public SSH sadece admin CIDR'dan acilir. ### SELinux Karari Rocky Linux 10 SELinux enforcing modda gelir. Karar: **disabled**. Gerekce: - Hetzner Cloud firewall (dis perimeter) + firewalld (host) iki katman ag guvenligini saglar. - Docker + davfs2 + firewalld kombinasyonu SELinux enforcing modda ek policy ve volume label yonetimi gerektirir. - Utils VPS'te de disabled yapilmis; tutarlilik saglanir. ```bash # /etc/selinux/config icinde: SELINUX=disabled # Degisiklik reboot sonrasi aktif olur reboot ``` Ansible'da: ```yaml - name: Disable SELinux ansible.posix.selinux: state: disabled register: selinux_change - name: Reboot if SELinux state changed ansible.builtin.reboot: when: selinux_change.changed ``` ### fail2ban Konfigurasyonu `/etc/fail2ban/jail.local` icerigi: ```ini [DEFAULT] ignoreip = 127.0.0.1/8 {{ admin_allowed_cidrs }} bantime = 21600 findtime = 300 maxretry = 5 banaction = iptables-multiport backend = systemd [sshd] enabled = true ``` - `bantime`: 6 saat ban - `findtime`: 5 dakika icinde - `maxretry`: 5 basarisiz giris → ban - `ignoreip`: admin CIDR'lari ban'dan muaf tutar Ansible'da `admin_allowed_cidrs` listesi space-separated stringe donusturulup template'e basilir. Not: Docker iptables kurallari firewalld ile etkilesebilir. Hetzner Cloud firewall asil dis perimeter kabul edilir; firewalld host icinde ikinci katman olarak kullanilir. ## Docker Role Sadece `iklim-app-01` uzerinde zorunludur. `iklim-db-01` uzerinde DB manual kurulum stratejisine gore opsiyonel tutulabilir. Docker kurulumu resmi Docker dnf repository uzerinden yapilir: - Docker GPG key + dnf repository (`https://download.docker.com/linux/rhel/docker-ce.repo`) - paketler: - `docker-ce` - `docker-ce-cli` - `containerd.io` - `docker-buildx-plugin` - `docker-compose-plugin` - Docker servisi enabled + started Docker convenience script kullanilmayacak. Production benzeri test ortami icin paket repository yolu tercih edilir. ## Swarm Role `iklim-app-01` uzerinde: - `docker swarm init` - advertise addr: `10.10.10.11` - data path addr: `10.10.10.11` - overlay network: - `iklimco-net` - driver: `overlay` - attachable: `true` - Node `type=service` label'i ile isaretlenir: ```bash docker node update --label-add type=service iklim-app-01 ``` - Node `AVAILABILITY=Active` kalir (drain edilmez); tek node hem manager hem worker'dir. Test tek node Swarm oldugu icin join token kullanimi yoktur. ## Node Directory Role `iklim-app-01` uzerinde deploy on kosullari: ```text /opt/iklimco /opt/iklimco/ssl /opt/iklimco/init /opt/iklimco/init/postgresql /opt/iklimco/init/mongodb ``` DB node uzerinde manuel DB kurulumu icin minimum: ```text /opt/iklimco /opt/iklimco/db /opt/iklimco/backup ``` ## StorageBox DAVFS Mount Role Her iki node'a uygulanir (`iklim-app-01` ve `iklim-db-01`). ### Amac Hetzner StorageBox'u WebDAV (DAVFS) protokolü üzerinden `/mnt/storagebox` olarak mount eder. Docker volume'lari bu dizine baglanarak veri kaliciligini ve yedeklemeyi saglar. ### Test Ortami Sub-Account | Parametre | Degisken | Deger | | --- | --- | --- | | Ana hesap | `storagebox_account` | `u469968` | | Sub-account | `storagebox_user` | `u469968-sub1` | | WebDAV URL | `storagebox_url` | `https://u469968-sub1.your-storagebox.de/` | | Mount point | `storagebox_mount_point` | `/mnt/storagebox` | ### Role Degiskenleri `group_vars/all.yml` — tum ortamlar icin ortak: ```yaml storagebox_account: "u469968" ``` `group_vars/test.yml` — test ortamina ozgu; user ve url account'tan turetilir: ```yaml storagebox_user: "{{ storagebox_account }}-sub1" storagebox_url: "https://{{ storagebox_user }}.your-storagebox.de/" storagebox_password: "{{ vault_storagebox_password }}" # Ansible Vault ile saklanir storagebox_mount_point: "/mnt/storagebox" ``` Prod ortaminda yalnizca suffix degisir (`sub1` → `sub2`), geri kalan her sey turetilir. `vault_storagebox_password` degeri Ansible Vault ile sifreli `group_vars/test-vault.yml` icinde tutulur: ```bash # Sifreleme ansible-vault encrypt group_vars/test-vault.yml # Duzenleme ansible-vault edit group_vars/test-vault.yml ``` `test-vault.yml` icerigi: ```yaml vault_storagebox_password: "SUB_ACCOUNT_PAROLASI" ``` ### Adimlar 1. **davfs2 kurulumu** ```yaml - name: Install davfs2 ansible.builtin.dnf: name: davfs2 state: present ``` 2. **Kimlik bilgileri dosyasi** (`/etc/davfs2/secrets`) ```yaml - name: Configure davfs2 secrets ansible.builtin.lineinfile: path: /etc/davfs2/secrets line: "{{ storagebox_url }} {{ storagebox_user }} {{ storagebox_password }}" create: yes mode: "0600" owner: root group: root ``` 3. **Mount point olustur** ```yaml - name: Create mount point ansible.builtin.file: path: "{{ storagebox_mount_point }}" state: directory mode: "0755" ``` 4. **fstab kaydı** ```yaml - name: Add fstab entry ansible.builtin.lineinfile: path: /etc/fstab line: >- {{ storagebox_url }} {{ storagebox_mount_point }} davfs _netdev,auto,user,rw,uid=root,gid=root 0 0 state: present ``` 5. **Mount et** ```yaml - name: Mount StorageBox ansible.builtin.command: mount {{ storagebox_mount_point }} args: creates: "{{ storagebox_mount_point }}/.mounted_marker" ``` Mount basarisi icin dizine bir marker dosyasi yazilabilir: ```yaml - name: Write mount marker ansible.builtin.copy: content: "mounted by ansible" dest: "{{ storagebox_mount_point }}/.mounted_marker" ``` ### Notlar - `davfs2` paketi EPEL repository'sinde bulunur; base role `epel-release`'i zaten kurar. - StorageBox sifreleri asla plaintext olarak repository'e eklenmez; Ansible Vault zorunludur. - Mount noktasi reboot'ta `_netdev` flag'i sayesinde network hazir olduktan sonra otomatik mount edilir. - Docker Swarm servisleri `/mnt/storagebox///` altindaki dizinleri bind mount olarak kullanir. ## StorageBox SSH Key Role Her iki node'a uygulanir (`iklim-app-01` ve `iklim-db-01`). ### Amac Sunucu uzerinde ed25519 SSH anahtar cifti uretilir ve StorageBox ana hesabina yuklenir. Bu sayede CI/CD pipeline'lari `STORAGEBOX_SSH_PRIV` Gitea secret'ini kullanarak sifre girmeden StorageBox'a erisebilir. ### Adimlar 1. **SSH key uret** (eger yoksa) ```yaml - name: Generate SSH key for storagebox ansible.builtin.user: name: root generate_ssh_key: yes ssh_key_type: ed25519 ssh_key_file: /root/.ssh/id_ed25519_storagebox ssh_key_comment: "{{ inventory_hostname }}-storagebox" ``` 2. **Public key'i StorageBox'a yukle** Bu adim manuel yapilir (ilk kez sifre gerektirir): ```bash cat /root/.ssh/id_ed25519_storagebox.pub | ssh -p23 u469968-sub1@u469968-sub1.your-storagebox.de install-ssh-key ``` Sonraki erisimler sifresiz calisir: ```bash sftp -P23 u469968-sub1@u469968-sub1.your-storagebox.de ``` 3. **Private ve public key'leri Gitea'ya ekle** Gitea → Organization Settings → Actions → Secrets: | Secret Adi | Deger | | --- | --- | | `STORAGEBOX_SSH_PRIV` | `/root/.ssh/id_ed25519_storagebox` icerigi | | `STORAGEBOX_SSH_PUB` | `/root/.ssh/id_ed25519_storagebox.pub` icerigi | Key icerigini almak icin: ```bash cat /root/.ssh/id_ed25519_storagebox cat /root/.ssh/id_ed25519_storagebox.pub ``` ### Notlar - Her sunucu icin ayri key uretilir; tum public key'ler StorageBox ana hesabina yuklenir. - Private key asla repo'ya commit edilmez; yalnizca Gitea secret olarak saklanir. ## Kabul Kriterleri - `ansible -i inventory/generated/test.yml all -m ping` basarili olur. - `iklim-app-01` uzerinde `docker info` calisir. - `iklim-app-01` uzerinde Swarm active olur; node `AVAILABILITY=Active` (drain degil). - `docker network ls` icinde `iklimco-net` gorulur. - `docker node inspect iklim-app-01 --format '{{.Spec.Labels}}'` ciktisi `map[type:service]` icerir. - `iklim-db-01` uzerinde public DB portu acik degildir. - Public portlar Hetzner firewall + firewalld seviyesinde `22`, `80`, `443` ile sinirlidir. - Her iki node'da `mount | grep storagebox` StorageBox mount'unu gosterir. - `ls /mnt/storagebox/.mounted_marker` basarili olur. - Reboot sonrasi mount otomatik olarak geri gelir.