- 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.
380 lines
9.7 KiB
Markdown
380 lines
9.7 KiB
Markdown
# 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.
|