Przejdź do treści
Paweł Maj

Dostęp do serwera tylko do odczytu

Dotyczy tylko aplikacji na własnym serwerze (Ubuntu / Debian). Przy Vercelu, Netlify czy Lovable nie ma czego przygotowywać.

Konto SSH tylko do odczytu

Osobne konto audyt: bez sudo do dowolnych komend, poza grupami administracyjnymi, bez dostępu do plików .env, logowanie wyłącznie kluczem, wygasa samo po 30 dniach. Około 15 minut — instrukcja poniżej. Nie znasz się na serwerach? Przejdziemy to razem na Meet, około 20 minut: udostępniasz ekran i sam wpisujesz komendy, a ja mówię, co wpisać, i od razu sprawdzam wynik — nie dostaję przy tym dostępu do serwera. Nie możesz dać konta wcale? Zadzwoń — ustalimy to indywidualnie.

Z konta korzystam ja i agent AI, z którym prowadzę audyt — wyłącznie do odczytu, bez dostępu do plików .env. Zanim agent się połączy, sam sprawdzam, co konto widzi. Dostęp dajesz dopiero po podpisaniu zgody na audyt i NDA. Wszystkie komendy wykonujesz Ty, na swoim koncie administratora.

1

Niczego ode mnie nie potrzebujesz

Mój klucz publiczny SSH jest już wpisany w komendę z kroku 5 — to nie jest sekret, można go pokazać każdemu. Datę wygaśnięcia komenda wylicza sama: dostęp wygasa 30 dni po wgraniu klucza. Wszystkie komendy wklejasz bez zmian, poza ścieżkami oznaczonymi wprost jako przykładowe.

Hasła nie ustawiamy i nigdzie go nie przesyłamy — logowanie wyłącznie kluczem.

2

Sprawdź, czy sekrety nie leżą w plikach czytelnych dla wszystkich

Nowe konto zobaczy każdy plik, który ma prawo odczytu „dla wszystkich”. Zanim je założysz, upewnij się, że nie ma wśród nich plików z hasłami i kluczami:

sudo find /var/www /srv /opt /home /etc -xdev -type f -perm -o=r \
  -not -path '*/node_modules/*' -not -path '*/migrations/*' \
  \( -name '.env*' -o -name '*.key' -o -name '*secret*' -o -name '*backup*' \
     -o -name '*.dump' -o -name '*.sql' -o -name '*.cron' -o -name 'ecosystem.config*' \) \
  -not -name '.env.example' 2>/dev/null

Druga komenda szuka kluczy prywatnych — publiczne certyfikaty pomija:

sudo find /var/www /srv /opt /home /etc -xdev -type f -perm -o=r -name '*.pem' \
  -exec grep -l 'PRIVATE KEY' {} + 2>/dev/null

Każdy wynik to plik, który konto audytowe mogłoby przeczytać. Jeśli zawiera sekret — odbierz odczyt innym użytkownikom albo usuń plik, jeśli jest zbędny:

sudo chmod o-r /sciezka/do/pliku

Typowe miejsca, w których sekret ląduje poza .env: kopie crontaba, stare zrzuty bazy, pliki konfiguracyjne menedżera procesów (np. ecosystem.config.js w pm2), skrypty wdrożeniowe. Crontaby użytkowników są chronione, ale /etc/crontab i pliki w /etc/cron.d/ może czytać każdy — sekretów nie trzymaj ani tam, ani w kopiach crontaba.

Ta kontrola i tak jest częścią audytu — lepiej, żeby wynik był pusty, zanim ktokolwiek się zaloguje.

3

Załóż konto audyt

sudo adduser --disabled-password --gecos "" audyt
sudo gpasswd -d audyt users
id audyt

Nowsze wersje Ubuntu same dopisują konto do grupy users — druga linia je z niej usuwa (jeśli konta tam nie ma, zobaczysz komunikat o błędzie i to jest w porządku). Wynik id ma pokazywać tylko grupę audyt. Konta nie dodawaj do żadnej z grup:

GrupaDlaczego nie
sudo, admin, wheelpełne uprawnienia administratora
docker, lxddostęp do Dockera to w praktyce dostęp roota
adm, systemd-journalodczyt logów — w logach są adresy IP i dane użytkowników aplikacji
grupa użytkownika aplikacji (np. www-data, ubuntu)odczyt plików aplikacji, w tym .env, jeśli ma prawa dla grupy
4

Zablokuj kontu odczyt plików .env

Pliki .env i .env.* są dla konta audyt zamknięte niezależnie od tego, jakie mają prawa dla grupy i dla wszystkich — blokadę zakłada lista ACL. Sprawdź, czy masz narzędzie:

command -v setfacl || sudo apt install acl

Potem zablokuj wszystkie takie pliki na serwerze — komenda wypisze każdy, który objęła:

sudo find / -xdev -type f \( -name '.env' -o -name '.env.*' \) \
  -exec setfacl -m u:audyt:--- {} + -print

Kontrola dla jednego pliku (ścieżkę podmień na swoją) — wynik ma zawierać user:audyt:---:

getfacl /var/www/aplikacja/.env | grep audyt

Blokada obejmuje pliki, które istnieją teraz. Jeśli w trakcie audytu wdrożenie utworzy nowy plik .env, powtórz komendę wyżej.

5

Wgraj klucz z datą ważności

Wklej komendy bez zmian — klucz jest mój, a datę wygaśnięcia (30 dni od dziś) wylicza date. Wpis jest pocięty na krótkie kawałki, bo długa linia łamie się przy wklejaniu do terminala:

sudo install -d -m 700 -o audyt -g audyt /home/audyt/.ssh
printf '%s%s%s%s%s\n' \
  'expiry-time="' "$(date -d '+30 days' +%Y%m%d)" '",no-port-forwarding,' \
  'no-agent-forwarding,no-X11-forwarding ' \
  'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAhfddecDgwE2AoGebHohtqn8lqxSbxgjSbFTRjYlBVx vibeaudit-audytor' \
  | sudo tee /home/audyt/.ssh/authorized_keys
sudo wc -l /home/audyt/.ssh/authorized_keys
sudo chown audyt:audyt /home/audyt/.ssh/authorized_keys
sudo chmod 600 /home/audyt/.ssh/authorized_keys
sudo chage -E "$(date -d '+30 days' +%F)" audyt

wc -l ma pokazać 1. Więcej linii oznacza, że wpis się złamał — logowanie kluczem nie zadziała, wklej komendę jeszcze raz.

Dwie blokady czasowe: expiry-time wyłącza klucz, a chage -E całe konto — w tym samym dniu, nawet jeśli zapomnisz je usunąć.

expiry-time wymaga OpenSSH 8.2 lub nowszego (Ubuntu 20.04+, Debian 11+). Wersję sprawdzisz komendą ssh -V.

Sprawdź, czy w konfiguracji SSH jest lista dozwolonych użytkowników:

sudo sshd -T | grep -Ei '^(allowusers|allowgroups)'

Pusty wynik = brak listy, nic nie robisz. Jeśli lista jest, poniższa komenda dopisze do niej audyt w osobnym pliku /etc/ssh/sshd_config.d/90-audyt.conf — istniejących wpisów nie zmienia, przed przeładowaniem SSH sprawdza składnię (sshd -t), a przy błędzie usuwa swój plik. Najpierw sprawdza, czy konfiguracja SSH wczytuje pliki z tego katalogu; jeśli nie, albo lista stoi w bloku Match, niczego nie zmienia i prosi o telefon. Na końcu wypisuje listę — ma na niej być audyt obok dotychczasowych kont:

if ! sudo sshd -T | grep -qi '^allowusers '; then
  echo 'Brak listy AllowUsers - nic nie zmieniam'
elif ! sudo awk '{l=tolower($0)} l~/^[[:space:]]*match[[:space:]]/{m=1} l~/^[[:space:]]*include[[:space:]]/&&index(l,"sshd_config.d")&&!m{i=1} l~/^[[:space:]]*allowusers[[:space:]]/&&m{a=1} END{exit !(i&&!a)}' /etc/ssh/sshd_config; then
  echo 'Nietypowa konfiguracja SSH - zadzwoń do mnie, dopiszemy konto razem'
else
  echo 'AllowUsers audyt' | sudo tee /etc/ssh/sshd_config.d/90-audyt.conf
  if sudo sshd -t; then sudo systemctl reload ssh || sudo systemctl reload sshd; else sudo rm /etc/ssh/sshd_config.d/90-audyt.conf; fi
  sudo sshd -T | grep -i '^allowusers'
fi

Jeśli pierwsza komenda pokazała allowgroups, konto musiałoby trafić do jednej z tych grup — zadzwoń, ustalimy to razem.

Na koniec sprawdź, czy SSH wpuszcza konto audyt do próby logowania. Pierwsza komenda zakończy się Permission denied — to w porządku, bo mój klucz jest tylko u mnie. Druga pokazuje wpis serwera o tej próbie:

ssh -o BatchMode=yes -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
  -p "$(sudo sshd -T | awk '/^port /{print $2; exit}')" audyt@localhost true
sudo journalctl -u ssh -u sshd --since '-2 min' --no-pager | grep audyt

Wpis z not allowed because not listed in AllowUsers albo expired oznacza, że konto jest zablokowane — wróć do komend wyżej. Wpis bez tych słów (np. Connection closed by authenticating user audyt) jest prawidłowy.

6

Zrób jednorazowy zrzut ustawień serwera

Część ustawień widzi tylko administrator: faktyczna konfiguracja SSH, reguły firewalla, kto może łączyć się z bazą, zadania w harmonogramie. Zamiast dawać kontu sudo, uruchamiasz raz mój skrypt i przesyłasz mi plik z wynikiem. Skrypt wyłącznie czyta standardowe pliki systemowe — nie zagląda do katalogów aplikacji, plików .env ani logów, z bazy PostgreSQL odczytuje tylko listę kont i baz (bez danych), a hasła i tokeny w wyniku zamazuje. Druga komenda pokazuje jego treść, zanim go uruchomisz (wyjście z podglądu: q):

curl -fsSLO https://vibeaudit.pl/zrzut-admin.sh
less zrzut-admin.sh
sudo bash zrzut-admin.sh > zrzut-admin.txt
wc -l zrzut-admin.txt

Skrypt wypisuje też uprawnienia sudo zwykłych kont użytkowników. Jeśli aplikację wdraża konto systemowe (np. deploy bez powłoki logowania), podaj jego nazwę przy uruchomieniu:

sudo bash zrzut-admin.sh deploy > zrzut-admin.txt

Wolisz uruchamiać komendy ze swojego komputera? Użyj ssh -t — bez -t sudo nie zapyta o hasło, a podgląd nie zadziała. admin@adres-serwera zamień na swoje konto administratora i adres serwera. Kolejno: pobranie i podgląd skryptu, uruchomienie, pobranie wyniku na komputer:

ssh -t admin@adres-serwera 'curl -fsSLO https://vibeaudit.pl/zrzut-admin.sh && less zrzut-admin.sh'
ssh -t admin@adres-serwera 'sudo bash zrzut-admin.sh > zrzut-admin.txt && wc -l zrzut-admin.txt'
scp admin@adres-serwera:zrzut-admin.txt .

Plik zrzut-admin.txt pobierz na swój komputer (np. scp albo przez program, którym łączysz się z serwerem) i prześlij mi go w kroku 8 — jako plik, nie wklejony do maila, bo wklejka łamie linie. Możesz go przejrzeć przed wysłaniem.

Bez tego kroku punkty „firewall”, „konfiguracja SSH”, „dostęp do bazy” i „harmonogram zadań” trafią do raportu jako niesprawdzone.

7

Sprawdź, co konto widzi

Zanim prześlesz mi dane dostępowe, sprawdź konto sam. Ścieżkę do .env podmień na swoją:

sudo -l -U audyt
sudo -u audyt cat /var/www/aplikacja/.env
sudo -u audyt ls /root

Oczekiwany wynik: sudo -l mówi, że konto nie może używać sudo, a dwie kolejne komendy kończą się Permission denied. Jeśli .env się wyświetla — wróć do kroku 4 i sprawdź ponownie.

Konto zobaczy

  • wersję systemu i listę oczekujących aktualizacji
  • otwarte porty i działające procesy
  • konfigurację nginx / Apache i SSH
  • konfigurację SSH, firewalla i bazy — z pliku z kroku 6, nie z konta
  • uprawnienia katalogów aplikacji
  • pliki czytelne dla wszystkich — stąd krok 2

Konto nie zobaczy

  • plików .env i sekretów aplikacji
  • crontabów innych użytkowników
  • zawartości bazy danych
  • logów systemowych i logów aplikacji
  • kluczy prywatnych i certyfikatów
  • katalogów domowych innych użytkowników
8

Prześlij mi dane dostępowe

Wystarczą cztery rzeczy: adres serwera, port SSH i nazwa konta (audyt) oraz plik zrzut-admin.txt z kroku 6 — na kontakt@vibeaudit.pl. Żadnych haseł, kluczy prywatnych ani plików .env — jeśli czegoś potrzebuję, poproszę o to wprost.

9

Po audycie usuń konto

sudo rm -f /etc/ssh/sshd_config.d/90-audyt.conf
sudo sshd -t && (sudo systemctl reload ssh || sudo systemctl reload sshd)
sudo pkill -u audyt
sudo userdel -r audyt
rm zrzut-admin.sh zrzut-admin.txt

Blok usuwa też plik z listą dozwolonych użytkowników SSH, jeśli komenda z kroku 5 go utworzyła (jeśli nie — nic się nie stanie), i przeładowuje SSH. Potwierdzenie usunięcia odnotowuję w dokumentacji audytu.

Nawet jeśli tego nie zrobisz, klucz i konto wygasną same w dniu ustawionym w kroku 5.

Instrukcja dotyczy serwerów z systemem Ubuntu lub Debian; na innych systemach nazwy pakietów i ścieżki mogą się różnić. Coś nie działa? Zadzwoń albo napisz — przejdziemy przez to razem. Więcej pytań: FAQ.