Cyberbezpieczeństwo okiem programisty

Kubernetes Secrets i pułapka echo -n

K

To jest krótki wpis o tym, dlaczego warto dobrze znać narzędzia, których się używa. Okazuje się, że nawet te najprostsze mogą czasami zaskoczyć…

Krótki zarys: na testowy klaster Kubernetesa (K8s) trafił mikroserwis, który komunikował się z innymi przez system kolejkowy RabbitMQ. Jednakże zaraz po starcie poda pojawił się komunikat, że aplikacja nie może połączyć się z serwerem RabbitMQ ze względu na nieprawidłowe dane uwierzytelniające.

W pierwszej kolejności sprawdziłem konfigurację w pliku manifestu K8s (YAML) i zweryfikowałem nazwy zmiennych środowiskowych oraz nazwę sekretu K8s, ale wszystko wyglądało w porządku. Zajrzałem więc do zawartości sekretu, ponieważ w środowisku testowym miałem do niego dostęp.

W celu uproszczenia przyjmijmy, że sekrety są składowane w domyślnej przestrzeni nazw K8s (default), a nazwa sekretu to my-credentials:

$ kubectl describe secret my-credentials
Name:         my-credentials
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
password:  12 bytes
username:  7 bytes

Polecenia kubectl get secret <SECRET_NAME> oraz kubectl describe secret <SECRET_NAME> nie pokazują domyślnie zawartości, żeby chronić nieuważnego użytkownika przed nieumyślnym ujawnieniem danych wrażliwych (np. w sytuacji, gdy wynik polecenia trafia do ogólnodostępnych logów). Pamiętajmy, że base64 to jedynie kodowanie tekstu, a nie jego szyfrowanie.

Aby odczytać zawartość, wykonałem:

$ kubectl get secret my-credentials -o jsonpath='{.data}' | jq
{
  "password": "cGFzc3dvcmQxMjMK",
  "username": "bWllY3p5aw=="
}
$ echo 'bWllY3p5aw==' | base64 -d
mieczyk$ echo 'cGFzc3dvcmQxMjMK' | base64 -d
password123

Po odkodowaniu base64 okazało się, że dane uwierzytelniające są prawidłowe (mieczyk:password123), ale jedna rzecz zwróciła moją uwagę: na standardowym wyjściu, po mieczyk znak zachęty pojawił się w tej samej linii, a po haśle password123 już w nowej linii.

Chyba już wiedziałem, co jest nie tak… Szybka weryfikacja:

$ echo 'password123' | base64
cGFzc3dvcmQxMjMK
$ echo -n 'password123' | base64
cGFzc3dvcmQxMjM=

Bingo! Okazało się, że podczas tworzenia sekretu K8s, hasło zostało podane razem ze znakiem nowej linii, które również zostało zakodowane w base64 i wykorzystane później do próby nawiązania połączenia z RabbitMQ. Poprawne hasło oczywiście nie zawierało białego znaku nowej linii i stąd ten początkowy błąd.

Należy więc pamiętać, że polecenie echo domyślnie dodaje znak nowej linii, a przełącznik -n temu zapobiega. Tego typu pomyłki mogą zdarzyć się w sytuacji, kiedy próbujemy dodać do sekretu zawartość pliku tekstowego, utworzonego z linii poleceń, na przykład echo 'password' > ./pass.txt.

Scena z filmu Chłopaki nie płaczą (2000).

Jeśli więc chcemy dodać sekret, w którym hasło jest brane z pliku, powinniśmy zrobić to w ten sposób:

$ echo -n 'password123' > ./pass.txt

$ kubectl create secret generic my-credentials --from-literal=username=mieczyk --from-file=password=./pass.txt
secret/my-credentials created

$ kubectl get secret my-credentials -o jsonpath='{.data}' | jq
{
  "password": "cGFzc3dvcmQxMjM=",
  "username": "bWllY3p5aw=="
}

Ewentualnie można jeszcze użyć polecenia printf, które nie dodaje znaku nowej linii w trybie domyślnym:

$ printf %s 'password123' > ./pass.txt

O autorze

Łukasz Mieczkowski

Programista, który zainteresował się cyberbezpieczeństwem.

Dodaj komentarz

Cyberbezpieczeństwo okiem programisty

Łukasz Mieczkowski

Programista, który zainteresował się cyberbezpieczeństwem.

Kontakt

Zapraszam do kontaktu za pośrednictwem mediów społecznościowych.