Cum protejezi cheile API ale unui serviciu cu systemd-creds și LoadCredential=

Configurare noua (How To)

Situatie

O metodă mai sigură decât păstrarea unei chei API în variabile de mediu sau direct în fișierul unității este să folosești credentialele systemd.

Modelul este:

cheie API
   ↓
systemd-creds encrypt
   ↓
fișier criptat
   ↓
LoadCredentialEncrypted=
   ↓
systemd decriptează la pornire
   ↓
/run/credentials/...
   ↓
serviciul citește cheia

Credentialele systemd sunt încărcate la pornirea serviciului, sunt read-only în timpul execuției și sunt accesibile procesului serviciului prin directorul indicat de variabila CREDENTIALS_DIRECTORY.

1. Verifică dacă ai systemd-creds

systemd-creds --version

Comenzile pentru criptare și decriptare sunt disponibile în versiunile moderne de systemd; mecanismul de credentiale a fost introdus începând cu systemd 247, iar criptarea cu systemd-creds din documentația actuală este disponibilă începând cu versiunea 250.

2. Creează cheia API

Exemplu:

sudo install -d -m 700 /etc/myapp
sudo nano /etc/myapp/api-key.txt

Pune cheia în fișier:

sk-example-very-secret-key

Protejează fișierul:

sudo chmod 600 /etc/myapp/api-key.txt
sudo chown root:root /etc/myapp/api-key.txt

Acest fișier este doar etapa inițială. Nu vrem ca aplicația să citească direct această copie plaintext.

3. Criptează cheia cu systemd-creds

sudo systemd-creds encrypt \
  --name=api_key \
  /etc/myapp/api-key.txt \
  /etc/myapp/api-key.cred

Rezultatul este un credential criptat și autentificat. systemd folosește AES-256-GCM, iar protecția poate folosi o cheie derivată din TPM2, cheia locală din /var/lib/systemd/credential.secret sau ambele, în funcție de configurația sistemului.

După ce ai verificat că fișierul criptat există:

sudo ls -l /etc/myapp/api-key.cred

șterge copia plaintext:

sudo shred -u /etc/myapp/api-key.txt

4. Configurează serviciul

Editează serviciul:

sudo systemctl edit myapp.service

Adaugă:

[Service]
LoadCredentialEncrypted=api_key:/etc/myapp/api-key.cred

Aici:

  • api_key este numele credentialului;
  • /etc/myapp/api-key.cred este fișierul criptat;
  • systemd îl decriptează la pornirea serviciului;
  • aplicația primește credentialul în directorul său de credentiale.

5. Citește cheia din aplicație

Directorul credentialelor este disponibil prin:

echo "$CREDENTIALS_DIRECTORY"

Credentialul nostru va fi disponibil astfel:

$CREDENTIALS_DIRECTORY/api_key

De exemplu, un script poate face:

#!/bin/sh

API_KEY=$(cat "$CREDENTIALS_DIRECTORY/api_key")

exec /usr/local/bin/myapp --api-key "$API_KEY"

Mai bine este ca aplicația să știe să citească direct fișierul credentialului, fără să îl transforme într-o variabilă de mediu. Documentația systemd recomandă acest model deoarece credentialele nu sunt propagate prin proces tree precum variabilele de mediu.

6. Reîncarcă systemd

sudo systemctl daemon-reload

Verifică unitatea:

systemd-analyze verify myapp.service

7. Pornește serviciul

sudo systemctl restart myapp.service

Verifică:

systemctl status myapp.service

Și logurile:

journalctl -u myapp.service -b

8. Verifică existența credentialului fără să afișezi cheia

Poți verifica faptul că serviciul are directorul de credentiale:

systemctl show myapp.service -p Environment

Pentru o verificare mai practică, aplicația sau wrapper-ul poate verifica existența fișierului:

test -r "$CREDENTIALS_DIRECTORY/api_key"

Evită comenzi precum:

cat /run/credentials/myapp.service/api_key

deoarece afișează secretul în terminal.

9. Adaugă și sandboxing

Credentialele sunt și mai utile împreună cu izolarea serviciului:

[Service]
LoadCredentialEncrypted=api_key:/etc/myapp/api-key.cred

User=myapp
Group=myapp

NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes

Documentația systemd recomandă și namespacing-ul mount-urilor pentru serviciile care folosesc credentiale; mai multe opțiuni de sandboxing, precum ProtectSystem, implică deja PrivateMounts. Astfel, directorul de credentiale al serviciului poate fi ascuns față de alte servicii.

10. Unde ajunge cheia?

Pentru un serviciu system-level, credentialele sunt în mod obișnuit disponibile sub:

/run/credentials/<numele-serviciului>/

Nu este recomandat să hardcodezi această cale în aplicație. Folosește CREDENTIALS_DIRECTORY.

11. De ce este mai bine decât Environment=

O abordare de evitat este:

[Service]
Environment=API_KEY=secret123

Variabilele de mediu sunt moștenite de procesele copil și pot traversa anumite limite de securitate ale proceselor. Documentația systemd recomandă LoadCredential, LoadCredentialEncrypted sau SetCredentialEncrypted pentru date sensibile.

De asemenea, nu pune o cheie secretă direct în:

SetCredential=api_key:secret123

Unit files sunt accesibile și prin D-Bus, deci SetCredential nu este potrivit pentru secrete sensibile. Pentru astfel de date se recomandă LoadCredential sau LoadCredentialEncrypted.

12. Fluxul complet

/etc/myapp/api-key.txt
        │
        │ systemd-creds encrypt
        ▼
/etc/myapp/api-key.cred
        │
        │ LoadCredentialEncrypted=
        ▼
systemd
        │
        │ decrypt + authenticate
        ▼
CREDENTIALS_DIRECTORY/api_key
        │
        ▼
myapp.service

Checklist

[ ] cheia nu este în unit file
[ ] cheia nu este în Environment=
[ ] cheia plaintext este ștearsă după criptare
[ ] fișierul .cred este protejat
[ ] LoadCredentialEncrypted= este folosit
[ ] aplicația citește CREDENTIALS_DIRECTORY
[ ] serviciul rulează cu User= dedicat
[ ] NoNewPrivileges=yes
[ ] ProtectSystem=strict
[ ] logurile nu afișează cheia
[ ] systemd-analyze verify trece

Nu tratezi cheia API ca pe o configurație obișnuită. O criptezi la storage, o atașezi serviciului prin LoadCredentialEncrypted=, iar systemd o decriptează doar când serviciul pornește. Aplicația primește cheia ca fișier read-only într-un spațiu dedicat serviciului, fără să fie nevoie să o pui în unit file sau în variabile de mediu.

Solutie

Tip solutie

Permanent

Voteaza

(0 din 0 persoane apreciaza acest articol)

Despre Autor

Leave A Comment?