• Akademia
  • Blog
  • O Serverless
  • O stronie

Jak zarz膮dza膰 sekretami w AWS?


"Jak zarz膮dza膰 sekretami w AWS?"

Update 29 czerwca 2020
Druga, praktyczna cz臋艣膰 tego artyku艂u znajduje si臋 tutaj: Jak u偶ywa膰 sekret贸w w AWS korzystaj膮c z AWS-SDK i Middy?

Je艣li zastanawiasz si臋 jak nale偶y zarz膮dza膰 sekretami w AWS to dobrze trafi艂e艣. AWS oferuje bardzo bogaty ekosystem bezpiecze艅stwa oparty o us艂ug臋 IAM, kt贸ry w wielu wypadkach w og贸le eliminuje sensowno艣膰 posiadania sekret贸w. Jednak pr臋dzej czy p贸藕niej przytrafi膮 si臋 nam us艂ugi wewn膮trz chmury lub od zewn臋trznych dostawc贸w, gdzie b臋dziemy musieli przechowywa膰 has艂a, klucze dost臋powe i inne tajne sk艂adniki autentykacji. Sztandarowym przyk艂adem b臋dzie baza danych MySQL, kt贸ra jest dost臋pna w us艂udze RDS. Pomimo tego, i偶 to us艂uga zarz膮dzana to potrzebujemy mie膰 do niej u偶ytkownika i has艂o. W naturalny spos贸b rodzi to pytanie, gdzie trzyma膰 owe has艂o w spos贸b bezpieczny?

Z tego artyku艂u dowiesz si臋

  • o najcz臋stszych b艂臋dach programist贸w i niebezpiecznych praktykach
  • jakie mo偶liwo艣ci oferuje AWS, je艣li chodzi o zarz膮dzanie sekretami
  • jakie us艂ugi programi艣ci maj膮 do dyspozycji
  • jakie s膮 najlepsze praktyki
  • i kt贸re warto wybra膰 ze wzgl臋du na nasze indywidualne potrzeby?

Niebezpieczne praktyki

Chcia艂bym m贸c nie pisa膰 tego paragrafu, ale 偶ycie uczy, 偶e niestety ludzie-programi艣ci cz臋sto maj膮 bezpiecze艅stwo w nosie. Zacznijmy od praktyki, kt贸r膮 sam widzia艂em wielokrotnie i za kt贸r膮 nale偶a艂oby la膰 programist贸w drewnian膮 linijk膮 po 艂apach (serio, uwa偶am, 偶e ta metoda dydaktyczna powinna wr贸ci膰 do szk贸艂 i zak艂ad贸w pracy 馃え).

Komitowanie sekret贸w do repozytorium kodu

To jest tak oczywiste, 偶e nie nale偶y tego robi膰, 偶e a偶 mi si臋 tego nie chce pisa膰 ale obowi膮zek wzywa. Nawet jak to repo jest tylko 鈥淭woje鈥 firmowe, to nigdy nie masz gwarancji, kto je b臋dzie przegl膮da艂 i kiedy. Takie sekrety mog膮 wyp艂yn膮膰 z firmy d艂ugo po tym jak opu艣cisz projekt. I b膮d藕my szczerzy, skoro jeste艣 misiem o niezbyt du偶ym rozumku (w ko艅cu komitujesz sekrety do repo), to raczej ich nie rotujesz cyklicznie, wi臋c b臋d膮 one dzia艂a艂y do ko艅ca 艣wiata i jeden dzie艅 d艂u偶ej.

Sekret sekretowi nier贸wny i r贸偶ne mog膮 by膰 skutki tego, 偶e wpadnie w niepowo艂ane r臋ce. Je艣li udost臋pnisz gdzie艣 swoje klucze dost臋powe do konta AWS (access key i secret access key) to mo偶esz liczy膰 na nieprzyjemne niespodzianki. Od wysokich rachunk贸w za zasoby powo艂ane przez 鈥渨艂amywaczy鈥 (jak komu艣 dasz klucze to raczej si臋 nie w艂ama艂, tylko wszed艂 馃檪), wyciek wra偶liwych danych, a偶 po totalny upadek firmy. Tak, s艂ysza艂em o historii gdzie, niepowo艂ana osoba korzystaj膮c z takich kluczy skasowa艂a wszystkie zasoby firmy z konta AWS i technologiczne zaplecze firmy przesta艂o istnie膰 w ci膮gu momentu.

Solucja: dyscyplina, polecam linijki 馃槣

Jawne sekrety w pliku properties

Plik properties, wyj膮tkowo cz臋sto stosowany w aplikacjach serwerowych, stanowi 藕r贸d艂o konfiguracji dla aplikacji, zwyczajowo jest 艂adowany do pami臋ci w czasie uruchamiania serwera aplikacji (np. Tomcat). Le偶y sobie gdzie艣 na dysku sewera. Pomijaj膮c sk膮d on si臋 tam wzi膮艂 (jaki艣 proces deploymentu) to trzymanie w nim sekret贸w jest r贸wnie偶 z艂膮 praktyk膮. W przypadku dziury bezpiecze艅stwa, osoba niepowo艂ana jest wstanie przejrze膰 taki plik i odczyta膰 nasz has艂a wprost z pliku.

Podobnie jest z trzymaniem sekret贸w jako zmiennych systemowych. Te偶 jest to ekstremalnie niebezpieczne.

Solucja: nie trzyma膰 sekret贸w w pliku properties. Je艣li ju偶 musisz to je szyfruj KMSem (ale to ma swoje reperkusje, komplikuje procesy devops, rotowania kluczy itd.) i u偶ywaj tylko tymczasowych gratn贸w do klucza KMS, tak aby po odszyfrowaniu aplikacja ju偶 nie mog艂a ich ponownie odszyfrowa膰, co uniemo偶liwi w艂amywaczowi ich odszyfrowanie. Podej艣cie z grantami mo偶na stosowa膰 przy immutable architecture.

Lambda - Sekrety jako zmienne 艣rodowiskowe

Podobnie jak w przypadk贸w serwer贸w jest to naganna praktyka. Niestety r贸wnie cz臋sto stosowana, gdy偶 jest ekstremalnie wygodna dla programisty, a na dodatek nosi znamiona poprawno艣ci. O co chodzi? Ot贸偶 w kodzie aplikacji nie trzymamy sekret贸w, a odwo艂ujemy si臋 do nich przez zmienn膮 艣rodowiskow膮 na przyk艂ad w taki spos贸b:

lamdba.js
1
2
3
4
module.exports.handler = async (event) => {
const password = process.env.password
// do something with the password
}

a samo has艂o jest ustawiane za pomoc膮 Serverless Framework w nast臋puj膮cy spos贸b

serverless.yml
1
2
3
4
myFunction:
handler: src/function.handler
environment:
password: mojeTajneHas艂o

Ewidentnie jest to s艂abe rozwi膮zanie bo has艂o jest jawnie trzymane w AWS jako zmienna 艣rodowiskowa, kt贸r膮 mo偶na 艂atwo odczyta膰 u偶ywaj膮c konsoli webowej.

Dalej, trzymanie has艂a w pliku serverless.yml w sumie nie wiele ro偶ni si臋 od trzymania go bezpo艣rednio w kodzie i te偶 nara偶a nas na skomitowanie go do repo. Co sprytniejsze osoby, eliminuj膮 ten problem stosuj膮c lokalnie na swoim komputerze pliki dodane do .gitignore przechowuj膮ce te sekrety (lub gotowe rozwi膮zania typu dotenv). Wtedy nasz plik konfiguracyjny by wygl膮da艂 nast臋puj膮co

serverless.yml
1
2
3
4
myFunction:
handler: src/function.handler
environment:
password: ${file:(komputer_lokalny/niekomitowalny_plik.yml):password}

Ta metoda nie jest zupe艂nie z艂a, ale wci膮偶 nie eliminuje problemu, 偶e sekret jest jawnie zapisany w konfiguracji Lamdby po jej deploymencie. W zwi膮zku z tym, w wi臋kszo艣ci wypadk贸w jest nieakceptowalna. Szczerze, mog臋 j膮 poleci膰 tylko przy indywidualnym developmencie hobbistycznych projekt贸w 馃槂

Solucja: w dalszej cz臋艣ci artyku艂u.

Poprawne zarz膮dzanie sekretami w AWS

Istniej膮 trzy us艂ugi z kt贸rych mo偶emy wybra膰 t臋, kt贸ra b臋dzie dla nas najlepsza.

1. KMS - Key Management Service

Pierwsza z nich s艂u偶y do zarz膮dzania kluczami szyfruj膮cymi dane. KMS jest zintegrowany z wielom膮 innymi us艂ugami AWS i nast臋pne us艂ugi o kt贸rych Ci powiem te偶 z niego korzystaj膮.

KMS jest w tym zestawieniu us艂ug膮 najbardziej niskopoziomow膮, to znaczy, 偶e sami b臋dziemy musieli go u偶y膰, aby zaszyfrowa膰 nasz sekret, a nast臋pnie u偶y膰 KMS API w naszym kodzie, aby go odszyfrowa膰. KMS nie oferuje 偶adnej 鈥減rzechowalni鈥 dla naszych sekret贸w, czyli nie do艣膰, 偶e musimy sami szyfrowa膰 to jeszcze musimy si臋 zastanowi膰 gdzie trzyma膰 te zaszyfrowane sekrety, jak je dostarczy膰 do aplikacji itd. Do tego dochodzi jeszcze jeszcze kwestia zarz膮dzania kluczem CMK i przemy艣lenie polityki rotowania kluczy.

W serverless, historycznie KMS by艂 u偶ywany przed pojawieniem si臋 dedykowanych us艂ug (o kt贸rych pisze poni偶ej) dlatego prawdopodobnie znajdziesz w internecie przyk艂ady jego u偶ycia. Wygl膮da to mniej wi臋cej tak

serverless.yml
1
2
3
4
myFunction:
handler: src/function.handler
environment:
encryptedPassword: niohi9jIOASUD09ASEDJ/APEODU093R72DJUDY0FFDC/SD9FSJNWEIFGH...

W pliku trzymamy uprzednio zaszyfrowany sekret (co dla mnie osobi艣cie jest s艂ab膮 praktyk膮), a w kodzie go odszyfrowujemy

lamdba.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const aws = require('aws-sdk')
const kms = new aws.KMS()

module.exports.handler = async (event) => {
let params = {
CiphertextBlob: Buffer.from(process.env.encryptedPassword, 'base64')
}

let secret = null
try {
const decrypted = await kms.decrypt(params).promise()
secret = decrypted.Plaintext.toString('utf-8')
}
catch (exception) {
console.error(exception)
}
}

殴r贸d艂o: https://stackoverflow.com/a/54029941/4105584

Oczywi艣cie tak膮 odszyfrowan膮 warto艣膰 warto sobie skeszowa膰 w Lambdzie, tak aby przy gor膮cym uruchomieniu funkcji Lambda nie wywo艂ywa膰 API KMS ponownie.

Podsumowuj膮c, wykorzystanie KMS jest ju偶 w pe艂ni bezpiecznym i profesjonalnym rozwi膮zaniem, to dobra praktyka. Jego g艂贸wn膮 wad膮 jest du偶a ilo艣膰 pracy wymaganej na przygotowanie rozwi膮zania, po cz臋艣ci wynikaj膮ca z braku przechowalni dla naszych sekret贸w, co powoduje, 偶e sami musimy o to dba膰. W rezultacie, takie zaszyfrowane sekrety cz臋sto l膮duj膮 w repozytorium kodu, co mo偶e samo w sobie nie jest niebezpieczne ale rodzi problemy przy rotowaniu kluczy i ewentualnym wdro偶eniu naszego rozwi膮zania w innym regionie lub na nowym koncie AWS (np. u innego klienta).

Zwr贸膰 r贸wnie偶 uwag臋, 偶e w powy偶szym przyk艂adzie, gdy zmieni si臋 has艂o, to musimy je zn贸w zaszyfrowa膰, umie艣ci膰 zaszyfrowane has艂o w pliku serverless.yml i ponownie zdeployowa膰 funkcj臋 lambda. Jest to wysoce manualny proces, kt贸ry mo偶e wykonywany raz na rok nie zajmie wiele czasu, ale z drugiej strony rodzi wielk膮 szans臋 na to, 偶e o nim zapomnimy i nagle nasza funkcja b臋dzie si臋 pos艂ugiwa艂a zdezaktualizowany has艂em, co w efekcie spowoduje, 偶e przestanie dzia艂a膰.

2. Systems Manager Parameter Store

Jest du偶a szansa, 偶e s艂ysza艂e艣 ju偶 o tej us艂udze. Jak sama nazwa wskazuje jest to przechowalnia parametr贸w wszelkiej ma艣ci. Dost臋p do tych parametr贸w mamy przez API oraz konsol臋 webow膮. Mo偶emy zatem wygodnie, manualnie, z poziomu przegl膮darki doda膰 nasze parametry, kt贸re potem b臋d膮 u偶ywane przez nasz膮 aplikacj臋.

Parameter Store integruje si臋 z KMS, dzi臋ki czemu mo偶emy zaszyfrowa膰 nasze sekrety. Jest to tak proste jak klikni臋cie jednego checkboxa 馃榾 Taki sekret jest dost臋pny pod unikaln膮 nazw膮 w regionie. Nazwy mog膮 tworzy膰 swoiste 鈥溑沜ie偶ki鈥 tak, aby logicznie grupowa膰 powi膮zane parametry np. mojaAplikacja/prod/bazyDanych/mysql/password oraz mojaAplikacja/prod/bazyDanych/mysql/username to si臋 bardzo przydaje przy developmencie. Zwr贸膰 uwag臋, 偶e ka偶dy stage dev, test, prod mo偶e mie膰 w takiej konfiguracji swoje parametry.

Tworz膮c parametr mamy do wyboru trzy opcje:

  • String - prosta, pojedyncza warto艣膰
  • StringList - lista string贸w odseparowanych przecinkami
  • SecureString - dowolny format znak贸w do 4KB, kt贸ry jest zaszyfrowany kluczem KMS

Ten ostatni typ nas oczywi艣cie najbardziej interesuje w kontek艣cie przechowywania sekret贸w. Poniewa偶, mamy sporo przestrzeni do dyspozycji to mo偶emy w SecureString trzyma膰 stringa, kt贸ry b臋dzie wi臋cej ni偶 pojedynczym has艂em. Osobi艣cie polecam trzyma膰 tam w postaci JSONa ca艂y obiekt ze wszystkimi danymi potrzebnymi do po艂膮czenia z baz膮 danych MySQL (url bazy, u偶ytkownik i has艂o). Po odszyfrowaniu parsuje JSONa i mam obiekt ze wszystkimi potrzebnymi credentialami (sorry za 偶argon 馃檪)

Dost臋p do parametr贸w SSM ustalamy za pomoc膮 polityk IAM. Jest to standardowe i wygodne podej艣cie gwarantuj膮ce, 偶e tylko wybrane przez nas role b臋d膮 mia艂y dost臋p do naszych parametr贸w. W po艂膮czeniu z indywidualnymi rolami dla funkcji lambda otrzymujemy bardzo bezpieczne rozwi膮zanie oferuj膮ce granularne dost臋py.

Niekwestionowan膮 zalet膮 parametr贸w SSM jest to, 偶e mog膮 by膰 u偶ywane przez wiele aplikacji lub komponent贸w (mikroserwis贸w) wewn膮trz tego samego systemu. Je艣li mamy dwa modu艂y 艂膮cz膮ce si臋 do tej samej bazy danych to nie musimy duplikowa膰 parametr贸w. Nadajemy tylko kolejny dost臋p w polityce IAM.

Przydaje si臋 to r贸wnie偶, gdy musimy napisa膰 jaki艣 ad-hoc鈥檕wy skrypt 艂膮cz膮cy si臋 do bazy i wyci膮gaj膮cy jakie艣 dane. Credentiale mamy na wyci膮gni臋cie r臋ki (jeden strza艂 do API) i nigdy si臋 nie walaj膮 odszyfrowane na dysku laptopa. Nie do艣膰, 偶e wygodnie to do tego zgodnie z normami bezpiecze艅stwa ISO / EIC 27001.

Oczywi艣cie mo偶emy w taki spos贸b przechowywa膰 dowolne sekrety, a nie tylko dane dost臋powe do MySQL. Duh! 馃榾

3. Secrets Manager

Ostatni膮, trzeci膮 us艂ug膮 jest Secrets Manager. S艂u偶y on do przechowywania sekret贸w ale r贸wnie偶 ich tworzenia.

Ju偶 t艂umacz臋 co chodzi?

Zgodnie z praktyk膮 infrastructure as code wszelkie zasoby w chmurze powinni艣my definiowa膰 jako kod. Gdy definiujemy w CloudFormation (czy Terraform) baz臋 danych, dajmy na to zn贸w MySQL to musimy poda膰 u偶ytkownika i has艂o dla pierwszego konta w MySQL, kt贸re zostanie stworzone. Rodzi to oczywi艣cie problem, poniewa偶 musimy to has艂o jako艣 przekaza膰 do CloudFormation jako parametr stacka. Pytanie oczywi艣cie sk膮d to has艂o wzi膮膰, je艣li je wygenerujemy na bie偶膮co to trzeba je te偶 gdzie艣 zapisa膰 bo inaczej nie b臋dziemy w stanie si臋 dosta膰 do bazy danych.

Oczywi艣cie mo偶esz has艂o wygenerowa膰, zapisa膰 w Systems Manager Parameter Store i nast臋pnie u偶y膰 przy tworzeniu bazy danych. Niestety w czystym CloudFormation taki scenariusz jest niemo偶liwy do realizacji i trzeba by si臋 posi艂kowa膰 CustomResource, kt贸re jest dosy膰 toporn膮 metod膮, kt贸rej wol臋 unika膰.

Na szcz臋艣cie w艂a艣nie ten problem rozwi膮zuje us艂uga Secrets Manager. Jest ona w stanie wygenerowa膰 has艂o, zapisa膰 je w swojej pami臋ci i nast臋pnie przekaza膰 jako parametr wej艣ciowy przy tworzeniu kolejnego zasobu w CloudFormation. Na chwil臋 obecn膮 te metoda pozwala tworzy膰 nowe credentiale dla baz danych w RDS, Redshift oraz DocumentDB (zarz膮dzane MongoDB w AWS).

Poza tym Secrets Manager, zupe艂nie jak Parameter Store, pozwala na stworzenie sekretu (parametru) r臋cznie (w konsoli webowej wybieramy 鈥淥ther type of secrets鈥). Odwo艂ujemy si臋 do niego przez API (AWS SDK) analogicznie jak w przypadku poprzedniej us艂ugi. Oczywi艣cie wszystkie sekrety 鈥減od spodem鈥 s膮 szyfrowane przy u偶yciu us艂ugi KMS.

Zawarto艣膰 parametru definiujemy jako zbi贸r par klucz = warto艣膰, kt贸re zostan膮 zwr贸cone przez API jako JSON. Od strony naszej aplikacji, dost臋p do sekretu jest bardzo podobny jak w przypadku Parameter Store.

lamdba.js
1
2
3
4
5
6
7
8
const aws = require('aws-sdk')
const sm = new aws.SecretsManager()

module.exports.handler = async (event) => {
const secretName = 'mojSekret'
const secret = await client.getSecretValue({ SecretId: secretName }).promise()
const credentials = JSON.parse(secret.SecretString)
}

Kt贸r膮 us艂ug臋 wybra膰?

Taka mnogo艣膰 opcji utrudnia podj臋cie decyzji. Poni偶ej umie艣ci艂em najbardziej istotne kryteria, kt贸re pomog膮 Ci w wyborze najlepszego podej艣cia do zarz膮dzania sekretami w Twojej aplikacji.

KMS Parameter Store Secrets Manager
Koszty 鈻笍 $1 za klucz miesi臋cznie
鈻笍 $0.03 za 10 000 偶膮da艅
鈻笍 Darmowy w wersji podstawowej
鈻笍 $0.05 za parametr na miesi膮c (Advanced)
鈻笍 $0.05 za 10 000 偶膮da艅 (Advanced oraz Higher Throughput)
鈻笍 $0.40 za parametr na miesi膮c
鈻笍 $0.05 za 10 000 偶膮da艅
Limity 10-30 ty艣 偶膮da艅 na sekund臋 w zale偶no艣ci od regionu 40 偶膮da艅 na sekund臋
za dop艂at膮 mo偶e by膰 zwi臋kszony do 1000
2 000 偶膮da艅 na sekund臋
Wady 鉂 Brak przechowalni kluczy
鉂 R臋czne szyfrowanie
鉂 Trzeba samodzielnie zaimplementowa膰 rotacj臋 uprzednio zaszyfrowanych sekret贸w
鉂 Konieczny redeployment, gdy zmieni si臋 sekret
鉂 Niskie limity, opcja Advanced warta rozwa偶enia
鉂 Brak automatycznej rotacji
鉂 Najdro偶sza us艂uga
Zalety 鉁旓笍 Wi臋ksza wszechstronno艣膰 (ale tylko wtedy gdy jest nam potrzebna) 鉁旓笍 Posiada przechowalnie kluczy
鉁旓笍Zmiana sekretu w jednym miejscu, wszystkie korzystaj膮ce aplikacje od razu mog膮 z niego korzysta膰
鉁旓笍 Posiada przechowalnie kluczy
鉁旓笍 Zmiana sekretu w jednym miejscu, wszystkie korzystaj膮ce aplikacje od razu mog膮 z niego korzysta膰
鉁旓笍 Tworzenie nowych sekret贸w (do wspieranych us艂ug, np. RDS)
鉁旓笍 Automatycznie rotowanie sekret贸w (do wspieranych us艂ug, np. RDS)

Podsumowanie

W takiej ilo艣ci wsp贸艂czynnik贸w trudno si臋 po艂apa膰 i podj膮膰 w艂a艣ciw膮 decyzje. Moja rada dla pocz膮tkuj膮cych jest taka:

  • je艣li tworzysz zas贸b przy pomocy CloudFormation, kt贸ry wspiera Secretes Manager (np. baz臋 danych MySQL w RDS) to wybierz Secrets Managera
  • w pozosta艂ych wypadkach wybierz Parameter Store, aby nie mie膰 koszt贸w. Keszuj w dzia艂aj膮cej lambdzie odszyfrowane warto艣ci parametr贸w, aby nie doj艣膰 do limit贸w.
  • gdy dojdziesz do limit贸w to znaczy, 偶e masz taki ruch w swoim systemie, 偶e ju偶 nie jeste艣 pocz膮tkuj膮cy, zatem rozwa偶 wszystko od nowa i wybierz najlepsze podej艣cie do Twojego systemu.

PS. Korzystanie z Secrets Managera rozwi膮za艂o u nas w projekcie te偶 taki problem, 偶e ju偶 si臋 nikt nikogo nie pyta jakie jest has艂o do bazy, bo ka偶dy wie gdzie mo偶e sobie sprawdzi膰 to has艂o. Ma艂a rzecz, a cieszy 馃憤馃槂

A teraz zapraszam Ci臋 do drugiego, praktycznego artyku艂u w tej serii Jak u偶ywa膰 sekret贸w w AWS korzystaj膮c z AWS-SDK i Middy?

殴r贸d艂a: