fix(security): 💀 VERY CRITICAL VULNERABILITY 💀 - auto-generate JWT and encryption secrets on first boot#444
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cab294a878
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
cab294a to
c794158
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c794158269
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
c794158 to
3a9dbb2
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3a9dbb20bd
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
3a9dbb2 to
fa73444
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fa7344412a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
fa73444 to
cce75c0
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cce75c0766
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
cce75c0 to
aee4ae1
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aee4ae123c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
aee4ae1 to
3057654
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 305765406f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
3057654 to
6a3d048
Compare
|
plz remove unnecessary comments to keep the code clean. |
Sure @zxh326 I will do it as soon as possible. Thank you ! |
6a3d048 to
e436507
Compare
|
@zxh326 Unnecesary comments removed, thank you! |
| type SystemSecret struct { | ||
| Name string `gorm:"primaryKey;column:name;type:varchar(64)"` | ||
| Value string `gorm:"column:value;type:text;not null"` | ||
| } |
There was a problem hiding this comment.
The format is very strange.
There was a problem hiding this comment.
Fixed! The entire file was missing tab indentation — gofmt had never been run on it. I ran gofmt -w on the whole file and also added json tags to the struct fields for consistency with the rest of the models in the codebase. Force-pushed.
New installations no longer ship with hardcoded secrets. On first boot, cryptographically secure random values are generated and persisted in a new system_secrets table. Subsequent restarts read from the database, ensuring stable secrets without any manual configuration. Priority chain: env var > database > auto-generate. Upgrade safety for existing installations: - If KITE_ENCRYPT_KEY env var was set: unchanged, value is persisted to DB - If running with the hardcoded default: existing encrypted data is detected, the default is preserved in DB, and a loud warning is emitted urging operators to rotate to a secure key - JWT secret follows the same logic (session invalidation on rotation is acceptable — users simply re-authenticate) Helm chart changes: - jwtSecret and encryptKey default to empty (auto-generated by the app) - Secret template only injects env vars when values are non-empty - existingSecret workflow is unaffected Removes the redundant KITE_ENCRYPT_KEY warning from LoadEnvs (now handled by EnsureSecrets with better migration-aware logic).
e436507 to
fac6cfb
Compare
Fix: Auto-generate JWT and encryption secrets — no more hardcoded keys
TL;DR
Right now, if you
helm install kiteordocker runwithout explicitly settingJWT_SECRETandKITE_ENCRYPT_KEY, the application silently runs with two well-known hardcoded strings that are published in the source code, the Helm chart, and the documentation. Anyone who has read the repo can forge admin JWTs or decrypt every OAuth secret in the database. This PR makes Kite generate its own cryptographically secure secrets on first boot and persist them so they survive restarts — without requiring any configuration.What's actually wrong today?
Two lines in
pkg/common/common.gothat have probably been there since day one:These are the actual defaults. If the corresponding env vars (
JWT_SECRET,KITE_ENCRYPT_KEY) aren't set, these values are used in production. The JWT one doesn't even log a warning — it just works. The encryption key at least emits a warning, but the app starts fine either way.JWT impact
The JWT secret is used in
pkg/auth/oauth_manager.goto sign and validate HS256 tokens. With the default, anyone can craft:{ "user_id": 1, "username": "admin", "exp": 9999999999 }Sign it with
kite-default-jwt-secret-key-change-in-production, send it as a cookie, and they're in. Full admin. Every cluster, every secret, every RBAC rule.Encryption key impact
KiteEncryptKeyis hashed with SHA-256 and used as the AES-GCM key inpkg/utils/secure.go. It encrypts OAuth client secrets, cluster kubeconfigs, and API keys stored in the database. With the default key, if an attacker gets read access to the database (backup file, SQL injection, compromised volume), they can decrypt everything offline.The same defaults are also hardcoded in
charts/kite/values.yaml, so even Helm users who forget to override get the insecure values.The fix: auto-generation with database persistence
New table:
system_secretsA minimal key-value table with just two rows (
jwt_secretandencrypt_key). Values are stored as plain text — notSecretString— because encrypting the encryption key itself would be circular.Priority chain for each secret
Every time Kite starts,
EnsureSecrets()runs this logic (afterInitDB(), before anything readsSecretStringfrom the DB):This means:
docker run/go run .: generates unique secrets, stores them in SQLite. Next restart reads them back. Zero-config, zero risk.helm install: same thing. The chart now defaultsjwtSecretandencryptKeyto empty strings, so no env var is injected, and the app auto-generates.The upgrade path in practice
Operator running Kite with default keys, upgrades to this version:
Nothing breaks. Data stays readable. But the warning is impossible to ignore. When they're ready to rotate, they set the env var and re-encrypt. For JWT, the same logic applies — worst case, existing sessions are invalidated and users re-authenticate, which is fine.
What changed, file by file
pkg/model/system_secret.goSystemSecretmodel,EnsureSecrets(), auto-gen logic with upgrade detectionpkg/model/model.goSystemSecret{}to the AutoMigrate listmain.gomodel.EnsureSecrets()betweenInitDB()andGetGeneralSetting()pkg/common/common.goEnsureSecrets)charts/kite/values.yamljwtSecretandencryptKeynow default to""with updated commentscharts/kite/templates/secret.yamlJWT_SECRET/KITE_ENCRYPT_KEYwhen values are non-emptydocs/config/chart-values.mddocs/zh/config/chart-values.mdWhy this matters beyond security
Simpler onboarding
Every "getting started" guide or tutorial can now be just
docker run ghcr.io/zxh326/kite. No "don't forget to set these two critical env vars" step. No "generate a random string withopenssl rand". It just works securely out of the box.Fewer support issues
I'd bet a good chunk of "my tokens stopped working" or "I can't decrypt cluster configs" issues come from people who set up Kite quickly, forgot to configure secrets, and then either rotated pods (losing env context) or realized too late that they're running on defaults. Auto-generation with DB persistence eliminates that entire category.
Zero overhead
The auto-gen path runs exactly once per secret on first boot. On subsequent starts, it's a single
SELECTper secret (2 queries total, indexed by primary key, ~0.1ms). No runtime overhead during normal operation.Multi-replica compatible
If operators need multiple replicas sharing the same secrets, they set the env var — same as today. The env var always wins and is persisted for consistency. For single-replica setups (which is most Kite deployments), auto-gen just works because they share the same database.
Breaking changes
The Helm chart
values.yamlchangesjwtSecretandencryptKeydefaults from the hardcoded strings to"". This means:--set jwtSecret=...: no change, their override still appliesEnsureSecrets()detects existing data → preserves the old default with a warningThe transition is designed to be seamless. No data loss, no broken sessions beyond what a normal pod restart would cause.