# Šifrování a klíče (https://docs.vitrinka.ai/cs/security/encryption)



## Obálka [#envelope]

Každý workspace vlastní 32bajtový datový šifrovací klíč AES-256-GCM (DEK). Řídicí databáze ho ukládá jen v zabalené podobě, zašifrovaný klíčem instalace pro šifrování klíčů (KEK), spolu s id poskytovatele, který ho zabalil. Rozbalení probíhá v procesu serveru při otevření workspace; rozbalený DEK se tam drží v paměti a nezapisuje se do databáze nájemce, do úložiště blobů ani do logu.

KEK se načítá z `VITRINKA_KEK` jako 64 hexadecimálních znaků. Pokud není nastavený, server použije `VITRINKA_MASTER_KEY`. Neplatná hodnota `VITRINKA_KEK` zabrání spuštění serveru. S `VITRINKA_KEYS_PROVIDER=openbao` KEK v prostředí vůbec není — zůstává uvnitř transit enginu a server mu posílá jen zabalené klíče — viz [Úložiště klíčů](#keystore).

## Co je zapečetěné [#sealed]

| Data                                                                                                                                                                                                                                                                                                                    | Formát                                                               | Vázáno na                  |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------- |
| Citlivé sloupce nájemce — zachycené události nahraných sessions a hlášení chyb, zprávy ve vláknech anotací a zadání revizí, dokumenty stránek a jejich historie úprav, paměť projektu, zmrazené snímky sdílení, textové mapy snímků obrazovky a souborů, záznamy pracovního času, úlohy a zpětná vazba hodnocení review | `enc2` — verze, id klíče, 12bajtový nonce, AES-256-GCM               | id workspace + druh obsahu |
| Každý blob — nahrané soubory, snímky obrazovky, nahrávky, zdroje náhledů                                                                                                                                                                                                                                                | `enc2` s druhem `blob`                                               | id workspace + `blob`      |
| Archivy záloh a další operátorské artefakty                                                                                                                                                                                                                                                                             | `enc2f` — čerstvý zabalený DEK, pak rámované 4 MiB bloky AES-256-GCM | index rámce + typ rámce    |

Šifrování váže obsah na workspace a jeho typ. Přesunutí šifrované hodnoty do jiného workspace nebo jiného typu sloupce proto způsobí chybu ověření. U archivů se ověřuje také pořadí a typ bloků; poškozený nebo neúplný archiv se nepovažuje za úspěšně obnovený.

Co tabulka nevyjmenovává, se ukládá tak, jak je, a chrání to šifrování svazku a řízení přístupu: zbytek nástěnky (karty kromě stránek, jejich pozice a popisky), úkoly, fulltextové indexy — které drží text odvozený ze zapečetěného obsahu — a identifikátory a časové údaje. Datový svazek hostované služby používá šifrování LUKS2; vlastní instalace si šifrování svazku zajišťuje sama.

## Objektové úložiště [#s3]

S `BLOB_BACKEND=s3` server po bucketu chce i šifrování na straně serveru: `VITRINKA_S3_SSE` je ve výchozím stavu `aes256`, `kms:<key id>` pro klíč spravovaný zákazníkem, nebo `off`. Backend, který hlavičku odmítne, degraduje na `off` s jedním řádkem v logu při startu; důvěrnost stojí tak jako tak na pečetění `enc2` na straně klienta, takže bucket odmítající SSE drží pořád jen šifrovaný text.

Průchozí cache před S3 (`BLOB_CACHE_MAX_GB`) ukládá stejný šifrovaný text, jaký drží bucket. Ukradený adresář cache nevydá nic, co by nevydal ukradený bucket.

## Přísný režim a pečeticí průchod [#strict]

Starší instalace mohou obsahovat data zapsaná před zavedením šifrování. Server je ve výchozím režimu umí přečíst. `VITRINKA_SEAL_STRICT=on` čtení takových dat odmítne, proto tento režim zapněte až po jejich zašifrování a úspěšném ověření.

```bash
vitrinka seal-existing -data /data                                   # sčítání nanečisto, nic nezapíše
vitrinka seal-existing -data /data -manifest /root/seal.json -execute # zapečetí každý řádek a objekt v prostém textu na místě
vitrinka seal-existing -data /data -verify                           # brána: nula nezapečetěných položek, každý zbytek pojmenovaný
```

Příkaz před každou aktualizací ověří zašifrování a zpětné dešifrování hodnoty. Zápis provede jen tehdy, pokud se mezitím původní hodnota nezměnila. `-verify` skončí chybou, pokud zbývají nezašifrované položky nebo pokud neměl co ověřit.

<Callout type="warn">
  `VITRINKA_SEAL_STRICT=on` přepínejte až poté, co nefiltrovaný běh `-verify` vypíše svůj řádek o průchodu. Přísný režim se zbylým prostým textem promění ty položky v chyby čtení, záměrně.
</Callout>

## Rotace klíče instalace [#rotate]

Rotace KEK nikdy nepřešifrovává data: rozbalí DEK každého workspace pod starým KEK a znovu ho zabalí pod novým, jedna transakce na řádek. Bajty DEK se nemění, takže nic pod nimi zapečetěného se nedotkne, a běh přerušený v půlce dokončíte tím, že ho spustíte znovu.

```bash
vitrinka rewrap-deks -data /data -old-kek <hex> -new-kek <hex>           # nanečisto
vitrinka rewrap-deks -data /data -old-kek <hex> -new-kek <hex> -execute
```

Klíče můžou přijít z `VITRINKA_REWRAP_OLD_KEK` a `VITRINKA_REWRAP_NEW_KEK` místo přepínačů, aby se nedostaly do `ps` a historie shellu. Spouštějte se zastaveným serverem: živý proces pořád drží starý KEK a kontrola klíče při startu potvrdí nový, než se obsluha obnoví. Zavedení vyhrazeného `VITRINKA_KEK` na instalaci, která běžela na záložním master klíči, je samo o sobě rotací — starý KEK je hodnota master klíče.

## Úložiště klíčů [#keystore]

`VITRINKA_KEYS_PROVIDER` rozhoduje, kdo drží KEK. Výchozí `env` je obálka popsaná výše: KEK sedí v prostředí serveru a nic ohledně klíčů neopouští hostitele. `openbao` přesune KEK do transit klíče v [OpenBao](https://openbao.org). Server ten klíč nikdy nevidí: každý zabalený DEK pošle transit enginu k dešifrování a dostane zpět DEK, takže kopie disků datového hostitele vydá zabalené klíče a žádný klíč, který by je otevřel. Běžící server drží DEK workspace v paměti jen po dobu, kdy je workspace otevřený, a každé rozbalení je řádek v auditu. Transit klíč se zakládá jako neexportovatelný, takže ani výpis klíčového serveru KEK nevydá, a jeho rotace v OpenBao je průchod přebalením, přesně jako rotace `VITRINKA_KEK`.

K této topologii hostovaná služba přechází: transit engine žije na vyhrazeném klíčovém serveru, který hostí jen úložiště tajemství, dostupném přes privátní síť WireGuard, a každý z obou serverů se při startu odemyká materiálem drženým na tom druhém. Který stav produkce právě provozuje, uvádí [bezpečnostní přehled](https://vitrinka.ai/legal/security). Vlastní instalace zůstává na `env`, pokud se její provozovatel nerozhodne jinak; pod `env` server kvůli klíčům nedělá žádné odchozí volání, takže [odpojené instalace](/cs/security/airgap) se tato sekce netýká.

| Proměnná                                                | Výchozí          | Poznámky                                                                                                                                                                                                                     |
| ------------------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VITRINKA_KEYS_PROVIDER`                                | `env`            | `env` nebo `openbao`.                                                                                                                                                                                                        |
| `VITRINKA_OPENBAO_ADDR`                                 | —                | URL transit enginu, jen v privátní síti. Povinné pro `openbao`.                                                                                                                                                              |
| `VITRINKA_OPENBAO_MOUNT`                                | `transit`        | Cesta transit mountu.                                                                                                                                                                                                        |
| `VITRINKA_OPENBAO_KEY`                                  | `vitrinka-kek`   | Název transit klíče; každý zabalený DEK nese id poskytovatele `openbao:<key>`.                                                                                                                                               |
| `VITRINKA_OPENBAO_ROLE_ID` `VITRINKA_OPENBAO_SECRET_ID` | —                | Přihlášení AppRole. Na straně OpenBao roli navažte na privátní adresu datového hostitele. Tajné.                                                                                                                             |
| `VITRINKA_OPENBAO_TOKEN`                                | —                | Statický token místo AppRole — jen pro vývoj. Tajné.                                                                                                                                                                         |
| `VITRINKA_OPENBAO_CA`                                   | systémové kořeny | Balík CA pro privátní certifikát na transit endpointu — cesta k PEM souboru, nebo přímo text PEM (hodnota začínající `-----BEGIN`; doslovné sekvence `\n` se rozbalí) tam, kde kontejner nepřipojuje žádný soubor hostitele. |
| `VITRINKA_DEK_REVERIFY`                                 | `15m`            | Jak často se DEK každého otevřeného workspace znovu ověřuje proti úložišti klíčů.                                                                                                                                            |

### Kontrola při startu [#keystore-boot]

Při startu server přes poskytovatele rozbalí master klíč a pak spustí stejnou kontrolu klíčů jako `env`: každý uložený zabalený DEK se jednou dešifruje přes transit a každé dešifrování přistane jako řádek `workspace.dek_unwrap` v [auditní stopě](/cs/security/audit) s důvodem `verify`. Jakékoli selhání — nedostupný engine, odmítnutá AppRole, DEK, který už nejde dešifrovat — spuštění odmítne s pojmenovanou nápravou, přesně jako neplatný `VITRINKA_KEK`. Server nikdy neobsluhuje nad úložištěm klíčů, které nedokázal prověřit.

### Opakované ověřování [#keystore-reverify]

Otevření workspace rozbalí jeho DEK přes transit (důvod `open`) a drží ho v paměti. Každých `VITRINKA_DEK_REVERIFY` pool nájemců znovu dešifruje zabalený DEK každého otevřeného workspace (důvod `reverify`); neshoda nebo odmítnutí workspace z poolu vyřadí a výsledek zaznamená jako zamítnutý. Klíč odvolaný nebo odrotovaný v OpenBao tak přestane obsluhovat do jednoho intervalu, bez restartu. Smyčka nikdy nedrží zámek poolu přes síťové volání, takže pomalý klíčový server zdrží opakované ověření, ne požadavek. Rotace zapisuje důvod `rewrap`; [kryptografické smazání](/cs/security/retention#shred) nic nerozbaluje a zapisuje se na vlastní řádek `workspace.dek.shred`, který nově uvádí i poskytovatele. Klíčový server vede vlastní auditní zařízení jako nezávislý záznam týchž volání.

### Přesun do úložiště klíčů a zpět [#keystore-migrate]

`rewrap-deks` je migrační dveře v obou směrech. `-to openbao` přebalí každý DEK z KEK v prostředí do transit klíče, jedna transakce na řádek, a k každému otiskne nové id poskytovatele; právě id poskytovatele na řádku dovoluje řádkům `env` a `openbao` uprostřed běhu koexistovat, takže přerušený průchod dokončíte tím, že ho spustíte znovu. Master klíč se stěhuje s ním: pod `openbao` je uložený zabalený v řídicí databázi a rozbalený jednou při startu, takže prostředí drží jen přihlašovací údaje AppRole a žádný dlouhodobý klíčový materiál.

```bash
vitrinka rewrap-deks -data /data -old-kek <hex> -to openbao            # nanečisto: spočítá řádky per poskytovatel, nic nezapíše
vitrinka rewrap-deks -data /data -old-kek <hex> -to openbao -execute
vitrinka rewrap-deks -data /data -to env -new-kek <hex> -execute       # návrat: znovu přebalí pod KEK z prostředí
```

Spouštějte se zastaveným serverem, s nastavenými proměnnými OpenBao pro stranu `openbao` a s KEK z prostředí (přepínač nebo `VITRINKA_REWRAP_OLD_KEK` / `VITRINKA_REWRAP_NEW_KEK`) pro stranu `env`; kontrola klíčů při dalším startu prověří cíl, než se obsluha obnoví.

<Callout type="warn">
  Opuštění úložiště klíčů je záměrný sestup: po `-to env` je KEK zpátky v prostředí a stopa rozbalování končí na hostiteli. Dělejte to jen kvůli vyřazení klíčového serveru, který nahrazujete, a napřed přebalte do jeho nástupce.
</Callout>

### Když je úložiště klíčů nedostupné [#keystore-degrade]

Start selže uzavřeně. Běžící server degraduje po jednom workspace: už otevřené workspace obsluhují dál, dokud jim neselže další opakované ověření, neotevřený workspace otevřít nejde a jeho požadavky odpovídají `503` s pojmenovaným úložištěm klíčů. Neúspěšné opakované ověření se zapíše do logu a započítá do `vitrinka_tenant_dek_reverify_failures_total`, který vydává listener `VITRINKA_METRICS_ADDR` — výstrahu na něj nastavte ve svém monitoringu. Krátký výpadek sítě nic nestojí; dlouhý výpadek ukončí obsluhu do jednoho intervalu `VITRINKA_DEK_REVERIFY`, ne naráz. Obnovte klíčový server a další otevření uspěje bez restartu.

## Export auditu [#audit-export]

Auditní stopa opouští hostitele jako hashem řetězené dávky podepsané vlastním klíčem instalace; proměnné, cíle a postup s `audit-verify` najdete v [Auditní stopě](/cs/security/audit#export).
