Skip to content

fix(security): 💀 VERY CRITICAL VULNERABILITY 💀 - auto-generate JWT and encryption secrets on first boot#444

Open
DioCrafts wants to merge 1 commit intokite-org:mainfrom
DioCrafts:fix/secret-auto-generation
Open

fix(security): 💀 VERY CRITICAL VULNERABILITY 💀 - auto-generate JWT and encryption secrets on first boot#444
DioCrafts wants to merge 1 commit intokite-org:mainfrom
DioCrafts:fix/secret-auto-generation

Conversation

@DioCrafts
Copy link
Contributor

Fix: Auto-generate JWT and encryption secrets — no more hardcoded keys

TL;DR

Right now, if you helm install kite or docker run without explicitly setting JWT_SECRET and KITE_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.go that have probably been there since day one:

JwtSecret       = "kite-default-jwt-secret-key-change-in-production"
KiteEncryptKey  = "kite-default-encryption-key-change-in-production"

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.go to 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

KiteEncryptKey is hashed with SHA-256 and used as the AES-GCM key in pkg/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_secrets

A minimal key-value table with just two rows (jwt_secret and encrypt_key). Values are stored as plain text — not SecretString — because encrypting the encryption key itself would be circular.

Priority chain for each secret

Every time Kite starts, EnsureSecrets() runs this logic (after InitDB(), before anything reads SecretString from the DB):

1. Environment variable set?  →  use it, persist to DB for consistency
2. Value in system_secrets?   →  use it (survives restarts, pod reschedules)
3. Neither?                   →  generate 32 random bytes, base64url encode, persist

This means:

  • New docker run / go run .: generates unique secrets, stores them in SQLite. Next restart reads them back. Zero-config, zero risk.
  • New helm install: same thing. The chart now defaults jwtSecret and encryptKey to empty strings, so no env var is injected, and the app auto-generates.
  • Existing deploy with env vars set: nothing changes. The env var always wins. It's also persisted to DB so if someone later removes the env var, the value is still there.
  • Existing deploy running on defaults (the scary case): the app detects that encrypted data already exists in the DB (clusters, OAuth providers, API keys). Instead of generating a new key that would make existing data unreadable, it persists the hardcoded default and emits a loud warning box in the logs telling the operator to set a real key.

The upgrade path in practice

Operator running Kite with default keys, upgrades to this version:

W0321 10:15:03.123456  ════════════════════════════════════════════════════════════
W0321 10:15:03.123456    KITE_ENCRYPT_KEY is using the insecure hardcoded default.
W0321 10:15:03.123456    Existing encrypted data has been preserved.
W0321 10:15:03.123456    Please set KITE_ENCRYPT_KEY to a secure random value
W0321 10:15:03.123456    and re-encrypt your data.
W0321 10:15:03.123456  ════════════════════════════════════════════════════════════

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

File Change
pkg/model/system_secret.go New. SystemSecret model, EnsureSecrets(), auto-gen logic with upgrade detection
pkg/model/model.go Added SystemSecret{} to the AutoMigrate list
main.go Inserted model.EnsureSecrets() between InitDB() and GetGeneralSetting()
pkg/common/common.go Removed the old "KITE_ENCRYPT_KEY not set" warning (superseded by EnsureSecrets)
charts/kite/values.yaml jwtSecret and encryptKey now default to "" with updated comments
charts/kite/templates/secret.yaml Conditional: only injects JWT_SECRET/KITE_ENCRYPT_KEY when values are non-empty
docs/config/chart-values.md Updated table with new defaults and auto-gen behavior
docs/zh/config/chart-values.md Same, in Chinese

Why 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 with openssl 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 SELECT per 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.yaml changes jwtSecret and encryptKey defaults from the hardcoded strings to "". This means:

  • New installs: auto-generated (secure by default)
  • Existing installs with --set jwtSecret=...: no change, their override still applies
  • Existing installs without overrides: were using the hardcoded default via the chart → now the env var won't be set → EnsureSecrets() detects existing data → preserves the old default with a warning

The transition is designed to be seamless. No data loss, no broken sessions beyond what a normal pod restart would cause.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

@DioCrafts DioCrafts force-pushed the fix/secret-auto-generation branch from cab294a to c794158 Compare March 21, 2026 22:38
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

@DioCrafts DioCrafts force-pushed the fix/secret-auto-generation branch from c794158 to 3a9dbb2 Compare March 21, 2026 22:45
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

@DioCrafts DioCrafts force-pushed the fix/secret-auto-generation branch from 3a9dbb2 to fa73444 Compare March 21, 2026 22:57
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

@DioCrafts DioCrafts force-pushed the fix/secret-auto-generation branch from fa73444 to cce75c0 Compare March 21, 2026 23:09
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

@DioCrafts DioCrafts force-pushed the fix/secret-auto-generation branch from cce75c0 to aee4ae1 Compare March 21, 2026 23:17
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

@DioCrafts DioCrafts force-pushed the fix/secret-auto-generation branch from aee4ae1 to 3057654 Compare March 21, 2026 23:25
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

@DioCrafts DioCrafts force-pushed the fix/secret-auto-generation branch from 3057654 to 6a3d048 Compare March 22, 2026 07:00
@zxh326
Copy link
Collaborator

zxh326 commented Mar 22, 2026

plz remove unnecessary comments to keep the code clean.

@DioCrafts
Copy link
Contributor Author

plz remove unnecessary comments to keep the code clean.

Sure @zxh326 I will do it as soon as possible. Thank you !

@DioCrafts DioCrafts force-pushed the fix/secret-auto-generation branch from 6a3d048 to e436507 Compare March 22, 2026 08:37
@DioCrafts
Copy link
Contributor Author

@zxh326 Unnecesary comments removed, thank you!

Comment on lines +16 to +19
type SystemSecret struct {
Name string `gorm:"primaryKey;column:name;type:varchar(64)"`
Value string `gorm:"column:value;type:text;not null"`
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The format is very strange.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).
@DioCrafts DioCrafts force-pushed the fix/secret-auto-generation branch from e436507 to fac6cfb Compare March 22, 2026 09:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants