Docker i zabezpieczanie haseł

162

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?

anthonator
źródło
7
Na Github jest otwarty problem z prośbą o najlepsze praktyki dotyczące Dockera i sekretów, problem jest tutaj: github.com/docker/docker/issues/13490
Luís Bianchin

Odpowiedzi:

92

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 -eargument (dla pojedynczych zmiennych w CLI) lub --env-fileargument (dla wielu zmiennych w pliku) do docker run. Przeczytaj to, aby używać środowiska z docker-compose.

Używanie --env-filejest zdecydowanie bezpieczniejszą opcją, ponieważ chroni to przed sekretami pojawiającymi się psw dziennikach lub w dziennikach, jeśli ktoś z nich korzysta set -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ć dockerpolecenia. (Oczywiście każdy użytkownik, który ma dostęp do dockerna hoście, i tak ma również roota ).

Moim preferowanym wzorcem jest użycie skryptu opakowującego jako ENTRYPOINTlub CMD. 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 .

Ben Whaley
źródło
Jak zauważono w innych komentarzach, będą 2 warstwy (po dodaniu i po pierwszym URUCHOMIENIU) zawierające .configplik.
Petr Gladkikh
1
Tak, zmienne env wydają się najlepszym rozwiązaniem. Patrzyłem na to w kontekście rozwoju TDDing Dockerfile.
gnoll110
5
Obawiam się, że jeśli twoje hasło jest zmienną env, pojawia się w docker inspect.
slim
Domyślna instalacja Dockera (na Linuksie) wymaga do uruchomienia uprawnień sudoer 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.
GrandOpener
7
@GrandOpener Dotyczy to tylko sytuacji, w której atakujący używa Twojego systemu. Jeśli wypchnę obraz dockera do repozytorium i zostanie on ściągnięty przez kogoś innego, nie obchodzi mnie, czy mają sudo w swoim własnym systemie, ale zdecydowanie obchodzi mnie, czy widzą sekrety w środowisku env, których już nie powinno tam być.
vee_ess
74

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.ymlmożesz określić plik zawierający zmienne środowiskowe dla kontenera:

 env_file:
- .env

Pamiętaj, aby dodać .envdo .gitignore, a następnie ustaw poświadczenia w .envpliku, takie jak:

SOME_USERNAME=myUser
SOME_PWD_VAR=myPwd

Przechowuj .envplik lokalnie lub w bezpiecznym miejscu, gdzie reszta zespołu może go pobrać.

Zobacz: https://docs.docker.com/compose/environment-variables/#/the-env-file

theUtherSide
źródło
15
Jeśli chcesz, możesz to również zrobić bez pliku .env. Po prostu użyj właściwości environment w pliku docker-compose.yml. „Zmienne środowiskowe zawierające tylko klucz są tłumaczone na ich wartości na komputerze, na którym działa funkcja Compose, co może być pomocne w przypadku wartości tajnych lub specyficznych dla hosta”.
D. Visser,
1
daj temu mężczyźnie ciasteczko! :) tak, to naprawdę dobra praktyka. Chcę tylko dodać, że docs.docker.com/compose/env-file to powinno działać automatycznie, ale w wersji 2 docker compose wydaje się, że musisz zadeklarować to tak, jak opisano w tej odpowiedzi.
odpowiednik 8
5
Używanie zmiennych środowiskowych jest odradzane przez sam zespół Dockera, ponieważ zmienną env można zobaczyć za pomocą / proc / <pid> / environment i docker inspect. To tylko zaciemnia sposób uzyskania poświadczeń dla atakującego, który uzyskał dostęp do roota. Oczywiście CVS nigdy nie powinien śledzić referencji. Myślę, że jedynym sposobem, aby uniemożliwić użytkownikowi root uzyskanie kredytu, jest odczytanie poświadczeń z poziomu aplikacji internetowej (mając nadzieję, że nie zaktualizuje ona swojego pliku środowiska proc) z zaszyfrowanego pliku, podczas procesu deszyfrowania bezpiecznie prosząc o hasło. Myślę, że spróbuję z grobowcem: github.com/dyne/Tomb
pawamoy
.gitignoreaby .envplik z poufnymi informacjami nie został wpisany do usługi GitHub. Jestem prawie pewien, że to nie zadziała, jeśli to dodasz.dockerignore
theUtherSide
cześć @theUtherSide, dziękuję za odpowiedź, mam pytanie, kiedy nie sprawdzam .envpliku i wykonuję wdrożenie na serwerze lokalnie, czy sugerujesz .envręczne utworzenie pliku na serwerze?
opensource-developer
37

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

Heather QC
źródło
Kilka przydatnych poleceń z powyższych linków:: docker secret createutwórz sekret docker secret inspect: wyświetl szczegółowe informacje o sekrecie docker secret ls: wyświetl wszystkie sekrety docker secret rm: usuń określoną --secretflagę tajnego dla docker service create: utwórz sekret podczas tworzenia usługi --secret-addi --secret-rmflagi dla docker 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.
PJ
7
Tak, musisz skonfigurować rój, aby używać sekretów Dockera
Heather QC
11
To dobry początek odpowiedzi, ale wymaga znacznie więcej informacji z tego, co jest powiązane, aby pojawić się w samej odpowiedzi.
Jeff Lambert,
7
Nie jestem pewien, czy może to być akceptowana odpowiedź, jeśli działa tylko z rojami. Wiele osób nie korzysta z rojów, ale nadal muszą przekazywać sekrety.
John Y
9

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 credspóźniej RUN rm credsnie 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 RUNpoleceń kompilacji. Jest to niebezpieczne, jeśli potrzebujesz w tym kredytów RUN. Zamiast tego umieszczasz źródło w katalogu lokalnym, uruchamiasz (tak jak w docker 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 prostu ADDw 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.

TvE
źródło
7

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:

$ echo "secret" > /root/configs/password.txt
$ docker run -v /root/configs:/cfg ...

In the Docker container:

# echo Password is `cat /cfg/password.txt`
Password is secret

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.

Malvineous
źródło
5

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

FROM alpine
RUN cat /run/secrets/password
CMD sleep inifinity

docker-compose.yml

version: '3.1'
services:
  app:
    build: .
    secrets:
      - password

secrets:
  password:
    file: password.txt

Aby zbudować, wykonaj:

docker-compose up -d

Czytaj dalej:

Murmel
źródło
2

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

FROM busybox
ARG user
RUN echo "user is $user"

polecenie budowania obrazu

docker build --build-arg user=capuccino -t test_arguments -f path/to/dockerfile .

podczas kompilacji drukuje

$ docker build --build-arg user=capuccino -t test_arguments -f ./test_args.Dockerfile .

Sending build context to Docker daemon 2.048 kB
Step 1 : FROM busybox
 ---> c51f86c28340
Step 2 : ARG user
 ---> Running in 43a4aa0e421d
 ---> f0359070fc8f
Removing intermediate container 43a4aa0e421d
Step 3 : RUN echo "user is $user"
 ---> Running in 4360fb10d46a
**user is capuccino**
 ---> 1408147c1cb9
Removing intermediate container 4360fb10d46a
Successfully built 1408147c1cb9

Mam nadzieję, że to pomoże! PA.

NickGnd
źródło
26
Według dokumentacji Dockera ARG : „Nie zaleca się używania zmiennych czasu budowania do przekazywania sekretów, takich jak klucze github, dane logowania użytkownika itp.”
Lie Ryan
3
Zastanawiam się tylko, dlaczego Docker odradza używanie --build-arg var=secretdo przekazywania klucza prywatnego SSH do obrazu, nie ma udokumentowanego uzasadnienia. Czy ktoś może to wyjaśnić?
Henk Wiersema
2
@HenkWiersema Informacje o procesach, dzienniki i historia poleceń są niepewne. Informacje o procesie są dostępne publicznie i obejmują wszystkie parametry wiersza poleceń. Często te rozmowy kończą się w dziennikach, które mogą być również publiczne. Atakujący często sprawdza informacje o uruchomionych procesach i przeszukuje publiczne logi w poszukiwaniu tajemnic. Nawet jeśli nie jest publiczny, może być przechowywany w historii poleceń, co ułatwiłoby komuś uzyskanie tajemnic za pośrednictwem konta nieadministracyjnego.
tu-Przywróć Monica-dor duh
2
Jaki jest zalecany sposób dostarczania poświadczeń potrzebnych w czasie kompilacji? Na przykład obraz, który potrzebuje dostępu do AWS s3, aby pobrać duży zestaw danych, który będzie znajdować się w obrazie?
ely
3
Wyobrażam sobie, że nie jest to zalecane, ponieważ docker historyeksponuje build-arg/ ARGzmienne. Można pobrać dowolny obraz, obejrzeć go i zobaczyć wszelkie sekrety przekazane podczas kompilacji jako parametr build-arg/ ARG.
vee_ess
2

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.shuż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-fileprzełącznikiem. Oczywiście zachowaj plik w tajemnicy. .gitignorei taki.

U mnie finish.shuruchamia program w Pythonie. Sprawdza, czy wcześniej nie działała, a następnie kończy konfigurację (np. Kopiuje nazwę bazy danych do Django settings.py).

Kieran Mathieson
źródło
2

Jest nowe polecenie dockera do zarządzania „sekretami”. Ale to działa tylko w przypadku klastrów roju.

docker service create
--name my-iis
--publish target=8000,port=8000
--secret src=homepage,target="\inetpub\wwwroot\index.html"
microsoft/iis:nanoserver 
José Ibañez
źródło
1

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.

Bunyk
źródło
Doceniam odniesienie do Biblii.
JakeCowton
-2

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).

Richard
źródło
2
Czy jest gdzieś odpowiedź na zadane pytanie?
Abhijit Sarkar