Environment_Infrastructure/setup/03-test-ansible-bootstrap.md
Murat ÖZDEMİR b115a4cbdf Implement Hetzner sizing report recommendations and detailed DB setups
- Add `hetzner-sizing-report.md` defining data-driven server type recommendations for test and prod environments.
- Update Terraform configurations to align with the recommended `CPX` server types and refine firewall rules for Docker Swarm and database interactions.
- Introduce comprehensive documentation and stack files for:
    - Single-node PostgreSQL/MongoDB deployment on a test DB worker node.
    - High-availability 3-node MongoDB replica set and Patroni+etcd PostgreSQL cluster for production.
- Enhance Ansible bootstrap roles with SELinux disabling, fail2ban configuration, and StorageBox SSH key management for CI/CD.
- Reorganize and rename setup documentation files for improved structure and clarity.
2026-05-11 14:54:09 +03:00

380 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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/<env>/<service>/` 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.