Encryption

Encryption #

Enkripsi adalah lapisan pertahanan terakhir dalam arsitektur keamanan Ansible. Ia tidak menggantikan kontrol akses atau audit, tapi memastikan bahwa bahkan ketika kontrol lain gagal — laptop dicuri, disk backup bocor, log terekspos ke pihak ketiga — data yang dicuri tetap tidak bisa dibaca tanpa kunci yang benar. Ansible menyentuh enkripsi di banyak titik: variabel yang berisi password di-enkripsi dengan Vault, koneksi transport ke managed node diamankan dengan SSH/TLS, sertifikat SSL/TLS untuk service internal harus didistribusikan dan di-rotate, dan data sensitif di log harus di-redact. Artikel ini membahas setiap titik tersebut secara sistematis — kapan pakai algoritma simetris versus asimetris, bagaimana mengelola key lifecycle, dan pola umum yang aman untuk masing-masing skenario. Pemahaman yang baik tentang kekuatan dan kelemahan setiap algoritma mencegah pilihan yang kelihatannya aman tapi sebenarnya rapuh.

Kapan Enkripsi Dibutuhkan? #

DATA SENSITIF:
  ✓ Password dan API key → ENCRYPT REQUIRED (Vault)
  ✓ Private key (SSH, TLS) → ENCRYPT REQUIRED (file permission + Vault)
  ✓ Database connection string dengan credential → ENCRYPT REQUIRED
  ✓ File konfigurasi production (web server, app config) → ENCRYPT REQUIRED
  ✓ Backup file yang berisi data → ENCRYPT REQUIRED (LUKS/dm-crypt)
  ✓ Token sesi untuk user → ENCRYPT in transit (TLS), EXPIRE cepat

DATA NON-SENSITIF:
  ✗ Hostname dan IP server → tidak perlu
  ✗ Path direktori (/var/log/app) → tidak perlu
  ✗ Nama user biasa → tidak perlu
  ✗ Konfigurasi non-secret (port, log level) → tidak perlu

Enkripsi menambah overhead CPU dan kompleksitas operasional. Tidak semua data butuh di-enkripsi — hanya yang risikonya material jika bocor. Klasifikasi data sensitif adalah langkah pertama sebelum menentukan strategi enkripsi yang sesuai.


Arsitektur Enkripsi di Ansible #

flowchart LR
    A["Ansible<br/>Control Node"] -->|"SSH/TLS"| B["Managed Node"]
    A -->|"Vault encrypt"| C["Vault File<br/>AES256"]
    A -->|"TLS"| D["Secret Manager<br/>Vault/AWS SM"]
    B -->|"SSH key"| E["authorized_keys"]
    B -->|"TLS cert"| F["Service<br/>nginx/postgres"]
    D -->|"runtime inject"| A
    C -->|"decrypt saat runtime"| A
    A -->|"AnsibleModule"| B

Enkripsi terjadi di empat titik dalam ekosistem Ansible: file Vault di control node (dienkripsi saat commit, didekripsi saat runtime), koneksi transport (SSH antara control dan managed node, atau HTTPS ke API), sertifikat di managed node (TLS untuk service internal), dan secret manager eksternal (Vault, AWS Secrets Manager) yang menyediakan secret ke playbook saat runtime tanpa pernah menyimpan plaintext di disk Ansible.


Algoritma Simetris vs Asimetris #

flowchart TD
    A["Pilih Algoritma"] --> B{"Jenis data?"}
    B -->|"File/data besar<br/>dienkrip sekali<br/>dan dibaca berkali-kali"| C["Simetris<br/>AES-256-GCM"]
    B -->|"Kunci perlu<br/>dibagikan ke<br/>banyak pihak"| D["Asimetris<br/>RSA/Ed25519/ECDSA"]
    C --> E["Vault file<br/>LUKS disk<br/>TLS bulk encryption"]
    D --> F["SSH key pair<br/>TLS handshake<br/>Digital signature"]

Pemilihan algoritma simetris versus asimetris menentukan seluruh operasional enkripsi. Simetris (AES-256-GCM) cocok untuk data dalam jumlah besar yang dienkripsi sekali dan dibaca berkali-kali — file Vault, full disk encryption, atau bulk data encryption. Asimetris (RSA, Ed25519, ECDSA) cocok untuk situasi di mana kunci publik harus dibagikan ke banyak pihak tanpa mengorbankan kunci privat — handshake TLS, SSH key pair, dan tanda tangan digital. Hampir semua sistem modern menggunakan keduanya secara hybrid: TLS misalnya menggunakan RSA/ECDSA untuk pertukaran kunci sesi, lalu AES untuk bulk encryption.


Tabel Perbandingan Algoritma #

Algoritma Tipe Panjang kunci Use case Catatan
AES-256-GCM Simetris 256 bit File Vault, disk encryption, TLS bulk Standar industri, hardware acceleration di CPU modern
ChaCha20-Poly1305 Simetris 256 bit Lingkungan tanpa hardware AES acceleration Cepat di software, populer di mobile
RSA-2048 Asimetris 2048 bit Legacy TLS, signature Sudah tua, migrasi ke ECDSA direkomendasikan
RSA-4096 Asimetris 4096 bit High-security signature Lambat untuk handshake, gunakan hanya jika benar-benar perlu
ECDSA P-256 Asimetris 256 bit TLS 1.3, SSH, modern Cepat, ukuran signature kecil
Ed25519 Asimetris 256 bit (key) SSH (disukai), signature Paling cepat, signature 64 byte
bcrypt Hash + salt variable Password hashing Cost factor 12 minimum di 2024
Argon2id Hash + memory-hard variable Password hashing baru Lebih kuat dari bcrypt untuk GPU attack
scrypt Hash + memory-hard variable Password hashing Alternatif Argon2id

Untuk Ansible, AES-256 via Vault adalah pilihan default. Untuk SSH key, Ed25519 memberikan keamanan dan performa terbaik. Untuk password, gunakan bcrypt cost factor ≥ 12 atau Argon2id jika library-nya tersedia.


Ansible Vault — Enkripsi At Rest #

Vault mengenkripsi file YAML dengan AES-256. File Vault tetap bisa di-include ke playbook seperti biasa, tapi isinya tidak terbaca tanpa vault password. Ansible mengenali file Vault otomatis berdasarkan header $ANSIBLE_VAULT;1.1;AES256.

# Buat file Vault baru
ansible-vault create secrets/production.yml

# Edit file Vault (prompt untuk password)
ansible-vault edit secrets/production.yml

# Enkripsi file YAML yang sudah ada
ansible-vault encrypt existing-file.yml

# Lihat isi tanpa edit
ansible-vault view secrets/production.yml

# Decrypt untuk operasi yang perlu plaintext (jarang)
ansible-vault decrypt secrets/production.yml

Setiap operasi membutuhkan vault password. Password bisa dimasukkan interaktif (--ask-vault-pass), dari file (--vault-password-file), dari environment variable (ANSIBLE_VAULT_PASSWORD_FILE), atau dari script kustom (--vault-password-client) yang mengambil password dari secret manager eksternal saat runtime.

# ANTI-PATTERN: vault password di Git
# .gitignore salah: lupa ignore vault password file
# Akibatnya: vault_password_prod.txt ter-commit, attacker bisa decrypt semua vault
# $ cat /var/log/git-history/vault_password_prod.txt
# my-s3cr3t-vault-password

# BENAR: vault password dari secret manager, inject saat runtime
# playbooks/deploy.yml
- name: Deploy dengan vault password dari AWS Secrets Manager
  hosts: production
  vars:
    vault_password: "{{ lookup('aws_secret', 'ansible/vault-password', region='ap-southeast-1') }}"
  vars_files:
    - secrets/production.yml  # File Vault, decrypted saat runtime
  tasks:
    - name: Deploy aplikasi
      template:
        src: app.conf.j2
        dest: /etc/myapp/app.conf
        mode: '0640'
        owner: root
        group: myapp

Inject vault password saat runtime dari secret manager eksternal memastikan password tidak pernah tersimpan di Git, di file konfigurasi Ansible, atau di environment variable yang persisten.


Strategi Multi-Vault #

Satu vault password untuk semua environment sangat tidak fleksibel. Setiap environment harus punya vault terpisah, dengan password yang berbeda, sehingga kebocoran satu vault tidak mengekspos environment lain.

# group_vars/all/vaults.yml
# Definisi vault per environment — di-include di vars_files
vault_files:
  development: secrets/dev.yml
  staging: secrets/staging.yml
  production: secrets/production.yml

# Vault password juga per environment
vault_passwords:
  development: "{{ lookup('env', 'ANSIBLE_VAULT_DEV_PASSWORD') }}"
  staging: "{{ lookup('env', 'ANSIBLE_VAULT_STAGING_PASSWORD') }}"
  production: "{{ lookup('aws_secret', 'ansible/vault-password', region='ap-southeast-1') }}"
# ANTI-PATTERN: satu vault untuk semua environment
# secrets/all.yml — berisi password production DAN dev DAN staging
# vault_password: 'one-password-for-everyone'
# Kebocoran = compromise seluruh organisasi

# BENAR: vault per environment + audit
# secrets/production.yml
---
production_db_password: "strong-random-32-char-password"
production_api_key: "very-long-random-api-key"
production_tls_cert: |
  -----BEGIN CERTIFICATE-----
  ...
  -----END CERTIFICATE-----  

Pisahkan vault per environment memungkinkan audit terpisah — vault production hanya diakses dari CI/CD yang terpercaya, vault development bisa diakses dari workstation developer tanpa mengorbankan keamanan production.


Rotasi password secara berkala. Password Vault, password database, dan API key harus di-rotate minimal setiap 90 hari. Ansible dapat mengotomasi rotasi dengan playbook yang generate password baru, update Vault, dan restart service yang bergantung pada secret tersebut. Tanpa rotasi, password yang bocor akan tetap valid sampai kebocoran itu terdeteksi — yang bisa bertahun-tahun.

Enkripsi Transport dengan SSH #

Koneksi antara Ansible control node dan managed node selalu dienkripsi oleh SSH. Tapi konfigurasi SSH yang lemah (protokol lama, cipher lemah, key exchange lemah) mengurangi efektivitas enkripsi transport.

# roles/ssh-hardening/tasks/main.yml
---
- name: Deploy sshd_config yang aman
  template:
    src: sshd_config.j2
    dest: /etc/ssh/sshd_config
    owner: root
    group: root
    mode: '0600'
  notify: Restart sshd

- name: Disable protokol dan cipher lama
  lineinfile:
    path: /etc/ssh/sshd_config
    regexp: "{{ item.regexp }}"
    line: "{{ item.line }}"
    state: present
  loop:
    - regexp: '^#?Protocol'
      line: 'Protocol 2'
    - regexp: '^#?Ciphers'
      line: 'Ciphers [email protected],[email protected],[email protected]'
    - regexp: '^#?MACs'
      line: 'MACs [email protected],[email protected]'
    - regexp: '^#?KexAlgorithms'
      line: 'KexAlgorithms [email protected],diffie-hellman-group-exchange-sha256'

Kombinasi chacha20-poly1305 + curve25519-sha256 + hmac-sha2-512-etm memberikan keamanan transport yang kuat tanpa mengorbankan performa, bahkan di CPU tanpa hardware AES acceleration.


Distribusi SSH Key #

SSH key pair memberikan autentikasi yang lebih aman dari password. Distribusi kunci publik ke managed node harus diotomasi, tapi kunci privat harus tetap aman.

# Generate SSH key pair di control node
ssh-keygen -t ed25519 -C "ansible-control-prod" -f ~/.ssh/ansible_ed25519

# Public key di-distribute ke managed node via Ansible
ansible all -m authorized_key -a "
  user=ansible
  key='{{ lookup('file', '~/.ssh/ansible_ed25519.pub') }}'
  state=present
"
# ANTI-PATTERN: kunci SSH tanpa passphrase, copy manual ke setiap server
# ssh-keygen -t rsa -b 2048 -N "" -f deploy_key
# scp deploy_key.pub user@server1:~/.ssh/authorized_keys
# scp deploy_key.pub user@server2:~/.ssh/authorized_keys
# ... (ribuan server)
# scp deploy_key user@laptop:/tmp/  # bocor ke laptop baru

# BENAR: passphrase + Ansible push + rotation berkala
# 1. Generate key dengan passphrase, simpan passphrase di Vault
# 2. Ansible push public key ke semua managed node
# 3. ssh-agent di control node memegang decrypted key saat runtime
# 4. Rotate keypair setiap 6-12 bulan
- name: Distribusikan SSH public key baru ke managed node
  hosts: all
  tasks:
    - name: Tambahkan ansible public key ke authorized_keys
      authorized_key:
        user: ansible
        key: "{{ lookup('file', '~/.ssh/ansible_ed25519.pub') }}"
        state: present
        exclusive: true  # Hapus key lain, pastikan hanya key baru yang valid
      notify: Audit SSH key change

Kunci SSH tanpa passphrase di laptop develop adalah mimpi buruk keamanan — begitu laptop dicuri, attacker punya akses ke seluruh infrastruktur. Passphrase + ssh-agent memberikan keamanan tanpa mengorbankan kenyamanan.


Sertifikat SSL/TLS #

Service internal (nginx, PostgreSQL, dll) sering menggunakan TLS untuk komunikasi terenkripsi. Sertifikat harus di-generate, didistribusikan, dan di-rotate secara berkala.

# roles/tls-cert/tasks/main.yml
---
- name: Generate private key
  community.crypto.openssl_privatekey:
    path: /etc/ssl/private/{{ service_name }}.key
    size: 4096
    type: RSA
    mode: '0600'
    owner: root
    group: ssl-cert
  register: private_key

- name: Generate CSR (Certificate Signing Request)
  community.crypto.openssl_csr:
    path: /etc/ssl/csr/{{ service_name }}.csr
    privatekey_path: /etc/ssl/private/{{ service_name }}.key
    common_name: "{{ service_name }}.internal.example.com"
    subject_alt_name:
      - "DNS:{{ service_name }}.internal.example.com"
      - "DNS:{{ service_name }}"
    organization_name: "Example Corp"
    organizational_unit_name: "Infrastructure"
    country_name: "ID"
    key_usage:
      - digitalSignature
      - keyEncipherment
    extended_key_usage:
      - serverAuth
      - clientAuth
  when: private_key.changed

- name: Self-sign certificate (untuk internal CA)
  community.crypto.x509_certificate:
    path: /etc/ssl/certs/{{ service_name }}.crt
    privatekey_path: /etc/ssl/private/{{ service_name }}.key
    csr_path: /etc/ssl/csr/{{ service_name }}.csr
    provider: selfsigned
    selfsigned_not_after: "+365d"  # Berlaku 1 tahun
    mode: '0644'
    owner: root
    group: root
  when: private_key.changed

Untuk service internal, self-signed certificate dari private CA internal sudah cukup. Untuk service yang diakses dari internet, gunakan Let’s Encrypt atau CA eksternal lain dengan ACME protocol untuk auto-renewal.


Sertifikat self-signed perlu di-trust secara eksplisit. Browser dan client TLS lain akan menolak sertifikat self-signed dari CA yang tidak dikenal. Untuk infrastruktur internal, deploy CA certificate ke semua managed node (via Ansible, tentu) dan tambahkan ke system trust store. Tanpa langkah ini, service TLS internal akan selalu gagal handshake dengan error “certificate verify failed”.

Enkripsi Backup #

Backup sering menjadi titik lemah dalam strategi enkripsi — dibuat dengan database aktif (plaintext), disimpan di disk atau cloud storage tanpa enkripsi, dan membawa data sensitif ke permukaan attack yang lebih luas. Backup harus dienkripsi sebelum meninggalkan host asal.

# Backup PostgreSQL + encrypt dengan GPG sebelum upload
pg_dump -Fc production_db > /tmp/production_db.dump
gpg --symmetric --cipher-algo AES256 \
    --output /backup/production_db.dump.gpg \
    /tmp/production_db.dump
rm -f /tmp/production_db.dump  # Hapus plaintext
aws s3 cp /backup/production_db.dump.gpg \
    s3://company-backups/production/$(date +%Y%m%d)/
shred -u /backup/production_db.dump.gpg  # Hapus lokal setelah upload

Atau gunakan LUKS/dm-crypt untuk full disk encryption pada volume backup — seluruh volume dienkripsi pada level block device, transparan untuk aplikasi.


Redact Data Sensitif di Log #

Log yang menampilkan password, API key, atau private key di plaintext adalah kebocoran data yang tertunda — biasanya tidak terdeteksi sampai audit atau sampai log terekspos ke pihak ketiga.

# ANTI-PATTERN: log output yang menampilkan rendered template
# ansible-playbook site.yml -vvv
# TASK [Deploy config] ****
# ok: [server1] => {
#     "msg": "Rendered template content: db_password=MyS3cretP@ss"
# }
# Log ini sekarang ada di syslog, journal, dan mungkin CI artifact

# BENAR: no_log: true untuk task yang render secret
- name: Deploy konfigurasi database
  template:
    src: db.conf.j2
    dest: /etc/myapp/db.conf
    mode: '0640'
  no_log: true  # Jangan log output task ini

- name: Test koneksi database
  command: psql -f /etc/myapp/db.conf -c "SELECT 1"
  no_log: true  # Output query mungkin bocor credential
  changed_when: false

no_log: true adalah satu line yang mencegah banyak insiden. Output task tidak akan muncul di Ansible log, syslog, atau CI artifact. Untuk CI/CD yang menyimpan log artifact, tanpa no_log: true kita secara efektif menulis password ke public storage.


Decision Tree — Pilih Pendekatan Enkripsi #

flowchart TD
    A["Data apa<br/>yang dienkripsi?"] --> B{"Kategori"}
    B -->|"Secret variable<br/>password, API key"| C["Vault file<br/>AES-256"]
    B -->|"Transport<br/>SSH/TLS"| D["SSH key pair<br/>atau TLS cert"]
    B -->|"Disk/storage"| E["LUKS/dm-crypt"]
    B -->|"Backup file"| F["gpg symmetric<br/>atau LUKS"]
    B -->|"Password user"| G["bcrypt cost 12+<br/>atau Argon2id"]
    C --> H{"Environment?"}
    H -->|"Production"| I["Vault terpisah<br/>+ AWS SM inject"]
    H -->|"Dev/staging"| J["Vault terpisah<br/>+ env var password"]
    D --> K{"Internal atau<br/>public-facing?"}
    K -->|"Internal"| L["Self-signed cert<br/>+ private CA"]
    K -->|"Public"| M["Let's Encrypt<br/>+ ACME renewal"]

Decision tree ini membantu memilih pendekatan enkripsi yang tepat untuk berbagai jenis data. Kuncinya adalah klasifikasi data dulu — baru pilih algoritma.


Ringkasan #

  • AES-256 via Vault adalah default untuk data at rest di Ansible. Gunakan Vault terpisah per environment, dan inject vault password dari secret manager eksternal saat runtime.
  • Ed25519 adalah pilihan terbaik untuk SSH key — cepat, kecil, dan aman. Selalu gunakan passphrase dan simpan decrypted key di ssh-agent, jangan pernah di file tanpa passphrase.
  • TLS internal bisa pakai self-signed certificate dari private CA — deploy CA certificate ke semua managed node via Ansible untuk trust.
  • Backup harus dienkripsi sebelum meninggalkan host — GPG symmetric untuk file individual, LUKS untuk full disk encryption pada volume backup.
  • no_log: true adalah satu line wajib untuk task yang render template berisi credential atau yang output-nya mungkin bocor secret.
  • bcrypt cost factor ≥ 12 atau Argon2id untuk password hashing. Jangan pernah simpan password plaintext atau hash dengan SHA1/MD5.
  • Rotasi berkala — password Vault, API key, dan SSH key harus di-rotate minimal setiap 90 hari. Ansible dapat mengotomasi seluruh proses rotasi.

← Sebelumnya: Ansible Vault   Berikutnya: Workflow →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact