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.
Leave A Comment?