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.
456 lines
12 KiB
Markdown
456 lines
12 KiB
Markdown
# 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 binary’sinin 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
|