Environment_Infrastructure/setup/03-test-ansible-bootstrap.md
Murat ÖZDEMİR 65443e81e7 Refine: Update documentation to accurately reflect Ansible assets and structure
This commit brings the `README.md` and Ansible setup guides (`03-test-ansible-bootstrap.md`, `07-prod-ansible-bootstrap.md`) in sync with the current state of the Ansible automation.

Key updates include:
- Acknowledging the presence of in-repository Ansible playbooks and shared roles.
- Correcting Ansible inventory output paths and Terraform output commands.
- Detailing the new `group_vars/all/{vars.yml,vault.yml}` structure.
- Updating Ansible prerequisites to include `passlib` for password hashing.
- Adding documentation for `iklim` system user creation, keyboard layout, and refined firewall rules.
- Removing outdated "Known Gaps" related to missing Ansible code.
2026-05-11 19:00:10 +03:00

456 lines
12 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.

# 03 - Test Ansible Bootstrap
Bu aşamanın amacı Terraform ile oluşturulan test makinelerini Linux, hardening, Docker ve Swarm açısından hazır hale getirmektir. DB yazılımı kurulumu bu aşamanın dışındadır.
## Ansible Kurulumu
Ansible, kontrol makinesinde (kendi bilgisayarınızda) yüklü olmalıdır. Hedef sunuculara herhangi bir ajan kurulmaz, sadece SSH erişimi yeterlidir.
### İşletim Sistemine Göre Kurulum
- **Ubuntu / Debian:**
```bash
sudo apt update
sudo apt install -y pipx python3-venv
pipx ensurepath
export PATH="$HOME/.local/bin:$PATH"
pipx install --include-deps ansible
```
> Not: `sudo apt install ansible` komutu bazı Ubuntu/Debian sürümlerinde eski Ansible paketlerini kurabilir. Bu nedenle güncel Ansible kullanımı için `pipx` yöntemi tercih edilmelidir.
- **Fedora / Rocky Linux / RHEL:**
```bash
sudo dnf install -y pipx python3-virtualenv
pipx ensurepath
export PATH="$HOME/.local/bin:$PATH"
pipx install --include-deps ansible
```
- **macOS (Homebrew):**
```bash
brew install ansible
```
- **Python Pip ile (Her platformda):**
```bash
pipx install --include-deps ansible
```
### Ek Python Bağımlılıkları
`password_hash` filtresi için `passlib` kontrol makinesinde gereklidir:
```bash
pipx inject ansible passlib
```
> `pip` ile kurduysanız: `pip install passlib`
### Kurulumun Doğrulanması
Hangi yöntemle kurarsanız kurun, kurulumun başarılı olduğunu doğrulamak için aşağıdaki komutları kullanın:
```bash
# Ansible versiyonunu ve yapılandırma yollarını kontrol edin
ansible --version
# Ansible binarysinin hangi konumdan çalıştığını kontrol edin
which -a ansible
```
## Ansible Komutlarını Çalıştırma
Tüm komutlar `ansible/test/` dizininden çalıştırılmalıdır. `ansible.cfg` inventory ve roles_path'i otomatik olarak tanımlar.
### 0. Gerekli Collection'ları Kur (İlk kurulumda bir kez)
```bash
ansible-galaxy collection install -r ../requirements.yml
```
### 1. Bağlantı Testi (Ping)
```bash
ansible all -m ping
```
### 2. Bootstrap Playbook'unu Çalıştırma
```bash
ansible-playbook test-bootstrap.yml --ask-vault-pass
```
*Not: `--ask-vault-pass` parametresi Ansible Vault parolasını sorar; StorageBox şifresi bu şekilde çözülür.*
### 3. Sadece Belirli Bir Rolü Çalıştırma (Tags)
```bash
ansible-playbook test-bootstrap.yml --tags "hardening" --ask-vault-pass
```
## Hedef Makineler
| Host | Rol |
| --- | --- |
| `iklim-app-01` | Swarm manager + app worker |
| `iklim-db-01` | Manuel DB kurulumu için OS-hardening uygulanmış DB node |
## Önerilen Dosya Yapısı
```text
ansible/
test/
ansible.cfg
inventory/
generated/
test.yml
group_vars/
all/
vars.yml
vault.yml
test-bootstrap.yml
roles/
base/
hardening/
docker/
swarm/
node_dirs/
storagebox/
storagebox_ssh_key/
```
## Base Role
Tüm test node'larına uygulanır:
- `dnf update`
- `epel-release` — ayrı task olarak önce kurulur; `fail2ban`, `davfs2`, `htop`, `btop` bu repoya bağımlı
- temel paketler (`epel-release` aktif olduktan sonra):
- `curl`
- `wget`
- `git`
- `jq`
- `tar`
- `unzip`
- `bash-completion`
- `gettext` — envsubst için; CI/CD deploy pipeline'larında gerekli
- `tree`
- `ca-certificates`
- `fail2ban`
- `chrony`
- `python3`
- `python3-pip`
- `python3-passlib` — `password_hash` filtresi için (EPEL)
- `htop` — interaktif proses izleme (EPEL)
- `btop` — kaynak monitörü, grafik arayüz (EPEL)
- timezone: `Europe/Istanbul`
- hostname ayarı
- klavye düzeni: `trq` (Türkçe Q)
- sistem reboot gerekiyorsa kontrollü reboot
## Security Hardening Role
Tüm test node'larına uygulanır:
- SSH password login kapatılır.
- Root SSH login kapatılır.
- Sadece SSH key ile login kalır.
- `PermitEmptyPasswords no`
- `MaxAuthTries 3`
- `fail2ban` SSH jail aktif edilir.
- `dnf-automatic` ile otomatik güvenlik güncelleştirmeleri aktif edilir.
- `iklim` sistem kullanıcısı oluşturulur; `wheel` grubuna eklenir (şifre vault'tan alınır).
- `firewalld` default:
- incoming: deny (drop zone)
- outgoing: allow
- SSH kuralı önce `drop` zone'a rich rule olarak yazılır, ardından default zone `drop` yapılır — kilitleme riski ortadan kalkar.
- Public SSH sadece admin CIDR'dan açılır.
### SELinux Kararı
Rocky Linux 10 SELinux enforcing modda gelir. Karar: **disabled**.
Gerekçe:
- Hetzner Cloud firewall (dış perimeter) + firewalld (host) iki katman ağ güvenliğini sağlar.
- Docker + davfs2 + firewalld kombinasyonu SELinux enforcing modda ek policy ve volume label yönetimi gerektirir.
- Utils VPS'te de disabled yapılmış; tutarlılık sağlanır.
```bash
# /etc/selinux/config içinde:
SELINUX=disabled
# Değişiklik reboot sonrası 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 Konfigürasyonu
`/etc/fail2ban/jail.local` içeriği:
```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 içinde
- `maxretry`: 5 başarısız giriş → ban
- `ignoreip`: admin CIDR'ları ban'dan muaf tutar
Ansible'da `admin_allowed_cidrs` listesi space-separated stringe dönüştürülüp template'e basılır.
Not: Docker iptables kuralları firewalld ile etkileşebilir. Hetzner Cloud firewall asıl dış perimeter kabul edilir; firewalld host içinde ikinci katman olarak kullanılır.
## Docker Role
Her iki node (`iklim-app-01` ve `iklim-db-01`) üzerinde de zorunludur. DB node'u Swarm Worker olarak ağa dahil olacağı için Docker Engine her iki makinede de kurulu olmalıdır.
Docker kurulumu resmi Docker dnf repository üzerinden yapılır:
- 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 kullanılmayacak. Production benzeri test ortamı için paket repository yolu tercih edilir.
## Swarm Role
- `iklim-app-01` üzerinde Swarm Manager olarak init edilir.
- `iklim-db-01` üzerinde Swarm Worker olarak join edilir (Overlay network erişimi için).
- advertise addr: `10.10.10.11` (manager için)
- overlay network:
- `iklimco-net`
- driver: `overlay`
- attachable: `true`
- Node etiketleri:
- `iklim-app-01` -> `type=service`
- `iklim-db-01` -> `role=db`
- `iklim-app-01` üzerinde hem manager hem worker (Active) görevini sürdürür.
## Node Directory Role
`iklim-app-01` üzerinde deploy ön koşulları:
```text
/opt/iklimco
/opt/iklimco/ssl
/opt/iklimco/init
/opt/iklimco/init/postgresql
/opt/iklimco/init/mongodb
/opt/iklimco/stacks
```
DB node üzerinde manuel DB kurulumu için minimum:
```text
/opt/iklimco
/opt/iklimco/db
/opt/iklimco/backup
```
## StorageBox DAVFS Mount Role
Her iki node'a uygulanır (`iklim-app-01` ve `iklim-db-01`).
### Amaç
Hetzner StorageBox'u WebDAV (DAVFS) protokolü üzerinden `/mnt/storagebox` olarak mount eder. Docker volume'ları bu dizine bağlanarak veri kalıcılığını ve yedeklemeyi sağlar.
### Test Ortamı Sub-Account
| Parametre | Değişken | Değer |
| --- | --- | --- |
| Ana hesap | `storagebox_account` | `u469968` |
| Sub-account | `storagebox_user` | `u469968-sub4` |
| WebDAV URL | `storagebox_url` | `https://u469968-sub4.your-storagebox.de/` |
| Mount point | `storagebox_mount_point` | `/mnt/storagebox` |
### Role Değişkenleri
Tüm değişkenler `group_vars/all/vars.yml` içinde tanımlanır:
```yaml
storagebox_account: "u469968"
storagebox_user: "{{ storagebox_account }}-sub4"
storagebox_url: "https://{{ storagebox_user }}.your-storagebox.de/"
storagebox_password: "{{ vault_storagebox_password }}"
storagebox_mount_point: "/mnt/storagebox"
```
Prod ortamında suffix `sub4` → `sub5` olarak değişir.
Şifreler Ansible Vault ile şifreli `group_vars/all/vault.yml` içinde tutulur:
```bash
ansible-vault edit group_vars/all/vault.yml
```
`vault.yml` içeriği:
```yaml
vault_storagebox_password: "SUB_ACCOUNT_PAROLASI"
vault_iklim_password: "IKLIM_KULLANICI_PAROLASI"
```
### Adımlar
1. **davfs2 kurulumu**
```yaml
- name: Install davfs2
ansible.builtin.dnf:
name: davfs2
state: present
```
2. **Kimlik bilgileri dosyası** (`/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 oluştur**
```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 başarısı için dizine bir marker dosyası yazılabilir:
```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 şifreleri asla plaintext olarak repository'e eklenmez; Ansible Vault zorunludur.
- Mount noktası reboot'ta `_netdev` flag'ı sayesinde network hazır olduktan sonra otomatik mount edilir.
- Docker Swarm servisleri `/mnt/storagebox/<env>/<service>/` altındaki dizinleri bind mount olarak kullanır.
## StorageBox SSH Key Role
Her iki node'a uygulanır (`iklim-app-01` ve `iklim-db-01`).
### Amaç
Sunucu üzerinde ed25519 SSH anahtar çifti üretilir ve StorageBox ana hesabına yüklenir.
Bu sayede CI/CD pipeline'ları `STORAGEBOX_SSH_PRIV` Gitea secret'ini kullanarak
şifresiz erişim sağlayabilir.
### Adımlar
1. **SSH Key üretimi**
```yaml
- name: Generate SSH key for StorageBox
ansible.builtin.user:
name: root
generate_ssh_key: yes
ssh_key_type: ed25519
ssh_key_file: .ssh/id_ed25519_storagebox
ssh_key_comment: "{{ inventory_hostname }}-storagebox"
```
2. **Public key'i StorageBox'a yükle**
Bu adım manuel yapılır (ilk kez şifre gerektirir):
```bash
cat /root/.ssh/id_ed25519_storagebox.pub | ssh -p23 u469968-sub4@u469968-sub4.your-storagebox.de install-ssh-key
```
Sonraki erişimler şifresiz çalışır:
```bash
sftp -P23 u469968-sub4@u469968-sub4.your-storagebox.de
```
3. **Private ve public key'leri Gitea'ya ekle**
Gitea → Organization Settings → Actions → Secrets:
| Secret Adı | Değer |
| --- | --- |
| `STORAGEBOX_SSH_PRIV` | `/root/.ssh/id_ed25519_storagebox` içeriği |
| `STORAGEBOX_SSH_PUB` | `/root/.ssh/id_ed25519_storagebox.pub` içeriği |
Key içeriğini almak için:
```bash
cat /root/.ssh/id_ed25519_storagebox
cat /root/.ssh/id_ed25519_storagebox.pub
```
### Notlar
- Her sunucu için ayrı key üretilir; tüm public key'ler StorageBox ana hesabına yüklenir.
- Private key asla repo'ya commit edilmez; yalnızca Gitea secret olarak saklanır.
## Kabul Kriterleri