Niedawno eksperymentowałem z Dockerem nad budowaniem niektórych usług do zabawy i jedną rzeczą, która mnie dręczy, jest umieszczanie haseł w pliku Dockerfile. Jestem programistą, więc przechowywanie haseł w kodzie źródłowym jest jak cios w twarz. Czy to w ogóle powinno być problemem? Czy istnieją dobre konwencje dotyczące obsługi haseł w plikach Dockerfiles?
162
Odpowiedzi:
Zdecydowanie jest to problem. Pliki Dockerfile są często rejestrowane w repozytoriach i udostępniane innym osobom. Alternatywą jest podanie wszelkich poświadczeń (nazw użytkowników, haseł, tokenów, czegokolwiek wrażliwego) jako zmiennych środowiskowych w czasie wykonywania . Jest to możliwe poprzez
-e
argument (dla pojedynczych zmiennych w CLI) lub--env-file
argument (dla wielu zmiennych w pliku) dodocker run
. Przeczytaj to, aby używać środowiska z docker-compose.Używanie
--env-file
jest zdecydowanie bezpieczniejszą opcją, ponieważ chroni to przed sekretami pojawiającymi sięps
w dziennikach lub w dziennikach, jeśli ktoś z nich korzystaset -x
.Jednak zmienne środowiskowe również nie są szczególnie bezpieczne. Są widoczne za pośrednictwem
docker inspect
, a zatem są dostępne dla każdego użytkownika, który może uruchamiaćdocker
polecenia. (Oczywiście każdy użytkownik, który ma dostęp dodocker
na hoście, i tak ma również roota ).Moim preferowanym wzorcem jest użycie skryptu opakowującego jako
ENTRYPOINT
lubCMD
. Skrypt opakowujący może najpierw zaimportować wpisy tajne z zewnętrznej lokalizacji do kontenera w czasie wykonywania, a następnie uruchomić aplikację, dostarczając sekrety. Dokładna mechanika tego zależy od środowiska wykonawczego. W AWS można używać kombinacji ról IAM, usługi zarządzania kluczami i S3 do przechowywania zaszyfrowanych wpisów tajnych w zasobniku S3. Coś w rodzaju HashiCorp Vault lub credstash to kolejna opcja.AFAIK nie ma optymalnego wzorca używania poufnych danych w ramach procesu kompilacji. W rzeczywistości mam SO pytanie na ten temat. Możesz użyć docker-squash, aby usunąć warstwy z obrazu. Ale w tym celu w Dockerze nie ma natywnych funkcji.
Przydatne mogą być komentarze shykes dotyczące konfiguracji w kontenerach .
źródło
.config
plik.docker inspect
.docker inspect
. Jeśli osoba atakująca może już wykonać sudo, wyrywanie hasła z funkcji docker inspect jest prawdopodobnie dość niskie na liście rzeczy, które mogą teraz pójść nie tak. Ten szczegół wydaje mi się akceptowalnym ryzykiem.Nasz zespół unika umieszczania poświadczeń w repozytoriach, więc oznacza to, że nie są do nich wpuszczani
Dockerfile
. Nasza najlepsza praktyka w aplikacjach polega na używaniu referencji ze zmiennych środowiskowych.Rozwiązujemy to za pomocą
docker-compose
.Wewnątrz
docker-compose.yml
możesz określić plik zawierający zmienne środowiskowe dla kontenera:Pamiętaj, aby dodać
.env
do.gitignore
, a następnie ustaw poświadczenia w.env
pliku, takie jak:Przechowuj
.env
plik lokalnie lub w bezpiecznym miejscu, gdzie reszta zespołu może go pobrać.Zobacz: https://docs.docker.com/compose/environment-variables/#/the-env-file
źródło
.gitignore
aby.env
plik z poufnymi informacjami nie został wpisany do usługi GitHub. Jestem prawie pewien, że to nie zadziała, jeśli to dodasz.dockerignore
.env
pliku i wykonuję wdrożenie na serwerze lokalnie, czy sugerujesz.env
ręczne utworzenie pliku na serwerze?Docker teraz (wersja 1.13 lub 17.06 i nowsze) obsługuje zarządzanie tajnymi informacjami. Oto przegląd i bardziej szczegółowa dokumentacja
Podobna funkcja istnieje w kubernetes i DCOS
źródło
docker secret create
utwórz sekretdocker secret inspect
: wyświetl szczegółowe informacje o sekreciedocker secret ls
: wyświetl wszystkie sekretydocker secret rm
: usuń określoną--secret
flagę tajnego dladocker service create
: utwórz sekret podczas tworzenia usługi--secret-add
i--secret-rm
flagi dladocker service update
: zaktualizuj wartość klucza lub usuń hasło podczas zadania aktualizacji usługi. Klucze tajne platformy Docker są chronione w spoczynku w węzłach menedżera i udostępniane do węzłów roboczych podczas uruchamiania kontenera.Nigdy nie powinieneś dodawać poświadczeń do kontenera, chyba że możesz rozgłaszać poświadczenia każdemu, kto może pobrać obraz. W szczególności robienie i
ADD creds
późniejRUN rm creds
nie jest bezpieczne, ponieważ plik kredytów pozostaje w ostatecznym obrazie w pośredniej warstwie systemu plików. Każdy, kto ma dostęp do obrazu, może go łatwo wyodrębnić.Typowe rozwiązanie, które widziałem, gdy potrzebujesz kredytów, aby wyewidencjonować zależności, a takie jest użycie jednego kontenera do zbudowania innego. Oznacza to, że zwykle masz jakieś środowisko kompilacji w kontenerze podstawowym i musisz je wywołać, aby skompilować kontener aplikacji. Zatem prostym rozwiązaniem jest dodanie źródła aplikacji, a następnie
RUN
poleceń kompilacji. Jest to niebezpieczne, jeśli potrzebujesz w tym kredytówRUN
. Zamiast tego umieszczasz źródło w katalogu lokalnym, uruchamiasz (tak jak wdocker run
) kontener, aby wykonać krok kompilacji z lokalnym katalogiem źródłowym zamontowanym jako wolumin, a referencje są wstrzykiwane lub montowane jako inny wolumin. Po zakończeniu kroku budowania, budujesz ostateczny kontener, po prostuADD
w lokalnym katalogu źródłowym, który zawiera teraz zbudowane artefakty.Mam nadzieję, że Docker doda kilka funkcji, aby to wszystko uprościć!
Aktualizacja: wygląda na to, że przyszłą metodą będzie zagnieżdżone kompilacje. W skrócie, plik dockerfile opisywałby pierwszy kontener używany do tworzenia środowiska wykonawczego, a następnie drugą kompilację zagnieżdżonego kontenera, która może złożyć wszystkie elementy w ostateczny kontener. W ten sposób rzeczy czasu kompilacji nie znajdują się w drugim kontenerze. Jest to aplikacja Java, w której potrzebujesz JDK do zbudowania aplikacji, ale tylko JRE do jej uruchomienia. Omawianych jest wiele propozycji, najlepiej zacząć od https://github.com/docker/docker/issues/7115 i skorzystać z niektórych linków do alternatywnych propozycji.
źródło
Alternatywą dla używania zmiennych środowiskowych, które mogą się komplikować, jeśli jest ich dużo, jest użycie woluminów, aby udostępnić katalog na hoście w kontenerze.
Jeśli umieścisz wszystkie swoje poświadczenia jako pliki w tym folderze, kontener będzie mógł odczytywać pliki i używać ich według własnego uznania.
Na przykład:
Wiele programów może odczytać swoje dane uwierzytelniające z oddzielnego pliku, więc w ten sposób możesz po prostu wskazać programowi jeden z plików.
źródło
rozwiązanie tylko w czasie wykonywania
docker-compose zapewnia również rozwiązanie nie działające w trybie swarm (od wersji 1.11: Secrets używające bind mounts ).
Sekrety są montowane jako pliki poniżej
/run/secrets/
przez docker-compose. Rozwiązuje to problem w czasie wykonywania (uruchamianie kontenera), ale nie w czasie kompilacji (budowanie obrazu), ponieważ/run/secrets/
nie jest montowany w czasie kompilacji. Ponadto to zachowanie zależy od uruchomienia kontenera za pomocą docker-compose.Przykład:
Dockerfile
docker-compose.yml
Aby zbudować, wykonaj:
Czytaj dalej:
źródło
W przypadku Dockera w wersji 1.9 możesz użyć instrukcji ARG, aby pobrać argumenty przekazane przez wiersz poleceń do obrazu podczas akcji kompilacji . Po prostu użyj flagi --build-arg . Możesz więc uniknąć przechowywania jawnego hasła (lub innych rozsądnych informacji) w pliku Dockerfile i przekazywać je w locie.
źródło: https://docs.docker.com/engine/reference/commandline/build/ http://docs.docker.com/engine/reference/builder/#arg
Przykład:
Dockerfile
polecenie budowania obrazu
podczas kompilacji drukuje
Mam nadzieję, że to pomoże! PA.
źródło
--build-arg var=secret
do przekazywania klucza prywatnego SSH do obrazu, nie ma udokumentowanego uzasadnienia. Czy ktoś może to wyjaśnić?docker history
eksponujebuild-arg
/ARG
zmienne. Można pobrać dowolny obraz, obejrzeć go i zobaczyć wszelkie sekrety przekazane podczas kompilacji jako parametrbuild-arg
/ARG
.Moje podejście wydaje się działać, ale prawdopodobnie jest naiwne. Powiedz mi, dlaczego jest źle.
ARG ustawione podczas budowania dockera są ujawniane przez komendę history, więc nie idź tam. Jednak podczas uruchamiania kontenera zmienne środowiskowe podane w poleceniu uruchomienia są dostępne dla kontenera, ale nie są częścią obrazu.
Tak więc w pliku Dockerfile wykonaj konfigurację, która nie obejmuje tajnych danych. Ustaw CMD na coś takiego
/root/finish.sh
. W poleceniu uruchamiania użyj zmiennych środowiskowych, aby wysłać tajne dane do kontenera.finish.sh
używa zmiennych zasadniczo do zakończenia zadań budowania.Aby ułatwić zarządzanie tajnymi danymi, umieść je w pliku, który jest ładowany przez docker run z
--env-file
przełącznikiem. Oczywiście zachowaj plik w tajemnicy..gitignore
i taki.U mnie
finish.sh
uruchamia program w Pythonie. Sprawdza, czy wcześniej nie działała, a następnie kończy konfigurację (np. Kopiuje nazwę bazy danych do Djangosettings.py
).źródło
Jest nowe polecenie dockera do zarządzania „sekretami”. Ale to działa tylko w przypadku klastrów roju.
źródło
12 Czynnik aplikacja metodologii mówi, że każda konfiguracja powinna być przechowywana w zmiennych środowiskowych.
Docker compose może wykonywać podstawianie zmiennych w konfiguracji, dzięki czemu może być używany do przekazywania haseł z hosta do dockera.
źródło
Chociaż całkowicie się zgadzam, nie ma prostego rozwiązania. Nadal istnieje pojedynczy punkt awarii. Plik dockerfile, etcd i tak dalej. Apcera ma plan, który wygląda jak pomocnik - podwójne uwierzytelnianie. Innymi słowy, dwa kontenery nie mogą rozmawiać, chyba że istnieje reguła konfiguracji Apcera. W ich demie uid / pwd był jasny i nie można go było ponownie wykorzystać, dopóki administrator nie skonfigurował powiązania. Aby to jednak zadziałało, oznaczało to prawdopodobnie łatanie Dockera lub przynajmniej wtyczki sieciowej (jeśli coś takiego istnieje).
źródło