#AdminCases - czyli gotowa solucja: jak Ansiblem zainstalować i skonfigurować VSFTPD z obsługą FTPS
Dzisiaj mamy na tapecie – zresztą jak w większości naszych przypadków – powrót do rozleniwionego bytu określanego mianem administrator.
Ave :)
Jako, że dawno nie było nam dane komunikować się ze sobą drogą blogową, pozwolę sobie wszystkich pozdrowić i mieć nadzieję, że jakoś się trzymacie.
Tym razem wspomniany administrator będzie miał za zadanie zainstalować serwer FTP w wersji z szyfrowaniem na serwerach Linux. Wszystko fajnie jak jest to jeden serwer, ale co w przypadku ich dużej liczby? Tylko administrator w trybie „hardcore”, w tych okolicznościach robiłby to ręcznie. To przecież tyle punktów do zrobienia, że łatwo o pomyłkę, nie wspominając, o bólu palców nawet pomimo wirtuozerii, jaką zapewne większość z Was posiada w obcowaniu z klawiaturą. Dlatego też znowu będziemy korzystać z dobrodziejstwa Ansible.
Czym jest i z czym to się je opisałem w poprzednim artykule: #AdminCases – czyli gotowa solucja: jak Ansiblem zainstalować nginx’a i zmienić mu konfigurację SSL tak, aby korzystał z certyfikatów z własnym CA. Dodam, że warto się z nim zapoznać, dowiecie się z niego jak zdefiniować plik inventory i w jaki sposób skonfigurować SSH, aby była możliwość logowania. Zachęcam do tej krótkiej lektury. Warto również rzucić okiem na Shell Scripting w przykładach: jak w szybki sposób wygenerować certyfikaty SSL dla serwerów, ponieważ potrzebne będą wygenerowane klucze i certyfikaty.
W poprzednim artykule o Ansible korzystaliśmy z modułu template, gdzie tworzyliśmy przykładowe pliki konfiguracyjne. Tym razem zrobimy trochę inny scenariusz, a mianowicie zmodyfikujemy pliki konfiguracyjne, które powstaną przy instalacji za pomocą modułów lineinfile oraz blockinfile. Będziemy również bawić się modułami apt i yum oraz systemd do instalacji pakietów oraz administracji serwisami. Oczywiście – jak pewnie zauważyliście – nasz przypadek będzie uzależniał swoje działanie od tego z jakiej rodziny Linux wywodzi się system operacyjny, na którym mamy wykonać instalację. Można zrobić więcej wodotrysków w postaci warunków wykonania, ale skupiamy się tutaj na crème de la crème funkcjonalności, aby nie przesłonić obrazu działań, które mamy zamiar osiągnąć.
To może tyle słowem wstępu, a teraz przejdźmy do czynów. Co chcemy zrobić, czyli standardowo określamy cel przyświecający naszym działaniom:
- chcemy na różnych systemach zainstalować serwer VSFTPD
- przekazać do systemów niezbędne pliki związane z szyfrowaniem
- zmienić pliki konfiguracyjne serwera VSFTPD, aby umożliwiał komunikację po protokole FTPS (nie mylić z SFTP – ten związany jest z SSH)
- uruchomić serwer i sprawdzić, czy jesteśmy tacy boscy jacy jesteśmy i wszystko działa.
Dla unaocznienia działania dla tego przykładu skorzystałem z dwóch systemów operacyjnych – jeden z rodziny RedHat, drugi z Debianowej. Wy możecie wstawić ile Wam się podoba – w końcu po to automatyzujemy te czynności.
Zakładamy grupę w inventory o nazwie Vsftpd. Tak jak już wcześniej wspomniałem to oraz konfiguracja SSH jest opisana w poprzednim artykule z cyklu #AdminCases z jedną małą różnicą. Tutaj nie będziemy korzystać z konta root tylko z normalnego użytkownika, który musi mieć uprawnienia do sudo oraz – w tym przykładzie testowym – będzie w trybie NOPASSWD w sudoers (dla tych, którzy nie wiedzą taki tryb powoduje, że w przypadku wywołania polecenia via sudo nie krzyczy o hasło tylko wykonuje polecenie z uprawnieniami administratora).
Przygotujmy również klucze i certyfikaty dla poszczególnych lub też jeden dla wszystkich serwerów. W naszym przykładzie będę korzystał z opcji per serwer, aby pokazać jak w yaml używać zmiennej ansible_host. Z tego właśnie powodu wszystkie pliki związane z szyfrowaniem zostały utworzone dla poszczególnych serwerów i umieszczone w katalogu /tmp serwera określanego mianem Control Node, czyli tego z którego uruchamiamy playbooka. To tyle, jeżeli chodzi o przygotowania środowiska i niezbędnych konfiguracji.
Tak jak poprzednio przedstawię obraz całego playbooka, a następnie opiszę poszczególne kroki, które przyjdzie wykonać Ansiblem. Miejmy nadzieję, że wszystko zakończy się powodzeniem. Uwaga! Spoiler SOA nr 1, czyli Standardowa Odpowiedź Administratora numer 1: U mnie działa :)
- hosts: Vsftpd
remote_user: aciek
become: true
become_method: sudo
tasks:
- name: Packages gather info
package_facts:
manager: auto
- name: Check if vsftpd is installed
debug:
msg: "Vsftpd not installed!"
when: "'vsftpd' not in ansible_facts.packages"
- name: Create directory /etc/ssl/vsftpd
file:
path: /etc/ssl/vsftpd
force: yes
mode: 0710
state: directory
- name: Copy certificate {{ ansible_hostname }}.crt to server
copy:
dest: /etc/ssl/vsftpd/{{ ansible_hostname }}.crt
src: /tmp/{{ ansible_hostname }}.crt
mode: 0440
force: yes
- name: Copy key {{ ansible_hostname }}.key to server
copy:
dest: /etc/ssl/vsftpd/{{ ansible_hostname }}.key
src: /tmp/{{ ansible_hostname }}.key
mode: 0440
force: yes
- name: If not installed install vsftpd service on RedHat Family OS
yum:
name: vsftpd
state: latest
when: "'vsftpd' not in ansible_facts.packages and ansible_os_family == 'RedHat'"
- name: If not installed install vsftpd service on Debian Family OS
apt:
name: vsftpd
state: latest
when: "'vsftpd' not in ansible_facts.packages and ansible_os_family == 'Debian'"
- name: Make sure a service VSFTPD is running
register: result
ignore_errors: true
systemd:
state: started
name: vsftpd
- name: Print info
debug:
msg: "VSFTPD is running"
when: result is succeeded
- name: Create backup of vsftpd.conf on Debian Family OS
copy:
remote_src: yes
src: /etc/vsftpd.conf
dest: /etc/vsftpd.conf.bak
when: "'vsftpd' not in ansible_facts.packages and ansible_os_family == 'Debian'"
- name: Create backup of vsftpd.conf on RedHat Family OS
copy:
remote_src: yes
src: /etc/vsftpd/vsftpd.conf
dest: /etc/vsftpd/vsftpd.conf.bak
when: "'vsftpd' not in ansible_facts.packages and ansible_os_family == 'RedHat'"
- name: Comment out default variables on Debian Family OS
lineinfile:
path: /etc/vsftpd.conf
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
loop:
- { regexp: '^ssl_enable', line: '#ssl_enable' }
- { regexp: '^rsa_cert_file', line: '#rsa_cert_file' }
- { regexp: '^rsa_private_key', line: '#rsa_private_key' }
when: "'vsftpd' not in ansible_facts.packages and ansible_os_family == 'Debian'"
- name: Insert variables to vsftpd.conf on Debian Family OS
blockinfile:
path: /etc/vsftpd.conf
insertafter: EOF
block: |
pasv_enable=YES
ssl_enable=YES
ssl_tlsv1=YES
ssl_sslv2=NO
ssl_sslv3=NO
rsa_cert_file=/etc/ssl/vsftpd/{{ ansible_hostname }}.crt
rsa_private_key_file=/etc/ssl/vsftpd/{{ ansible_hostname }}.key
allow_anon_ssl=NO
force_local_data_ssl=YES
force_local_logins_ssl=YES
require_ssl_reuse=NO
ssl_ciphers=HIGH
pasv_min_port=40000
pasv_max_port=50000
debug_ssl=YES
when: "'vsftpd' not in ansible_facts.packages and ansible_os_family == 'Debian'"
- name: Insert variables to vsftpd.conf on RedHat Family OS
blockinfile:
path: /etc/vsftpd/vsftpd.conf
insertafter: EOF
block: |
pasv_enable=YES
ssl_enable=YES
allow_anon_ssl=NO
ssl_tlsv1_2=YES
ssl_sslv2=NO
ssl_sslv3=NO
rsa_cert_file=/etc/ssl/vsftpd/{{ ansible_hostname }}.crt
rsa_private_key_file=/etc/ssl/vsftpd/{{ ansible_hostname }}.key
allow_anon_ssl=NO
force_local_data_ssl=YES
force_local_logins_ssl=YES
require_ssl_reuse=NO
ssl_ciphers=HIGH
pasv_min_port=40000
pasv_max_port=50000
debug_ssl=YES
when: "'vsftpd' not in ansible_facts.packages and ansible_os_family == 'RedHat'"
- name: Restart VSFTPD
systemd:
name: vsftpd
state: restarted
daemon_reload: yes
Proste prawda? ;)
Lecimy z tłumaczeniem słowno-muzycznym (bez udziału muzyki), co i kiedy się dzieje.
Cała akcja, jak już wcześniej wspomniałem, odbywa się na grupie serwerów o nazwie: Vsftpd. Bohaterem, który ma zwycięsko przejść przez wszystkie przeciwności losu, czyli task-i jest użytkownik aciek, który będzie korzystał z metody sudo. Zatem jakie zadania ma na swojej drodze?
Pierwszymi, jakie napotyka, jest zgromadzenie informacji przy pomocy modułu package_facts i dla świętego spokoju wyświetlenie, czy serwer VSFTPD jest zainstalowany. Wizualnie mniej więcej wygląda to w ten sposób:
Kolejnym etapem jest skorzystanie z modułu file w celu założenia katalogu na Managed Node, czyli na tym do którego dobija się Ansible. Z opcją state ustawioną na wartość directory oznacza to, że ta operacja dotyczy katalogu, a że go nie ma to zostanie założony z uprawnieniami 710 co uniemożliwi normalnemu użytkownikowi wglądu do niego. Po tej operacji listing katalogu wygląda tak:
drwx--x---. 2 root root 42 11-18 20:23 vsftpd
Kolejne dwa taski odpowiadają za przekopiowanie klucza i certyfikatu korzystając z modułu copy również ograniczając uprawnienia do kopiowanych plików. W przypadku tych zadań korzystamy ze zmiennej ansible_hostname, ponieważ nazwa pliku jest tożsama z nazwą hosta, na którym plik ma się znaleźć. (Właśnie to chciałem pokazać wybierając opcję z kluczem, certyfikatem dla każdego serwera). Output z wykonania wygląda tak:
Dotychczas nie sprawdzaliśmy na jakiej rodzinie serwerów było wykonywane zadanie, ponieważ nie było to konieczne. Teraz trzeba to zmienić ponieważ dochodzimy do etapu kiedy mamy do zainstalowania pakiety VSFTPD, a każdy z systemów ma swój system zarządzania pakietami nomem omen nazywający się tak samo moduł Ansible. Zatem w przypadku RedHat skorzystamy z yum, a w przypadku rodziny Debian z apt do instalacji pakietu. State: latest oznacza, że ma to być najnowszy pakiet.
Sprawdźmy teraz, czy serwer po instalacji został uruchomiony i czy działa. W tym celu skorzystamy z dobrodziejstwa modułu systemd, który daje dostęp do menedżera systemu i usług dla Linuxów obecnej generacji, który nosi taką samą nazwę jak moduł. Zapytanie o działanie zarejestrujemy w zmiennej result dla systemd z parametrami state: started oraz name: vsftpd. W następnym kroku wyświetlimy informację o tym, że serwer działa w przypadku, gdy result jest zakończony sukcesem.
Jak widać serwer już działa jednak na defaultowych ustawieniach, a my chcemy wymusić na nim działanie z szyfrowaniem oraz trybem pasywnym w konfiguracji. Aby to uczynić będziemy poprawiać pliki konfiguracyjne korzystając z dobrodziejstw modułów lineinfile oraz blockinfile. Zanim dojdziemy do tego jakich użyć zaklęć musimy znać lokalizację samych plików konfiguracyjnych. Tutaj jest niespodzianka, ponieważ są różne dla interesujących nas rodzin serwerów, co będzie determinowało konieczność wykonywania operacji w osobnych taskach. W obu przypadkach nazwa pliku konfiguracyjnego to vsftpd.conf jednak dla Debian Family plik znajduje się bezpośrednio w /etc, za to w RedHat w /etc/vsftpd.
Podobno najwięksi twardziele nie robią backupów, ale że my nimi nie jesteśmy, to za pomocą znanego już nam modułu copy z atrybutem remote_src ustawioym na yes, robimy kopię pliku konfiguracyjnego. Ustawienie to oznacza, że operacje kopiowania będą wykonywane tylko na plikach znajdujących się na zdalnym serwerze.
No i już teraz widać brzeg. W konfiguracji dla Debian mamy ustawione zmienne ssr_enable, rsa_cert_file oraz rsa_private_key. W pierwszej kolejności pozbywamy się ich korzystając z modułu lineinfile i możliwości poszukania wzorca regexp i zamiany linii, która posiada dany wzorzec na inny zapis. Zrobimy to przy użyciu pętli podstawiając pod atrybut regexp wspomniane nazwy zmiennych ze znacznikiem ^ (w wolnym tłumaczeniu: zaczyna się od) i wprowadzając zamiast komentarz w postaci znacznika # z nazwą zmiennej. Dla przykładu ssr_enable zostanie zamieniony na #ssr_enable i tak dalej w przypadku pozostałych 2 zmiennych i to właśnie chcieliśmy osiągnąć na początek. W dalszej części musimy dopisać do pliku, niezbędną konfigurację, która to włączy funkcjonowanie FTPS i trybu pasywnego. Do tego wykorzystamy blockinfile z atrybutem insertafter ustawionym na EOF i wypisanym blokiem zmiennych, które to mają znaleźć się w vsftpd.conf. Jak to tłumaczyć? Dopisz na końcu pliku wszystko co mam w block. Jak będziecie mogli zobaczyć na własne oczy dopisana treść jest oznaczona w tagach BEGIN i END ANSIBLE MANAGED BLOCK, a te zmienne, które zmienialiśmy za pomocą lineinfile mają # na początku i są bez wartości. Zamieszczam tylko interesujący nas wycinek konfiguracji.
Po stronie tego, czym opluwa nas Ansible wygląda to następująco:
Nie pozostaje nic innego jak wykonać restart serwisu, co powinno zakończyć działanie naszego playbooka:
Dotarliśmy do końca ale czy będzie to szczęśliwy koniec za chwilę się o tym przekonamy. Do sprawdzenia użyję Filezilli, ale może to być dowolny klient FTP, który ma obsługę TLS. Nie żyjmy już dłużej w niepewności tylko spróbujmy nawiązać połączenie i jeżeli wszystko poszło prawidłowo powinniśmy otrzymać komunikat:
W przypadku mojego serwera z rodziny RedHat mam:
W przypadku Ubuntu, z którego korzystałem:
A log z FTP wygląda tak:
Status: Connection established, waiting for welcome message...
Response: 220 (vsFTPd 3.0.3)
Command: AUTH TLS
Response: 234 Proceed with negotiation.
Status: Initializing TLS...
Status: Verifying certificate...
Status: TLS connection established.
Command: USER aciek
Response: 331 Please specify the password.
Command: PASS ***********************
Response: 230 Login successful.
No i – jak to powiedział Zagłoba – żeśmy Bohuna usiekli ;)
Mam nadzieję, że przykład się podobał i pokazuje, że Ansible nie jest taki straszny.
W najbliższej przyszłości postaram się znowu coś ciekawego poruszyć, a jeżeli ktoś ma jakieś przemyślenia, prośby, czy uwagi to zapraszam do kontaktu.
Don’t drink and root … better use Ansible :)