إدارة الإعدادات مع Ansible

Ansible Vault وإدارة الأسرار

18 دقيقة الدرس 8 من 30

Ansible Vault وإدارة الأسرار

يصل كل مشروع Ansible في بيئة الإنتاج إلى نقطة تحوّل واحدة حتماً: تحتاج إلى تخزين كلمة مرور قاعدة بيانات، أو رمز API، أو مفتاح SSH خاص، أو شهادة TLS جنباً إلى جنب مع الـ playbooks — لكن لا يمكنك إيداع أسرار نصية صريحة في نظام إدارة الإصدارات. Ansible Vault هو الحل المدمج. يوفر تشفير AES-256-GCM لمتغيرات فردية، أو ملفات متغيرات كاملة، أو ملفات ثنائية اعتباطية، ويتكامل بسلاسة مع خطوط أنابيب CI/CD عبر نظام vault-ID مرن. على نطاق شركات مثل Google وNetflix وStripe، يُدمج الفرق Ansible Vault مع مدير الأسرار (AWS Secrets Manager، أو HashiCorp Vault، أو GCP Secret Manager) — لكن حتى في هذه الحالة، تظل الملفات المشفرة بـ Vault خط الدفاع الأخير حين تكون مخازن الأسرار الخارجية غير متاحة عند التطبيق. أتقن Vault جيداً قبل أن تلجأ إلى أدوات أكثر تعقيداً.

نموذج التشفير

يُشفّر Ansible Vault البيانات في حالة السكون باستخدام AES-256-GCM مع مفتاح مشتق من كلمة مرور vault عبر PBKDF2-SHA256 (600,000 تكرار اعتباراً من Ansible 2.17). يُرمَّز النص المشفر بـ base64 ويُلف في ترويسة مميزة حتى تعرف Ansible أي الملفات تفكّ تشفيرها عند التشغيل. توجد درجتان من الدقة:

  • الملفات المشفرة — يُشفَّر ملف متغيرات YAML كامل أو أي ملف اعتباطي (مفتاح خاص، حزمة PKCS#12) ككتلة واحدة. الملف غير مقروء في المستودع؛ تفكّ Ansible تشفيره في الذاكرة عند وقت التنفيذ.
  • السلاسل المشفرة المضمّنة (سلاسل YAML مُعلَّمة بـ !vault) — يُشفَّر قيمة السر فقط؛ يبقى اسم المتغير مقروءاً. هذا هو الاختيار الصحيح دائماً تقريباً: يتيح مخرجات git diff ذات معنى ويتيح للمراجعين رؤية المتغيرات التي تغيّرت دون رؤية قيمها.
فكرة محورية — يُفضَّل استخدام السلاسل المشفرة المضمّنة على الملفات المشفرة. تشفير ملف group_vars/production/vault.yml بالكامل يُخفي كل شيء — بما فيه أسماء المتغيرات — مما يجعل مراجعة الكود مؤلمة. شفّر قيم الأسرار فقط باستخدام ansible-vault encrypt_string، واحتفظ بها في ملف YAML هيكلي نصي صريح، وستبقى طلبات السحب قابلة للمراجعة.

العمليات الأساسية لـ Vault

الواجهة الرئيسية هي ansible-vault. كل عملية تقبل إما موجه كلمة مرور تفاعلياً أو ملف كلمة مرور (ملف نصي عادي يحتوي على كلمة المرور، مقيّد بصلاحية 0600). في خطوط الأنابيب، استخدم دائماً ملف كلمة مرور أو سكريبت vault-ID — ولا تستخدم الموجهات التفاعلية أبداً.

# --- إنشاء ملف مشفر جديد --- ansible-vault create group_vars/production/vault.yml # --- تشفير ملف نصي موجود --- ansible-vault encrypt group_vars/production/secrets.yml # --- فك التشفير في المكان (لا تفعل هذا أبداً في مستودعات الإنتاج) --- ansible-vault decrypt group_vars/production/secrets.yml # --- عرض ملف مشفر دون فك تشفيره على القرص --- ansible-vault view group_vars/production/vault.yml # --- تحرير ملف مشفر (يفتح $EDITOR، يعيد التشفير عند الحفظ) --- ansible-vault edit group_vars/production/vault.yml # --- إعادة المفتاح (تدوير كلمة مرور التشفير) --- ansible-vault rekey group_vars/production/vault.yml # --- تشفير سلسلة واحدة (سر مضمّن) --- # اللصق مباشرة في ملف متغيرات YAML كوحدة !vault ansible-vault encrypt_string 'S3cr3tP@ssw0rd!' --name 'db_password' # مثال على الناتج (يُلصق في ملف المتغيرات كما هو): # db_password: !vault | # $ANSIBLE_VAULT;1.1;AES256 # 38623930336162366631... # ... # --- تشغيل playbook مع Vault --- # موجه تفاعلي: ansible-playbook site.yml --ask-vault-pass # ملف كلمة مرور (مفضّل في الأتمتة): ansible-playbook site.yml --vault-password-file ~/.vault_pass # معرفات vault متعددة: ansible-playbook site.yml \ --vault-id dev@~/.vault_pass_dev \ --vault-id prod@~/.vault_pass_prod

معرّفات Vault — الطريقة الصحيحة للتعامل مع بيئات متعددة

معرّف Vault هو تسمية مرتبطة بحمولة مشفرة، مُقدَّمة في Ansible 2.4. حين تشفّر سراً باستخدام --vault-id dev@password_file، تُضمّن Ansible التسمية dev في ترويسة النص المشفر. عند التشغيل، تُطابق Ansible كل قيمة مشفرة مع كلمة المرور الصحيحة تلقائياً — يمكنك توفير عشرات معرفات vault وستجرّب Ansible كلاً منها حتى تنجح.

هذا يحل مشكلة الأسرار متعددة البيئات بأناقة: تعيش أسرار بيئات dev وstaging وprod في المستودع نفسه، مشفرة بكلمات مرور منفصلة. المطور لديه كلمة مرور dev؛ خط أنابيب CI لديه كلمة مرور prod؛ ولا يمكن لأيٍّ منهما قراءة أسرار الآخر.

# تشفير بتسمية vault ID (@ يفصل التسمية عن مصدر كلمة المرور) ansible-vault encrypt_string 'prod-db-secret' \ --vault-id prod@~/.vault_pass_prod \ --name 'db_password' # تشفير سر dev بمعرف dev ansible-vault encrypt_string 'dev-db-secret' \ --vault-id dev@~/.vault_pass_dev \ --name 'db_password' # تشغيل playbook بكلا المعرفَين — تطابق Ansible تلقائياً ansible-playbook site.yml \ --vault-id dev@~/.vault_pass_dev \ --vault-id prod@~/.vault_pass_prod # في ansible.cfg، اضبط معرفات vault الافتراضية لتجنب خيارات CLI: # [defaults] # vault_identity_list = dev@~/.vault_pass_dev, prod@~/.vault_pass_prod
Ansible Vault ID flow across dev, staging, and prod environments Git Repo vault_dev.yml vault-id: dev vault_staging.yml vault-id: staging vault_prod.yml vault-id: prod Developer ~/.vault_pass_dev CI Pipeline $VAULT_PASS_STAGING Prod CD System $VAULT_PASS_PROD Ansible Vault ID matching AES-256-GCM decrypt in-memory only Fleet hosts plaintext in-memory
تدفق معرّف Vault: تُشفَّر أسرار كل بيئة بمعرف vault منفصل؛ تُطابق Ansible كلمة المرور الصحيحة عند التشغيل وتفكّ التشفير في الذاكرة فقط — لا يلمس النص الصريح القرص أبداً.

استخدام أسرار Vault في Playbooks والقوالب

بمجرد تعريف متغير مشفر بـ Vault (في group_vars/ أو host_vars/ أو ملف متغيرات مُدرَج)، تستخدمه تماماً كأي متغير Ansible آخر. تفكّ Ansible تشفيره بشفافية في بداية التشغيل. في قوالب Jinja2، يتوسع المتغير إلى قيمته النصية الصريحة على العقدة المُدارة — لكن النص الصريح لا يُكتب أبداً على قرص عقدة التحكم.

# group_vars/production/vars.yml (هيكل نصي صريح — مُودَع في git) db_host: db.prod.internal db_port: 5432 db_name: appdb db_user: appuser db_password: !vault | $ANSIBLE_VAULT;1.2;AES256;prod 61616262333130336637... ... # templates/database.conf.j2 (قالب Jinja2 — مُودَع في git) [database] host = {{ db_host }} port = {{ db_port }} name = {{ db_name }} user = {{ db_user }} password = {{ db_password }} # مهمة playbook لنشر الإعداد - name: Deploy database config ansible.builtin.template: src: templates/database.conf.j2 dest: /etc/app/database.conf owner: appuser group: appuser mode: '0600' # حرج: ملفات الإعداد التي تحتوي أسراراً يجب أن تكون 0600 no_log: true # يمنع ظهور مخرجات المهمة لحماية السر من التسجيل
خطأ إنتاجي شائع — اضبط دائماً no_log: true على المهام التي تتعامل مع متغيرات الأسرار. بدونه، ستُسجّل مخرجات Ansible التفصيلية (-v) وملحقات callback (AWX وSemaphore وDatadog) قيمة السر المفكوك تشفيره في stdout والملفات السجلية ونظام SIEM. مهندس واحد يشغّل -vvv في شاشة طرفية مشتركة يكفي لكشف كلمة مرور قاعدة بيانات الإنتاج. أضف no_log: true إلى كل مهمة قد تحتوي معاملاتها أو مخرجاتها المُسجَّلة على سر — إنها الحادثة الإنتاجية الأكثر شيوعاً المرتبطة بـ Vault.

Vault في خطوط أنابيب CI/CD

النمط القياسي في خطوط الأنابيب: خزّن كلمة مرور Vault كسر CI (سر GitHub Actions، أو متغير مُخفى في GitLab CI، أو بيانات اعتماد Jenkins)، اكتبها في ملف مؤقت عند بدء الوظيفة، مرّر --vault-password-file إلى Ansible، واحذف الملف بأمان عند انتهاء الوظيفة. لا تمرّر كلمة المرور مباشرةً كوسيطة CLI — إذ تظهر في ps aux وسجل الشل.

# مثال على GitHub Actions (.github/workflows/deploy.yml) jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Write vault password file run: | printf '%s' "$VAULT_PASS_PROD" > /tmp/.vault_pass chmod 600 /tmp/.vault_pass env: VAULT_PASS_PROD: ${{ secrets.VAULT_PASS_PROD }} - name: Run Ansible playbook run: | ansible-playbook -i inventory/prod site.yml \ --vault-password-file /tmp/.vault_pass env: ANSIBLE_HOST_KEY_CHECKING: "False" - name: Shred vault password file if: always() # يعمل حتى لو فشلت خطوة playbook run: shred -u /tmp/.vault_pass # لخطوط أنابيب مبنية على vault-ID (موصى به للمستودعات متعددة البيئات): # - name: Run with vault IDs # run: | # ansible-playbook -i inventory/prod site.yml \ # --vault-id prod@/tmp/.vault_pass_prod \ # --vault-id shared@/tmp/.vault_pass_shared
نصيحة احترافية — استخدم سكريبت كلمة مرور Vault للأسرار الديناميكية. وسيطة --vault-password-file تقبل أي سكريبت قابل للتنفيذ، وليس فقط ملفاً ثابتاً. وجّهها نحو سكريبت يجلب كلمة المرور من AWS Secrets Manager أو HashiCorp Vault أو GCP Secret Manager عند التشغيل. هذا يُلغي ملفات كلمات مرور Vault طويلة الأمد كلياً ويتيح لك تدوير كلمة المرور الجذرية في مدير الأسرار دون لمس أي ملفات Ansible:

#!/usr/bin/env python3 # scripts/vault_pass.py — يجلب كلمة مرور vault من AWS Secrets Manager import boto3, sys client = boto3.client('secretsmanager', region_name='us-east-1') secret = client.get_secret_value(SecretId='ansible/vault/prod') print(secret['SecretString'], end='') # بدون سطر جديد في النهاية # الاستخدام: # ansible-playbook site.yml --vault-id prod@scripts/vault_pass.py # chmod +x scripts/vault_pass.py -- يجب أن يكون قابلاً للتنفيذ

تشفير ملفات غير YAML

لا يقتصر Vault على ملفات YAML. يمكنك تشفير مفاتيح TLS الخاصة، أو مفاتيح SSH الخاصة، أو حزم PKCS#12، أو أي ملف ثنائي. تفكّ Ansible تشفير هذه الملفات في موقع مؤقت على العقدة المُدارة، تستخدمها (مثلاً في وحدة copy)، ثم تحذف الملف المؤقت. سير العمل مطابق لملفات YAML المشفرة:

# تشفير مفتاح TLS خاص للتخزين في git ansible-vault encrypt --vault-id prod@~/.vault_pass_prod \ files/tls/api.prod.internal.key # نشره بوحدة copy — تفكّ Ansible تشفيره بشفافية - name: Deploy TLS private key ansible.builtin.copy: src: files/tls/api.prod.internal.key # مشفر بـ Vault على القرص dest: /etc/ssl/private/api.key owner: root group: ssl-cert mode: '0640' no_log: true # التحقق من أن الملف لا يزال مشفراً في مستودع git: # head -1 files/tls/api.prod.internal.key # $ANSIBLE_VAULT;1.2;AES256;prod

تدوير المفاتيح وإعادة المفتاح

يجب تدوير كلمات مرور Vault بشكل دوري وفوراً عند أي اشتباه بالاختراق. أمر rekey يُعيد تشفير كل المحتوى بكلمة مرور جديدة دون فك تشفيره على القرص:

# إعادة مفتاح ملف واحد ansible-vault rekey \ --vault-id prod@~/.vault_pass_prod_old \ --new-vault-id prod@~/.vault_pass_prod_new \ group_vars/production/vault.yml # إعادة مفتاح جميع ملفات vault في شجرة مجلد find . -name "vault*.yml" -exec \ ansible-vault rekey \ --vault-id prod@~/.vault_pass_prod_old \ --new-vault-id prod@~/.vault_pass_prod_new \ {} \; # بعد إعادة المفتاح: حدّث كلمة المرور في مدير الأسرار، # ألغِ كلمة المرور القديمة، واحذف ملف كلمة المرور القديمة. shred -u ~/.vault_pass_prod_old
المعيار التشغيلي للشركات الكبرى — تعامل مع كلمات مرور Vault كبيانات اعتماد PAM. يجب أن تكون: مخزونة فقط في مدير الأسرار (ليس في مديري كلمات المرور أو البريد الإلكتروني)، مُدارة بالتدوير كل 90 يوماً على الأقل وعند مغادرة أي عضو في الفريق، مرتبطة بهوية خدمة مع تسجيل مراجعة، ولا تُشارك أبداً عبر Slack أو البريد الإلكتروني. على نطاق واسع، كلمة مرور Vault نفسها هي أعلى قيمة للسر في نشر Ansible — اختراق كلمة مرور vault يكشف كل سر في كل بيئة تستخدم ذلك المعرف.

Ansible Vault أساس قوي للأسرار في حالة السكون، لكنه طبقة واحدة فقط من استراتيجية أسرار متكاملة. ادمجه مع بيانات اعتماد قصيرة الأجل من مزود السحابة (أدوار AWS IAM، أو GCP Workload Identity)، ودوّر كلمات مرور قواعد البيانات عبر الأسرار الديناميكية لـ Vault أو تدوير AWS Secrets Manager، وراجع الوصول إلى ملفات كلمات مرور Vault بنفس صرامة مراجعة مفاتيح SSH الجذرية. المهندسون الذين يُستدعَون في الثانية الثانية من الليل بسبب تسرّب سر هم دائماً من تخطّوا إحدى هذه الطبقات.