Wybierz region
pl
  • PL
  • EN
Wydrukuj

#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 :)

Galeria


Adam Paszkiewicz

Adam aka aciek, Ekspert ds. Technologii z Asseco Warszawa. Z technologiami związany od okresu dojrzewania (a ten był dawno temu) ale gotować i żeglować też potrafi. Rzeczy niemożliwe realizuje od ręki, na cuda trzeba chwilę zaczekać. Uważa, że w życiu jak i w pracy dobrze mieć kupę radości ... z przewagą radości.


Wydrukuj