Ansible Vault: git मध्ये secrets सुरक्षित कसे ठेवावे
Ansible Vault मध्ये vars file किंवा एक inline string encrypt करा, staging आणि production वेगळे ठेवा आणि rekey करताना कोणती version-specific मर्यादा लागू होते ते जाणून घ्या.
Ansible Vault काय सुरक्षित करते आणि काय सुरक्षित करत नाही
Ansible Vault तुमच्या playbook repository मधील secrets encrypt करते. त्यामुळे git मध्ये plaintext password ऐवजी ciphertext साठवला जातो. ansible-vault command संपूर्ण file किंवा file मधील एकच value encrypt करते. यासाठी तुम्ही निवडलेल्या password पासून तयार केलेली symmetric key वापरली जाते. play चालू असताना Ansible तो content memory मध्ये decrypt करते. त्यामुळे तो variable इतर कोणत्याही variable प्रमाणेच काम करतो.
या पद्धतीची एक स्पष्ट मर्यादा आहे. Vault repository मध्ये at rest असलेल्या secret चे संरक्षण करते आणि त्यापलीकडे काहीही करत नाही. एखादे task चालल्यानंतर, तुम्ही ते थांबवले नाही तर ती value memory मध्ये, rendered template मध्ये, module arguments मध्ये आणि run output मध्ये plaintext स्वरूपात असते. playbook चालवू शकणाऱ्या प्रत्येक व्यक्तीकडे vault password असतो. त्यामुळे Vault टीमबाहेरील लोकांपासून secrecy देते; टीममधील प्रत्येक व्यक्तीसाठी स्वतंत्र access control देत नाही.
तुम्ही अद्याप playbook लिहिले नसेल, तर VPS साठी पहिले Ansible playbook पासून सुरुवात करा. त्या playbook ला password आवश्यक झाल्यावर येथे परत या.
Encrypt a whole file, or a single string?
ansible-vault encrypt replaces a file with ciphertext. The file becomes one block of base64 text under a header line beginning with $ANSIBLE_VAULT. Use it when the file contains nothing but secrets.
ansible-vault encrypt_string encrypts one value and prints a YAML snippet you paste into an ordinary vars file. The variable name stays readable and only the value is ciphertext. Use it when secrets sit beside plaintext settings.
The difference that matters in daily work is the diff. A vault file is re-encrypted with a fresh random salt every time you save it, so every byte of the ciphertext changes. git diff then shows one unreadable block replaced by another unreadable block, which means a reviewer cannot tell whether you rotated one password or rewrote the file. With encrypt_string, each secret is its own block inside a plaintext file, so a diff shows exactly which variable changed and leaves the rest of the file alone.
The inline form has a cost, and it arrives at rotation time: ansible-vault rekey does not touch inline blocks. Choose the file form when the secret list is long and changes rarely. Choose the inline form when the file mixes secrets with normal variables and you want code review to mean something.
संरक्षित मूल्ये स्पष्ट दिसतील अशी group_vars मांडणी
Ansible group_vars/<group>.yml लोड करते आणि group_vars/<group>/ निर्देशिकेतील प्रत्येक फाइलही लोड करते. निर्देशिका-आधारित मांडणी वापरणे योग्य आहे, कारण त्यामुळे एकाच group मध्ये plaintext फाइल आणि encrypted फाइल शेजारी ठेवता येतात.
inventory/
hosts.ini
group_vars/
all/
vars.yml
vault.yml
web/
vars.yml
vault.yml
host_vars/
db01/
vars.yml
vault.yml
playbooks/
site.ymlप्रत्येक vault.yml encrypted असते. प्रत्येक vars.yml plaintext असते. कोणते मूल्य संरक्षित आहे हे वाचक कोणतीही फाइल न उघडता पाहू शकतो, कारण फाइलचे नाव ते स्पष्ट करते.
या नमुन्याचा दुसरा भाग indirection आहे. encrypted फाइलमध्ये प्रत्येक variable च्या नावापूर्वी vault_ लावा.
vault_db_password: "a real password"
vault_grafana_admin_token: "a real token"त्यानंतर plaintext फाइलमधून, तिच्या शेजारी असलेल्या त्या नावांचा reference द्या.
db_password: "{{ vault_db_password }}"
grafana_admin_token: "{{ vault_grafana_admin_token }}"Roles आणि templates मध्ये db_password वापरले जाते. मूल्य कुठून आले आहे हे त्यांना कधीही माहीत नसते. त्यामुळे playbook आणि role यांच्यातील विभाजन स्पष्ट राहते. Plaintext vars.yml searchable index म्हणूनही काम करते: grep -r vault_ group_vars/ repository ला अपेक्षित असलेली प्रत्येक secret सूचीबद्ध करते, कोणतेही मूल्य decrypt न करता. यासाठी प्रत्येक secret साठी एक अतिरिक्त नाव ठेवावे लागते. तसेच vault_ नावात टायपो असल्यास तो syntax error म्हणून नव्हे, तर run time वेळी undefined variable म्हणून आढळतो.
encrypt_string ने एक चल वापरा कूटबद्ध करा
ansible-vault encrypt_string --vault-id prod@~/.ansible/vault-prod.txt \
--stdin-name 'vault_db_password'गुप्त मूल्य टाइप करा आणि नंतर Ctrl-D दाबा. --stdin-name हे मूल्य standard input मधून वाचते. त्यामुळे ते तुमच्या shell history file मध्ये नोंदवले जात नाही. दुसऱ्या पद्धतीत मूल्य command line वर दिले जाते. त्यामुळे shell ते नोंदवते:
ansible-vault encrypt_string --vault-id prod@~/.ansible/vault-prod.txt \
'a real password' --name 'vault_db_password'दोन्हीपैकी कोणत्याही पद्धतीने command एक YAML block छापते. तो vars file मध्ये जसा छापला आहे तसाच paste करा. कारण !vault tag अंतर्गत असलेले indentation हे मूल्याचाच भाग आहे.
vault_db_password: !vault |
$ANSIBLE_VAULT;1.2;AES256;prod
6638643965323633646262656665306333616466396630323136393465356136396436383331
3131303163306665326539353837343663313762616561306534373963383531613664393332!vault tag YAML loader ला सांगतो की scalar हा text नसून ciphertext आहे. Header मध्ये format version, cipher आणि ते कूटबद्ध करणारा vault ID label असतो. vault ID शिवाय कूटबद्ध केलेल्या मूल्यामध्ये label नसलेला 1.1 header असतो. तो देखील कार्य करतो; मात्र password कुठून आले हे त्यातून कमी स्पष्ट होते.
Vault password कुठे ठेवावे?
Repository च्या बाहेर. हा एकमेव नियम आहे ज्याला कोणताही अपवाद नाही.
--ask-vault-pass प्रत्येक run मध्ये एकदाच prompt दाखवते आणि काहीही साठवत नाही. Laptop साठी ही योग्य आहे; परंतु cron job किंवा CI runner साठी योग्य नाही.
Password file ही plain text file असते. तिच्या पहिल्या ओळीत password असतो. ती tight permissions सह रिकामी तयार करा आणि नंतर editor मध्ये password भरा. त्यामुळे password तुमच्या shell history मध्ये जाणार नाही:
mkdir -p ~/.ansible
install -m 600 /dev/null ~/.ansible/vault-prod.txt
$EDITOR ~/.ansible/vault-prod.txt--vault-password-file वापरून कोणत्याही command ला ती file द्या:
ansible-playbook -i inventory/hosts.ini playbooks/site.yml \
--vault-password-file ~/.ansible/vault-prod.txtप्रत्येक command मध्ये हा flag पुन्हा लिहिणे सहज विसरता येते. त्यामुळे repository च्या root मधील ansible.cfg मध्ये तो एकदाच सेट करा.
[defaults]
inventory = inventory/hosts.ini
vault_password_file = ~/.ansible/vault-prod.txtहीच setting ANSIBLE_VAULT_PASSWORD_FILE environment variable मधूनही वाचली जाते. CI job सामान्यतः याच पद्धतीने ती पुरवते. Job स्वतःच्या credential store मधून password घेऊन temporary directory मधील file मध्ये लिहिते, variable export करते आणि run संपल्यावर file delete करते. .gitignore मध्ये filename pattern देखील जोडा. कारण ansible.cfg मधील path commit केला जातो आणि लवकरच कोणीतरी checkout च्या आत खरी file तयार करेल.
Password file executable असल्यास Ansible ती run करते आणि file text म्हणून वाचण्याऐवजी तिच्या standard output मधून password वाचते. अशा प्रकारे password disk वर अजिबात न लिहिता system keyring किंवा cloud secret manager मधून vault password घेता येतो. --vault-id द्वारे वापरल्या जाणाऱ्या script साठी काही अतिरिक्त आवश्यकता आहेत: तिचे नाव -client ने किंवा extension असलेल्या -client ने समाप्त झाले पाहिजे, ती executable असली पाहिजे, तिने --vault-id option स्वीकारला पाहिजे आणि password standard output वर छापला पाहिजे.
दोन vault ID: staging आणि production
vault ID हा vault password ला जोडलेला label असतो आणि तो label@source म्हणून लिहिला जातो. Source म्हणजे prompt, password file चा path किंवा client script चा path. Labels मुळे एकाच repository मध्ये एकापेक्षा अधिक password अंतर्गत secrets ठेवता येतात. त्यामुळे staging password ने production file उघडत नाही.
ansible-vault encrypt --vault-id staging@~/.ansible/vault-staging.txt \
group_vars/staging/vault.yml
ansible-vault encrypt --vault-id prod@~/.ansible/vault-prod.txt \
group_vars/prod/vault.ymlएका run ला आवश्यक असलेले प्रत्येक ID द्या:
ansible-playbook playbooks/site.yml \
--vault-id staging@~/.ansible/vault-staging.txt \
--vault-id prod@~/.ansible/vault-prod.txtकिंवा ते एकदाच ansible.cfg मध्ये नमूद करा:
[defaults]
vault_identity_list = staging@~/.ansible/vault-staging.txt, prod@~/.ansible/vault-prod.txtएका वर्तनामुळे अनेकांना आश्चर्य वाटते. Default नुसार label हा lock नसून hint असतो. Ansible सध्या उपलब्ध असलेले प्रत्येक secret file वर वापरून पाहते आणि त्यापैकी एखाद्याने file decrypt होईपर्यंत प्रयत्न करते. त्यामुळे staging label असलेली file production password योग्य key असल्यास उघडते. [defaults] अंतर्गत vault_id_match = True सेट करा किंवा ANSIBLE_VAULT_ID_MATCH environment variable सेट करा. त्यानंतर Ansible file header शी जुळणाऱ्या label असलेले secretच वापरते. या तपासणीसाठी 1.2 header आवश्यक आहे. त्यामुळे सुरुवातीपासून vault ID वापरून encrypted केलेल्या content वरच हे लागू होते.
एकापेक्षा अधिक ID loaded असतील, तर ansible-vault encrypt कोणत्या password ने encrypt करायचे हे स्वतः ठरवू शकत नाही. ते --encrypt-vault-id prod ने निर्दिष्ट करा किंवा repository साठी default सेट करण्याकरिता ansible.cfg मध्ये vault_encrypt_identity सेट करा.
याचा मुख्य फायदा deployment scope मध्ये दिसतो. staging deploy करणाऱ्या CI job ला फक्त staging password दिला जातो. त्यामुळे breached runner production credentials वाचू शकत नाही. एकाच control machine वरून Linux servers च्या समूहावर plays चालवत असताना हे विभाजन लहान incident आणि अतिशय मोठ्या incident मधील फरक ठरते.
एखादी व्यक्ती टीममधून बाहेर पडल्यावर vault ची encryption key बदला
Rekey केल्याने vault चा password बदलतो आणि नवीन password वापरून सामग्री पुन्हा encrypt केली जाते. यामुळे आधी घडलेली कोणतीही गोष्ट पूर्ववत होत नाही. ज्याच्याकडे जुना password होता, तो repository ची स्वतःजवळ ठेवलेली कोणतीही प्रत अजूनही decrypt करू शकतो. त्या प्रतीमधील प्रत्येक जुना commit देखील decrypt करता येतो. त्यामुळे password धारक व्यक्ती बाहेर पडताच vault password उघड झालेला मानावा आणि खालील क्रमाने प्रक्रिया करा.
- Servers आणि third-party services वरील वास्तविक credentials बदला. Access प्रत्यक्षात रद्द करणारी हीच पायरी आहे.
ansible-vault editवापरून vault files मध्ये नवीन values भरा.- प्रत्येक encrypted file नवीन vault password ने पुन्हा encrypt करा.
- ज्यांना अजूनही password आवश्यक आहे, त्यांना नवीन vault password repository व्यतिरिक्त इतर channel द्वारे द्या.
ansible-vault rekey --vault-id prod@~/.ansible/vault-prod-old.txt \
--new-vault-id prod@prompt \
group_vars/prod/vault.yml host_vars/db01/vault.ymlrekey एका command मध्ये अनेक files स्वीकारते आणि --new-vault-id prod@prompt password disk वरून वाचण्याऐवजी तो एकदाच विचारते. Label बदलण्याचे कारण नसल्यास तोच label ठेवा, कारण command ज्या प्रत्येक file ला पुन्हा लिहिते, त्या file च्या header मध्ये label लिहिला जातो.
याठिकाणी inline form ची मर्यादा स्पष्ट होते. ansible-vault rekey पूर्णपणे encrypted files वर कार्य करते. त्यामुळे plaintext vars file मध्ये असलेला !vault block बदलला जात नाही. ते blocks आधी शोधा आणि नंतर नवीन password वापरून encrypt_string द्वारे प्रत्येक block पुन्हा तयार करा:
grep -rl '!vault' group_vars/ host_vars/पूर्ण trade-off असा आहे: inline blocks मुळे diffs वाचता येतात, परंतु rotation वेळी manually तपासणी करावी लागते. पूर्णपणे encrypted files एका command ने rotate करता येतात, परंतु review मध्ये त्यातून उपयुक्त माहिती मिळत नाही.
तुमच्या output मध्ये secret अजूनही का दिसते
Value decrypt होताच Vault चे काम पूर्ण होते. Ansible task चा result report करते आणि arguments echo करणारे module ते credential त्या report मध्ये समाविष्ट करते. Verbose run, template task वरील --diff, arguments dump करणारी failed task किंवा output file मध्ये लिहिणारे callback plugin — या सर्वांमध्ये plaintext राहू शकतो. File encrypt केल्याने यापैकी कोणतीही समस्या दूर होत नाही.
no_log: true हा switch आहे. Credential स्वीकारणाऱ्या प्रत्येक task वर तो सेट करा.
- name: Write the application environment file
ansible.builtin.template:
src: app.env.j2
dest: /etc/myapp/app.env
owner: myapp
group: myapp
mode: "0600"
no_log: trueत्यानंतर Ansible त्या task चा result output मध्ये दाखवत नाही. त्यामुळे log मध्ये task चालल्याची नोंद राहते, पण त्याने हाताळलेली value नोंदवली जात नाही. विशेषतः loops वर तो सेट करा. Loop प्रत्येक item साठी स्वतंत्र result report करते. Credential list वर चालवलेला loop संपूर्ण list report करतो.
Decrypted secret बाहेर पडण्याची आणखी चार ठिकाणे आहेत. no_log यापैकी कोणत्याही ठिकाणी लागू होत नाही:
- Template मधून render केलेल्या file ला तुम्ही दिलेले
modeआणिownerमिळतात. Credential असलेल्या कोणत्याही file साठीmode: "0600"आणि विशिष्ट owner सेट करा. अन्यथा target host वर secret सर्व users साठी readable होऊ शकते. ansible.builtin.commandकिंवाansible.builtin.shellला दिलेले secret command चालू असताना target host वरील process list मध्ये दिसते. तेथील कोणताही local user ते वाचू शकतो. त्याऐवजी ते file किंवा environment variable मार्फत द्या.- Fact caching गोळा केलेले facts control machine वर disk वर लिहिते. त्यामुळे secret असलेला registered variable संवेदनशील आहे असे कोणीही न मानलेल्या cache file मध्ये जाऊ शकतो.
- तोच secret सहसा दुसऱ्या ठिकाणीही असतो, जसे container ने वाचलेली environment file. तेथील नियम वेगळे आहेत. त्यासाठी Compose env files मधून credentials दूर ठेवणे हा भाग पहा.
no_log मुळे debugging अधिक कठीण होते. हाच त्याचा उद्देश आहे. एखादी task अपेक्षेप्रमाणे चालत नसेल, तर test host वर ते तात्पुरते काढा. बदल production मध्ये नेण्यापूर्वी ते पुन्हा सेट करा.
एन्क्रिप्ट केलेल्या फायली plaintext मागे न ठेवता वाचा आणि संपादित करा
ansible-vault view group_vars/prod/vault.yml pager मध्ये decrypt करते आणि disk वर काहीही लिहित नाही. ansible-vault edit temporary file मध्ये decrypt करते, तुमचे $EDITOR उघडते आणि ते बंद केल्यावर पुन्हा encrypt करते. ansible-vault decrypt पेक्षा या दोन्ही पद्धतींना प्राधान्य द्या. ansible-vault decrypt मुळे working tree मध्ये plaintext file राहते. चुकीने stage केलेली decrypted vault file ही वास्तविक credential public repository मध्ये पोहोचण्याची सर्वात सामान्य पद्धत आहे.
पूर्णपणे encrypt केलेल्या फायलींसाठी Git decrypt करताना readable diff दाखवू शकते:
git config --local diff.ansible-vault.textconv "ansible-vault view --vault-password-file ~/.ansible/vault-prod.txt"
printf '%s\n' 'group_vars/**/vault.yml diff=ansible-vault' >> .gitattributesते enable करण्यापूर्वी त्याचा परिणाम समजून घ्या. आता git diff production secrets तुमच्या terminal मध्ये print करेल. त्यामुळे ते terminal च्या scrollback मध्ये आणि कोणत्याही screen share मध्ये दिसू शकतात. ही एका मशीनवर एका व्यक्तीसाठीची local सुविधा आहे. त्यामुळे git config local ठेवा. इतर लोकांचे checkouts वेगळ्या पद्धतीने कार्य करतील, जोपर्यंत तेही हीच configuration सेट करत नाहीत.
Vault हे योग्य साधन राहात नाही तेव्हा
Vault हा प्रत्येक label साठी एक password असलेला file format आहे. त्यामुळे तो कुठे अपुरा पडतो हे या रचनेवर ठरते. खालीलपैकी कोणतीही बाब लागू असल्यास वास्तविक secret store वापरा.
- प्रत्येक व्यक्तीसाठी स्वतंत्र access आवश्यक आहे. Playbook चालवणाऱ्या प्रत्येकाकडे तोच password असतो. Vault IDs access ला environment नुसार वेगळे करतात, व्यक्तीनुसार कधीच नाही.
- Audit trail आवश्यक आहे. कोणत्या व्यक्तीने काय decrypt केले किंवा ते कधी केले, याची Vault मध्ये कोणतीही नोंद राहत नाही.
- ठरावीक वेळापत्रकानुसार rotation आवश्यक आहे. Vault मध्ये expiry किंवा versioning नाही. त्यामुळे एखादा credential दोन वर्षांपासून बदललेला नाही हे कळण्याची कोणतीही सूचना मिळत नाही.
- Application ला run time वर secret आवश्यक आहे. Boot वेळी database password वाचणाऱ्या service ने तो deployment repository मधून वाचू नये.
यानंतर रचना उलटते. Ansible secrets साठवणे थांबवते आणि lookup plugin द्वारे run time वर ते fetch करते. यासाठी HashiCorp Vault (समान नावामुळे गोंधळ होऊ शकणारे वेगळे product), cloud provider चा secret manager किंवा control machine वरील keyring वापरता येतो. Repository मध्ये path असतो, store मध्ये value असते आणि store access log ठेवतो. लहान team साठी API असलेला self-hosted password manager, जसे Vaultwarden server, हेच काम लहान प्रमाणात करू शकतो.
एक credential या सर्व प्रक्रियेबाहेर राहतो. Servers पर्यंत पोहोचण्यासाठी control machine वापरत असलेला SSH key ही Vault ची समस्या नाही, कारण कोणतेही play चालण्यापूर्वी Ansible ला तो आवश्यक असतो. तो agent आणि passphrase यांच्या साहाय्याने व्यवस्थापित करा. यासाठी SSH key management ची मूलतत्त्वे वापरा.
FAQ
संपूर्ण vars फाइल कूटबद्ध करावी की फक्त secret string?
फाइलमध्ये फक्त secrets असतील, तर संपूर्ण फाइल कूटबद्ध करा. त्यामुळे एका command ने सर्व secrets rotate करता येतात आणि मांडणी सोपी राहते. Secrets सामान्य variables च्या बाजूला असतील, तर ansible-vault encrypt_string वापरा. त्यामुळे diff मध्ये फक्त कूटबद्ध value बदलते आणि कोणत्या variable मध्ये बदल झाला हे reviewer पाहू शकतो. यामध्ये rotation हा तडजोडीचा मुद्दा आहे. ansible-vault rekey संपूर्ण फाइल्सवर लागू होते आणि inline !vault blocks तसेच ठेवते. त्यामुळे नवीन password वापरताना ते blocks manually पुन्हा तयार करावे लागतात.
Ansible Vault password file कुठे ठेवावी?
ती repository च्या बाहेर, mode 0600 सह, ~/.ansible/vault-prod.txt सारख्या path वर ठेवा. --vault-password-file वापरून तिच्याकडे निर्देश करा. किंवा ansible.cfg मधील [defaults] अंतर्गत vault_password_file सेट करा. आणखी एक पर्याय म्हणजे environment मध्ये ANSIBLE_VAULT_PASSWORD_FILE सेट करणे. CI मध्ये job ने स्वतःच्या credential store मधून password temporary file मध्ये लिहावा, variable export करावा आणि job संपल्यावर ती फाइल delete करावी. फाइल executable असल्यास Ansible ती run करते आणि standard output मधून password वाचते. त्यामुळे password disk वर साठवण्याऐवजी तो keyring मधून मिळवता येतो.
staging आणि production साठी वेगवेगळे vault passwords कसे वापरावे?
--vault-id staging@/path/to/file आणि --vault-id prod@/path/to/file वापरून प्रत्येक password ला label द्या. त्यानंतर प्रत्येक environment च्या files त्याच्या स्वतंत्र label अंतर्गत encrypt करा. Run time ला दोन्ही IDs pass करा. किंवा [defaults] अंतर्गत vault_identity_list मध्ये त्यांची यादी द्या. By default Ansible कडे असलेला प्रत्येक secret वापरून पाहते, जोपर्यंत फाइल decrypt होत नाही. फाइल header शी जुळणाऱ्या label असलेलाच secret वापरून पाहायचा असल्यास vault_id_match = True सेट करा. अनेक IDs load केले असतील, तर encryption साठी वापरायचा ID --encrypt-vault-id ने निवडा.
Ansible Vault मुळे run output मध्ये password दिसणे थांबते का?
नाही. Vault repository मध्ये साठवलेल्या secret चे संरक्षण करते. Task run झाल्यावर value plaintext असते. Verbose run किंवा failed task मुळे ती log मध्ये जाऊ शकते. Credential हाताळणाऱ्या प्रत्येक task मध्ये no_log: true जोडा. Template करून तयार केलेल्या कोणत्याही file वर restrictive mode आणि owner सेट करा. Secrets command arguments म्हणून pass करणे टाळा. Command सुरू असताना ते target host वरील process list मध्ये दिसू शकतात.