Ansible je orchestrační systém stejně jako Puppet. Slouží k nastavování hostitelů (serverů nebo pracovních stanic) do požadovaného stavu.
Co se dozvíte v článku
Logika Puppetu je v tom, že máte centrální server – Puppet master, a na hostech agenta. Agenti si v pravidelných intervalech stahují nastavení a aplikují ho. Výhodou je, že agenti provádějí nastavování i v případě, kdy ztratili spojení s masterem, vezmou svou poslední verzi a stále ji aplikují. Nevýhodou je, že Puppet agent na hostitelích vyžaduje řadu knihoven (například Rubby). Kdysi jsem Puppet používal, ale tehdy novou verzi se mi už nepodařilo rozjet a zběhl jsem k Ansiblu. Ale principiálně ho lze jako doručovací systém certifikátů také použít.
Ansible bez agenta
Oproti tomu Ansible na to jde obráceně, nemá agenta. Na hostitelích je třeba jen nainstalovaný Python a sshd. Ansible můžete mít pak nainstalovaný na své pracovní stanici nebo na centrálním orchestračním serveru, a oba můžete používat současně. Ansible při svém spuštění vytvoří dočasný pythonovský skript, který pomocí ssh na hostitele nakopíruje, spustí, a tím provádí nastavování hosta.
Ansible má dva režimy: přímé volání příkazů a playbooky. Playbook je takovým logickým seskupením množiny příkazů. Ansible s playbookem je spíše designovaný pro jednorázové nastavování, ale pokud ho spouštíte v pravidelných intervalech, plní stejnou funkci jako Puppet.
Přesně takhle to mám udělané i já, centrální orchestrační server spouští pravidelně Ansible playbook a provádí nastavování. Distribuuje ssh klíče, udržuje stálou konfiguraci logování, ssh daemona a tak dále. Když už mám Ansible takhle rozchozený, přímo se nabízí použít ho pro centrální distribuci certifikátů.
Ansible jako doručovatel
V nastavení hostitele v hosts.yml vyplním proměnné – jméno certifikátu a jméno souboru s cestou, jak se má na cílový server certifikát nahrát. To je vše co pro doručení certifikátu na hostitele musím udělat.
hosts.yml
ukazkovyServer:
ansible_host: 10.0.0.10
phpmyadmin: "/var/www/html/phpmyadmin/"
user: "root"
password: "xxx"
sshkeys:
- "{{ key_group_spravci }}"
- "{{ key_jan_vondracek }}"
crtName: "server.upce.cz"
apacheCrt: "/etc/apache2/ssl/server-crt.pem"
apacheKey: "/etc/apache2/ssl/server-key.pem"
Ale jak to doručování certifikátu Ansible vlastně dělá? V playbooku je skupině servers přidělena role certs a náš ukázkový server je členem skupiny servers.
playbook.yml --- - hosts: servers roles: - sshkeys - apps - certs
Role certs má v adresáři defaults definované prázdné proměnné. Nastavením v host s.yml jim dám jinou, neprázdnou hodnotu.
roles/certs/defaults/main.yml crtName: "" apacheCrt: "" apacheKey: "" crtName1: "" apacheCrt1: "" apacheKey1: "" ...
Úkol v tasks se provede jen, když jsou dané proměnné neprázdné. V našem příkladu jsme vyplnili crtName a apacheCrt, task Apache certificate file se provede. Ale to, že se provede, ještě neznamená, že se soubor bude kopírovat. Ansible je šikovný a než bude kopírovat, nejdříve se přesvědčí, jestli se soubor na serveru liší od toho místního. Pokud jsou soubory stejné, kopírování se neprovede, stejně jako znovunačtení Apache.
To je klíčový moment, uacme vám vygeneruje nový certifikát a už se o víc nestaráte. Ansible vše ostatní vyřídí sám. Zjistí, že se soubor na serveru liší od toho co má on, a nahradí ho novou verzí. V případě souboru se soukromým klíčem Ansible postupuje stejně jako u souboru s certifikátem. Jen u klíče se na serveru nastaví práva 0640, soukromý klíč by neměl být schopen přečíst každý uživatel.
V ukázce tasku Apache certificate file mám zobrazený task jen jeden. Ale ve skutečnosti jich mám více, jejich pojmenování a názvy proměnné se liší o index. To proto, abych mohl na jeden server dát certifikátů víc. Jsou případy, kdy se mi všechna jména do jednoho certifikátu nevejdou – limit je 100 doménových jmen na jeden certifikát.
Pak tu mám task Command certificate file. Většina serverů má klasického Apache, ale některé servery jsou speciální. Certifikát se instaluje do Postfixu, Dovecotu, Tomcatu, Dockeru, Radiusu a dalších. V tomto případě je restartovací příkaz aplikace velice individuální a už nestačí nahrát certifikát a restartovat Apache. Příkaz používá proměnné crtName, cmdCrt, cmdKey a především cmdCmd – to je ten restartovací příkaz. Tasků command mám také více a jsou opět oddělené indexy.
roles/certs/tasks/main.yml
---
- name: Apache certificate file
copy:
src: "{{ crtName }}/{{ crtName }}/cert.pem"
dest: "{{ apacheCrt }}"
when: crtName != "" and apacheCrt != ""
notify:
- Get service facts
- reload apache
- name: Apache certificate key
copy:
src: "{{ crtName }}/private/{{ crtName }}/key.pem"
dest: "{{ apacheKey }}"
mode: 0640
when: crtName != "" and apacheKey !=""
notify:
- Get service facts
- reload apache
- name: Command certificate file
copy:
src: "{{ crtName }}/{{ crtName }}/cert.pem"
dest: "{{ cmdCrt }}"
when: crtName != "" and cmdCrt != ""
notify:
- command crt
- name: Command certificate key
copy:
src: "{{ crtName }}/private/{{ crtName }}/key.pem"
dest: "{{ cmdKey }}"
mode: 0640
when: crtName != "" and cmdKey !=""
notify:
- command crt
Když Ansible nakopíruje soubor, pomocí notify se zavolá handler, který službu znovu načte nebo restartuje. Protože když certifikát vyměníte, aplikace si ho musí znovu načíst, aby ho mohla používat. Pokud handler zavoláte třeba třikrát (měnily se tři certifikáty na jednom serveru) Ansible je opět šikovný a reload provede jen jednou. Handler se totiž provádí až po dokončení všech tasků pro daného hostitele, takže se nestane, že by reloadoval a pak ještě kopíroval nějaký soubor.
Handler Get service facts rozšíří seznam faktů (proměnných získaných Ansiblem z hostitele) o služby spuštěné přes systemd. Proto ho volám před reloadovacím handlerem, abych zjistil, že Apache na serveru je a že běží. Volám ho až s handlerem, protože tato operace nějakou sekundu trvá. Když máte 150 serverů, Ansible playbook hned běží o minuty déle.
U cmd handleru jsem zvolil variantu shell – existuje ještě command a raw. Shell nastavuje proměnné prostředí a umožňuje příkazy řetězit třeba pomocí středníku nebo ampersandu.
roles/certs/handlers/main.yml
---
- name: Get service facts
ansible.builtin.service_facts:
- name: reload apache
ansible.builtin.systemd_service:
name: apache2.service
state: reloaded
when: ansible_facts['services']['apache2.service'] is defined and ansible_facts['services']['apache2.service']['state'] == 'running'
- name: command crt
ansible.builtin.shell: "{{ cmdCmd }}"
when: cmdCmd != ""
Poznámka: Ještě bych se rád vrátil k pojmenování adresářů a souborů v nastavení Ansiblu, ty si totiž nemůžete vymyslet úplně podle svého. Sám jsem s tím v počátcích používání bojoval, než jsem to pochopil. Adresář roles je výchozí nastavení z Ansiblu, v něm se nacházejí všechny adresáře s rolemi. Název podadresáře role musí být stejný jako název role. V podadresáři role jsou pevně dané názvy defaults, handlers a tasks.
Každý z těchto adresářů obsahuje soubor main.yml. d efaults/main.yml se spouští jako první a je v něm definice proměnných. Soubor t asks/main.yml obsahuje vlastní nastavovací příkazy. Soubor handlers/main.yml obsahuje příkazy na restartování služeb. Adresář files nemá main.yml, zde Ansible standardně hledá soubory, které se používají v rámci dané role.
Rizika a různé poznámky
To, že jsou certifikáty uložené na jednom místě i s jejich automatizační kódy, je určité riziko. Pokud se někdo na server dostane, získá autorizační kódy a může si certifikát nechat vytvořit sám. Nebo získá aktuální soukromý klíč, který může za určitých okolností použít k dešifrování provozu ze serverů. Ale myslím, že i při ruční výměně certifikátů s dlouhou platností stejně někde existoval síťový adresář, kde byly všechny certifikáty zálohovány. Takže zas takový rozdíl to není. Tady musí nastoupit vícevrstvé zabezpečení. Server s Ansible musí být oddělený od ostatních serverů, uživatelé na něj musejí mít omezený přístup, používat DNSsec atd.
Kopírování automatizačních kódů přes schránku může být také nebezpečné, nikdo kódy určitě nebude ručně přepisovat, na to jsou příliš dlouhé. Ve schránce se často nacházejí velmi zajímavá data a jsou známy útoky na text kopírovaný z webu. Riziko minimalizujeme důsledným zabezpečením pracovní stanice, vícevrstvou obranou serveru atd.
Naopak distribuce certifikátu Ansiblem je zcela bezpečná. Komunikace se serverem probíhá šifrovaně a obě strany vzájemně kontrolují svou identitu pomocí veřejných klíčů v SSH.
Při přechodu ze staticky nasazovaných certifikátů na dynamické s sebou přináší i jednu podstatnou změnu. Dosud, když jsem měnil domény v certifikátu, jsem jednoduše vygeneroval jiný certifikát s novými doménovými jmény. Klíč certifikátu zůstal stejný. To znamenalo, že případné záznamy typu TLSA v DNS zůstávaly stejné. Jenže už samotný přechod na automatiku znamená nový klíč, původní není možné použít, zkoušel jsem to.
To je škoda, protože záznamy TLSA jsou super, přestože jsou tak málo využívány a tohle jim hodí další klacek pod nohy. Myslím, že to zkracování platnosti certifikátů je nedomyšlené. U webových serverů dejme tomu. Ale představte si, že se to týká i poštovních serverů a tam už mi přijde, že to nedává tak velký smysl. Co pak teprve Wi-Fi a servery Radius? Když vyměním certifikát pro eduroam, někteří klienti mají potíže s připojením. Až bude certifikát platit měsíc, iPad mě bude na změnu certifikátu upozorňovat každý měsíc? Někde to zkrácení prostě smysl nedává.
Snad vám toto pojednání bude užitečné. Nastává doba zkracování.
