Budowanie obrazów wiąże się z różnymi wyzwaniami, jednym z nich jest bezpieczeństwo trwale zapisanych w nich danych. Jak uniknąć upublicznienia czegoś, co powinno zostać prywatne?
Nie mówię tu o kodzie jako takim, bo jeśli budujemy obrazy przeznaczone do użycia wewnętrznego, to musimy zadbać o ich bezpieczne przechowywanie i użytkowanie, co jest zupełnie inną historią. Wyobraźmy sobie jednak, że budujemy obraz zawierający publicznie dostępne narzędzie, pomagające w codziennej pracy, a jego kod jest dostępny publicznie (np. composer
). Gdzie w takim razie w całym procesie wydawniczym jest miejsce na sekrety, bo o nich właśnie mowa?
Czym są sekrety?
Sekrety to dane, które jak sama nazwa wskazuje, chcielibyśmy zachować dla siebie. Są to wszelakie hasła, tokeny, certyfikaty, na których opiera się np. komunikacja z zewnętrznymi usługami. Dotychczas Docker umożliwiał użycie sekretów w stackach Swarm, również opartych o docker compose
. Dzięki temu da się w prosty sposób dostarczyć do aplikacji różnego rodzaju poufne informacje, ale dopiero w tzw. runtime — nie są one dostępne ani w procesie budowania, ani nie są trwale przechowywane w obrazie (a więc nie można ich wyciągnąć, przeglądając historię warstw), można z nich korzystać dopiero po uruchomieniu kontenera.
Poufne dane w procesie budowania
Wspomniany #Composer, czy wiele innych narzędzi, w procesie budowania obrazu musi pobrać swoje zależności. W przypadku projektów Open Source zazwyczaj te zależności również są dostępne w publicznych repozytoriach GitHub, Gitlab czy innych. Jednak dostawcy tych usług zabezpieczają się przed nieuprawnionym użyciem, zatem przy masowym pobieraniu może pojawić się konieczność uwierzytelniania, a żeby to obejść potrzebujemy tokenów już w trakcie budowania obrazu.
Budując obrazy korzystające z paczek pobieranych z wszelakich rejestrów (Composer, NPM, PYPI), możemy też potrzebować integracji z prywatnym rejestrem (Private Packagist, Artifactory, Verdaccio). O ile obecność samych paczek nie musi być dla nas problemem, o tyle danymi do uwierzytelniania już niekoniecznie chcielibyśmy się dzielić z innymi 😉
Zresztą nie tylko o paczki chodzi — możemy przecież pobierać jakieś artefakty: definicje tłumaczeń, statyczne pliki… Źródłem mogą być inne systemy zintegrowane przez API, w którym również musimy się uwierzytelnić.
Sekrety w momencie budowania
Kilka dni temu w repozytorium docker/compose
do głównej gałęzi v2
dołączone zostały zmiany dostarczające długo wyczekiwaną (issue z 2018 roku!) funkcjonalność — sekrety dostępne na etapie budowania obrazu 🎉. Gdy zostanie wydana w oficjalnej wersji, będzie można z niej korzystać następująco:
services:
ssh:
image: build-test-secret
build:
context: .
secrets:
- mysecret
secrets:
mysecret:
file: ./secret.txt
FROM alpine
RUN echo "foo" > /tmp/expected
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret > /tmp/actual
RUN diff /tmp/expected /tmp/actual
Mając tak zdefiniowany Dockerfile
oraz stack #Docker Compose możemy w bezpieczny sposób przekazać sekret do kontekstu Dockera i użyć go w cyklu budowania. Oczywiście zapisywanie go w jakimś pliku nie jest najlepszym pomysłem, to jedynie przykład obrazujący przekazanie i odczyt sekretu 😉. Jeśli poprawnie go użyjemy, to nie będzie on dostępny w wynikowym obrazie, ani w jego historii warstw (które można podejrzeć wieloma narzędziami).
To umożliwi w końcu spójny proces budowania obrazu z wykorzystaniem sekretów zarówno dla #CI, jak i lokalnego użycia na potrzeby rozwoju aplikacji.
Pozostaje czekać na nową wersję Docker Compose 😁