Szyfrowanie po stronie klienta
Argon2id, HKDF i XChaCha20-Poly1305 chronią dane przed wysłaniem ich do API.
Self-hosted menedżer haseł szyfrowany end-to-end w przeglądarce, z YubiKey/FIDO2 i bez dostępu serwera do jawnych sekretów.
N3X Vault to niezależny menedżer haseł z własnym protokołem i szyfrowaniem end-to-end w przeglądarce. Serwer nie otrzymuje hasła głównego ani odszyfrowanego klucza konta.
Stan wydania: security alpha nadaje się do przeglądu protokołu, developmentu i kontrolowanych testów. Nie przechowuj jeszcze sekretów produkcyjnych.
Interfejs aplikacji
Prawdziwy interfejs produktu — kliknij podgląd, aby zobaczyć go w pełnym rozmiarze.
1 / 3
Loginy, bezpieczne notatki i kody TOTP są szyfrowane po stronie przeglądarki przed wysłaniem do własnego serwera.
Hosting zarządzany przez N3X
N3X może przygotować i utrzymywać tę aplikację: od uruchomienia i SSL po aktualizacje, monitoring oraz kopie zapasowe.
Argon2id, HKDF i XChaCha20-Poly1305 chronią dane przed wysłaniem ich do API.
Odblokowanie przez WebAuthn PRF, inwentaryzacja kluczy i wymóg przygotowania dwóch urządzeń.
Szyfrowany eksport i wznawialne odtworzenie do pustej instalacji bez ukrytego klucza uniwersalnego.
Rust API i PostgreSQL przechowują ciphertext, parametry protokołu i minimalne metadane operacyjne.
15-minutowe sesje przeglądarki są przechowywane tylko w pamięci i można je odwołać po stronie serwera.
Prywatna sieć bazy, kontenery bez zbędnych capabilities, read-only filesystem i sekrety w wolumenie.
KONFIGURATOR DOCKER
Ustaw docelową domenę przed rejestracją YubiKey. Zmiana RP ID po wdrożeniu unieważni istniejące poświadczenia sprzętowe.
Musi być ostateczną domeną reverse proxy, np. vault.firma.pl.
name: n3xvault
x-api-security: &api-security
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
tmpfs:
- /tmp:size=32m,mode=1777
x-web-security: &web-security
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
tmpfs:
- /tmp:size=32m,mode=1777
- /app/.next/cache:size=64m,mode=0700,uid=1001,gid=1001
services:
bootstrap:
image: alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce
restart: "no"
command:
- /bin/sh
- -ec
- |
umask 077
if [ ! -s /run/n3xvault/database_password ]; then
od -An -N32 -tx1 /dev/urandom | tr -d ' \n' > /run/n3xvault/database_password
fi
if [ ! -s /run/n3xvault/bootstrap_token ]; then
od -An -N32 -tx1 /dev/urandom | tr -d ' \n' > /run/n3xvault/bootstrap_token
fi
chmod 0444 /run/n3xvault/database_password /run/n3xvault/bootstrap_token
volumes:
- runtime_secrets:/run/n3xvault
networks:
- backend
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
postgres:
image: postgres:17-alpine@sha256:18cfe3ef5e6815560c98237d6216d1e5119702fb0f3894c8785dd58b8bbe5d73
restart: unless-stopped
depends_on:
bootstrap:
condition: service_completed_successfully
environment:
POSTGRES_DB: n3xvault
POSTGRES_USER: n3xvault
POSTGRES_PASSWORD_FILE: /run/n3xvault/database_password
volumes:
- postgres_data:/var/lib/postgresql/data
- runtime_secrets:/run/n3xvault:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n3xvault -d n3xvault"]
interval: 5s
timeout: 5s
retries: 12
networks:
- backend
api:
image: ghcr.io/nexitpl/n3xvault-api:alpha
restart: unless-stopped
depends_on:
bootstrap:
condition: service_completed_successfully
postgres:
condition: service_healthy
environment:
N3XVAULT_BIND: 0.0.0.0:8080
N3XVAULT_DATABASE_HOST: postgres
N3XVAULT_DATABASE_NAME: n3xvault
N3XVAULT_DATABASE_USER: n3xvault
N3XVAULT_DATABASE_PASSWORD_FILE: /run/n3xvault/database_password
N3XVAULT_BOOTSTRAP_TOKEN_FILE: /run/n3xvault/bootstrap_token
N3XVAULT_ENVIRONMENT: ${N3XVAULT_ENVIRONMENT:-development}
N3XVAULT_PUBLIC_ORIGIN: https://vault.example.com
N3XVAULT_WEBAUTHN_RP_ID: vault.example.com
N3XVAULT_SESSION_TTL_SECONDS: ${N3XVAULT_SESSION_TTL_SECONDS:-900}
N3XVAULT_WEBAUTHN_FLOW_TTL_SECONDS: ${N3XVAULT_WEBAUTHN_FLOW_TTL_SECONDS:-300}
N3XVAULT_DEVICE_PAIRING_TTL_SECONDS: ${N3XVAULT_DEVICE_PAIRING_TTL_SECONDS:-300}
RUST_LOG: ${N3XVAULT_LOG_LEVEL:-n3xvault_api=info,tower_http=info}
volumes:
- runtime_secrets:/run/n3xvault:ro
healthcheck:
test: ["CMD", "curl", "--fail", "--silent", "http://127.0.0.1:8080/health/ready"]
interval: 15s
timeout: 3s
retries: 5
start_period: 15s
networks:
- backend
<<: *api-security
web:
image: ghcr.io/nexitpl/n3xvault-web:alpha
restart: unless-stopped
depends_on:
api:
condition: service_healthy
environment:
N3XVAULT_API_INTERNAL_URL: http://api:8080
ports:
- "127.0.0.1:8793:3000"
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:3000/api/health"]
interval: 20s
timeout: 5s
retries: 5
start_period: 20s
networks:
- frontend
- backend
<<: *web-security
networks:
frontend:
backend:
internal: true
volumes:
postgres_data:
runtime_secrets:
mkdir -p n3xvault
cd n3xvault
# Zapisz konfigurację jako docker-compose.yml
docker compose up -d
docker compose ps
docker compose run --rm api print-bootstrap-token
curl -fsS http://127.0.0.1:8793/api/healthPORADNIK
Instalacja, pierwsze kroki, ustawienia i rozwiązywanie problemów.
N3X Vault to menedżer haseł z szyfrowaniem po stronie klienta, uruchamiany na własnym serwerze.
Security alpha: używaj tej wersji wyłącznie do kontrolowanych testów. Nie jest zatwierdzona do przechowywania sekretów produkcyjnych.
Potrzebujesz serwera z Docker Compose, przeglądarki i docelowej domeny HTTPS. PostgreSQL jest dołączony do zestawu. Odblokowanie kluczem sprzętowym wymaga zgodnej przeglądarki oraz klucza FIDO2 obsługującego WebAuthn PRF; odblokowanie hasłem pozostaje dostępne.
docker-compose.yml w osobnym katalogu na serwerze.docker compose up -d
docker compose ps
docker compose run --rm api print-bootstrap-token
Publiczny adres aplikacji i WebAuthn RP ID muszą odpowiadać docelowej domenie przed rejestracją kluczy. Późniejsza zmiana RP ID uniemożliwi używanie wcześniej zarejestrowanych poświadczeń sprzętowych.
Token pierwszego uruchomienia traktuj jak hasło. Nie zapisuj go w publicznych logach ani definicji stosu. Po utworzeniu pierwszego konta rejestracja właściciela zostaje zamknięta.
Portainer może nie udostępniać katalogu roboczego Compose. Token już istnieje w trwałym wolumenie; odczytaj go przez uruchomiony kontener API.
docker ps \
--filter label=com.docker.compose.service=api \
--format 'table {{.Names}}\t{{.Label "com.docker.compose.project"}}'
Z listy wybierz kontener API właściwego stosu i zastąp jego nazwą PORTAINER_API_CONTAINER:
docker exec PORTAINER_API_CONTAINER \
/usr/local/bin/n3xvault-api print-bootstrap-token
Możesz również otworzyć Portainer → Containers → kontener API → Console → /bin/sh i wykonać:
/usr/local/bin/n3xvault-api print-bootstrap-token
Jeżeli API nie działa, znajdź kontener bootstrap właściwego stosu. Zastąp jego nazwą PORTAINER_BOOTSTRAP_CONTAINER; tymczasowy kontener odczyta tylko istniejący wolumen:
docker ps -a \
--filter label=com.docker.compose.service=bootstrap \
--format 'table {{.Names}}\t{{.Label "com.docker.compose.project"}}'
docker run --rm \
--volumes-from PORTAINER_BOOTSTRAP_CONTAINER:ro \
alpine:3.22 \
cat /run/n3xvault/bootstrap_token
Użyj referencji obrazu Alpine przypiętej w pobranym Compose, jeśli nie masz lokalnie alpine:3.22. Nie usuwaj wolumenu runtime_secrets ani nie generuj nowego tokenu w ramach rozwiązywania tego problemu.
Po odblokowaniu hasłem przejdź do Bezpieczeństwo, dodaj zgodny klucz FIDO2 i wykonaj drugi dotyk wymagany do konfiguracji odblokowania. Zarejestruj dwa klucze przed poleganiem na tej metodzie.
W odblokowanym sejfie wybierz Recovery kit. Zapisz zaszyfrowany plik .n3xvault na chronionym nośniku offline.
Odtwarzanie wymaga pustej instalacji, pliku recovery kit, tokenu instalacji docelowej oraz oryginalnego hasła głównego albo zarejestrowanego klucza z PRF. Odtwarzanie kluczem sprzętowym wymaga tego samego RP ID. Przy zmianie domeny użyj ścieżki hasła i ponownie zarejestruj klucze. Sprawdź odtwarzanie na odizolowanej pustej instalacji.
Wykonuj spójne kopie postgres_data i runtime_secrets oraz przechowuj osobny recovery kit. Aktualizację wykonuj zgodnie z opisem wydania, zachowując wolumeny i dotychczasową domenę:
docker compose pull
docker compose up -d
docker compose ps
Przy problemie z uruchomieniem sprawdź stan kontenerów oraz docker compose logs --tail=100 api web. Jeśli klucz nie odblokowuje sejfu, sprawdź HTTPS, domenę, RP ID i obsługę PRF w przeglądarce. Zmienianie adresu między localhost i 127.0.0.1 także zmienia kontekst poświadczeń.
Szyfrowanie i odszyfrowywanie danych odbywa się w kliencie. Sam token instalacji ani sesji nie odszyfruje zawartości sejfu.