..noszę, bezpieczne kredki, bardzo lubią mnie.. Mógłby sobie zanucić programista, gdyby chciał.. ;).
Tak jak obiecałem, będziemy kontynuować temat wpisu z 28 czerwca “Kto schował kredki?”. Tu skupimy się na bezpiecznym serwowaniu aplikacji niezbędnych “kredek” przy pomocy Vaulta i Nomada, zaczynając od konfiguracji samego silnika w Vaulcie. Całość opisu tej integracji (ten i poprzedni wpis) powstał po zabawie w wirtualnym labie Hashicorpa.
Co mamy?
Na razie mamy zapewnione bezpieczeństwo samej bazy MongoDB, która przy deploymencie, przy pomocy integracji Nomada z Vaultem, dostaje swoje inicjalne dane uwierzytelniające (dalej w tekście będę pisał - sekrety, może nie do końca poprawnie po polsku, ale moim zdaniem - krócej i zgrabniej).
Vault: Uruchamiamy silnik bazodanowy
Silnik bazodanowy w Hashicorp Vault to takie “proxy” pomiędzy klientem a bazą danych. Vault integruje się z kilkoma powszechnie używanymi bazami danych poprzez pluginy (patrz: obrazki poniżej). Klient uwierzytelnia się w Vaulcie, dostaje odpowiednią rolę, która jest autoryzowana do kilku czynności związanych z operacjami na użytkowniku w wybranej bazie danych. Czynności te wykonuje za klienta Vault, a wyniki (sekrety) serwuje klientowi. Wszystko odbywa się oczywiście w ramach bezpiecznie zestawionej komunikacji (TLS) oraz uwierzytelnianiu z wykorzystaniem krótko-żyjących tokenów Vaulta.
Swoją drogą, zwróćmy uwagę na szeroką paletę integracji Vaulta (warto na to poświęcić więcej czasu). :)
konfiguracja
Kiedy mamy już uruchomiony silnik bazodanowy, musimy go odpowiednio skonfigurować:
- Konfiguracja pluginu (MongoDB),
- Definicja roli/ról w Vaulcie, która pozwoli klientowi na operacje z bazą danych (ról może być wiele, a sama rola może być albo dynamiczna albo statyczna),
- Definicja połaczenie do bazy danych (albo wiele połączeń do tej samej lub różnych baz), poprzez który Vault będzie wykonywał operacje w bazie danych, w zależności od przypisanego użytkownika i roli,
- Zdefiniować rotation statements, tak aby Vault automatycznie mógł zrotować wymagane sekrety (ten sam obrazek z definicją połączenia).
A sam panel po skonfigurowaniu integracji wygląda tak:
Ta konfiguracja posłuży nam do tego, aby prawidłowo obsługiwać generowanie kont aplikacyjnych dla danej bazy w MongoDB poprzez zdefiniowaną rolę. Tzn: zdefiniowana_rola_w_Vaulcie_1 -> zdefiniowany użytkownik w bazie (z uprawnieniami do zarz. użytkownikami) -> zdefiniowany_prefix_nazwy_konta -> hasło.
Wszystko na swoim miejscu
Czego potrzebuje nasza aplikacja do prawidłowego funkcjonowania z bazą MongoDB?
- CONNECTION_STRING
- JWTSECRET
- PORT
- SESSION_ID
I to wszystko mamy zapisane key-value store o nazwie “my-fs-app”:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ vault kv get kv/my-fs-app
== Secret Path ==
kv/data/my-fs-app
======= Metadata =======
Key Value
--- -----
created_time 2022-06-24T06:29:25.540471649Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 2
========== Data ==========
Key Value
--- -----
CONNECTION_STRING mongodb://appuser:*******@srv1u100.nukelab.home:27017/ComplexApp?retryWrites=true&w=majority
JWTSECRET ************
PORT ************
SESSION_ID ************
Jak zdążyliście pewnie zauważyć, w CONNECTION_STRING nazwa konta i hasło to właśnie te kredki, które wygenerował nam Vault przy pomocy skonfigurowanego plugina. Trochę ręcznie. Wiem. Nie udało mi się jeszcze dojść jak poprawnie wyciągnąć te kredki template-m Nomada ze ścieżki database/... A po drugie musiałbym potem robić w pliku nomadowym jakieś złożenie tych wartości w jeden connection string, trzymając nazwę serwera osobno, parametry URLa osobno i mongo:// osobno.
Sprawdźmy czy wszystko jest na swoim miejscu:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ vault list database/mongodb/static-roles
Keys
----
my-app-user
$ vault read database/mongodb/static-roles/my-app-user
Key Value
--- -----
credential_type password
db_name my-fs-app
last_vault_rotation 2022-06-10T13:57:51.082470413+02:00
rotation_period 8760h
rotation_statements []
username appuser
$ vault read database/mongodb/static-creds/my-app-user
Key Value
--- -----
last_vault_rotation 2022-06-10T13:57:51.082470413+02:00
password ******************
rotation_period 8760h
ttl 7554h9m26s
username *******
Ale.. zaraz, zaraz. My to nie Nomad, skąd Vault ma wiedzieć, że klient Nomada jest upoważniony do sięgnięcia po te kredki? Póki co, Vault “rżnie głupa”, że nic nie wie o żadnych kredkach dla aplikacji - Missing: vault.read(kv/my-fs-app)
. Zgodnie z podejściem ustanowionym w poprzednim odcinku, klient Nomada odczyta wszystkie polityki poza przeznaczoną tylko dla serwera Nomada. Wracamy więc, do tematu ustawienia odpowiedniej polityki, bo rolę i krótkoterminowy token mamy już ustawione w poprzednim ćwiczeniu. Więc załadowanie takiej o to polityki jak poniżej, w zupełności wystarczy (dla kv v1 i v2):
1
2
3
4
5
6
7
8
9
10
11
12
# Access apps secrets
path "kv/data/my-fs-app" {
capabilities = ["read"]
}
path "secret/kv/data/my-fs-app" {
capabilities = ["read"]
}
(Dodajemy do Vaulta przez UI-a, albo vault policy write..
)
Nomad: konfiguracja deploymentu aplikacji
Konfiguracja specs file aplikacji, konstrukcyjnie niczym się praktycznie nie różni od tego, którego wyprodukowałem dla mongo. Podmienione zostały tylko parametry i zlikwidowana stanza dotycząca miejsca na dysku dla danych. Sam plik jest dostępny tutaj
Start aplikacji
nomad plan app.nomad
i nomad job run -check-index 0 app.nomad
..i sprawdźmy czy aplikacja odpowiada..
..i ciągnie dane z Mongo (porównajmy oba obrazki)
Co dalej?
Dobrze byłoby ten proces teraz skonsolidować - jeden job dla Mongo i aplikacji. A następnie zautomatyzować, wykorzystując rozwijany intensywnie w tym roku Nomad Packs. Ten Nomad Pack to coś jak Helm dla Kubernetesa. Własne repozytorium paczek, zarządzanie wersjami itd.