Repository navigation
Conversation
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
Security-sensitive migration gaps, plaintext credential logging, configuration regressions, and substantial unrelated scope require remediation and human review.
7 open findings
Runtime config incorrectly triggers JWT key clearing · New Bcrypt hashes are incorrectly treated as plaintext · New Bootstrap logs the generated admin password to stderr · New PBKDF2 iteration count lacks minimum and maximum validation · New Trusted proxy setting lacks default and config.yml entry · New Runtime-only JWT fallback invalidates tokens after restart · New Unrelated changes expand the PR beyond a focused PBKDF2 feature · New
What changed in this PR
Migrates standalone authentication to PBKDF2-SHA256 while expanding credential storage, login protection, and configuration behavior.
Changes:
- Adds PBKDF2 password generation and verification.
- Introduces keyring-backed credential bootstrap and cleanup.
- Adds login rate limiting, trusted proxies, tests, and API documentation.
| File | Description |
|---|---|
pkg/secrets/pbkdf2.go |
Implements PBKDF2 hashing. |
pkg/secrets/pbkdf2_test.go |
Tests PBKDF2 behavior. |
internal/controller/ws/v1/redirect_test.go |
Synchronizes WebSocket tests. |
internal/controller/openapi/devices.go |
Documents login rate limiting. |
internal/controller/httpapi/v1/login.go |
Verifies hashes and rate-limits logins. |
internal/controller/httpapi/v1/login_test.go |
Updates authentication fixtures. |
internal/controller/httpapi/v1/login_ratelimit_test.go |
Tests rate limiting. |
internal/app/app.go |
Configures trusted proxies. |
integration-test/collections/console_mps_apis.postman_collection.json |
Adds authorization coverage. |
integration-test/collections/console_environment.postman_environment.json |
Adds admin variables. |
go.sum |
Records dependency checksums. |
go.mod |
Adds terminal support. |
config/config.yml |
Updates authentication defaults. |
config/config.go |
Changes credential and JWT configuration. |
config/config_test.go |
Tests JWT behavior. |
cmd/app/secret_store.go |
Adds credential bootstrap and keyring storage. |
cmd/app/main.go |
Integrates credential handling. |
cmd/app/main_test.go |
Tests credential workflows. |
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
Comment on lines
+187
to
+192
| // Captured before resolution overwrites these fields below: a non-empty | ||
| // value here means config.yml itself holds a plaintext credential that | ||
| // must be cleared after it's persisted elsewhere. Resolving everything | ||
| // from the keyring/env instead must not force a (possibly read-only) | ||
| // config.yml rewrite that has nothing to clean up. | ||
| configFileHadCredentials := cfg.AdminUsername != "" || cfg.AdminPassword != "" || cfg.JWTKey != "" |
Comment on lines
+555
to
+560
| func normalizeAdminPasswordHash(password string) (hash string, converted bool, err error) { | ||
| if secrets.IsPBKDF2Hash(password) { | ||
| return password, false, nil | ||
| } | ||
|
|
||
| pbkdf2Hash, err := secrets.GeneratePBKDF2Hash(password) |
Comment on lines
+581
to
+590
| // Security: Log to stderr (not stdout) to prevent accidental credential exposure in stdout captures. | ||
| // Credentials should only be logged once during initial bootstrap for user visibility. | ||
| fmt.Fprintln(os.Stderr, "") | ||
| fmt.Fprintln(os.Stderr, "\033[31m===============================================\033[0m") | ||
| fmt.Fprintln(os.Stderr, "\033[31mCONSOLE ADMIN CREDENTIALS - FIRST-RUN BOOTSTRAP\033[0m") | ||
| fmt.Fprintln(os.Stderr, "\033[31m===============================================\033[0m") | ||
| fmt.Fprintln(os.Stderr, "\033[31mRecord these values now; they will NOT be shown again.\033[0m") | ||
| fmt.Fprintln(os.Stderr, "") | ||
| fmt.Fprintf(os.Stderr, " Admin Username: %s\n", "standalone") | ||
| fmt.Fprintf(os.Stderr, " Admin Password: %s\n", password) |
Comment on lines
+95
to
+97
| iterations, err := strconv.Atoi(parts[0]) | ||
| if err != nil || iterations <= 0 { | ||
| return parsedPBKDF2Hash{}, false |
Comment on lines
+85
to
+93
| // TrustedProxies lists the reverse proxy/gateway IPs or CIDRs allowed to | ||
| // set X-Forwarded-For/X-Real-IP for c.ClientIP() (used by login rate | ||
| // limiting and audit logs). Empty (the default) trusts none, so | ||
| // ClientIP() always reads the direct connection's RemoteAddr — safe | ||
| // against header spoofing, but means every client behind an untrusted | ||
| // proxy is grouped under that proxy's single address. Deployments that | ||
| // sit behind a real reverse proxy should list it here to get accurate | ||
| // per-client addresses instead. | ||
| TrustedProxies []string `yaml:"trusted_proxies" env:"HTTP_TRUSTED_PROXIES"` |
Comment on lines
+468
to
+478
| key, err := GenerateRuntimeJWTKey() | ||
| if err != nil { | ||
| return err | ||
| return fmt.Errorf("config: generating auth.jwtKey: %w", err) | ||
| } | ||
|
|
||
| return updateConfigFile(configPath, func(c *Config) { c.AdminPassword = adminPassword }) | ||
| cfg.JWTKey = key | ||
|
|
||
| log.Printf( | ||
| "WARNING: no auth.jwtKey was configured, so a runtime-only JWT key was generated for this process. " + | ||
| "For production, provide AUTH_JWT_KEY or auth.jwtKey explicitly, or use OAuth2/OIDC; local basic auth is intended for single-user deployments.", | ||
| ) |
Comment on lines
+119
to
+123
| // The handshake response is already flushed by the time Redirect runs, but | ||
| // httptest.Server.Close() does not wait for goroutines still serving a | ||
| // hijacked (upgraded) connection, so without this the handler goroutine can | ||
| // outlive the test and race the next test's config.NewConfig() write. | ||
| var handlerWG sync.WaitGroup |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1302 +/- ##
==========================================
+ Coverage 62.32% 62.43% +0.11%
==========================================
Files 159 161 +2
Lines 13435 13669 +234
==========================================
+ Hits 8373 8534 +161
- Misses 5062 5135 +73 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
nbmaiti
force-pushed
the
feat/jwt_sec_auth_keystore
branch
3 times, most recently
from
October 28, 2026 17:32
c88691c to
287303b
Compare
…tion Support AUTH_SECRET_BACKEND=config|keyring while keeping config as the default throughout v1.x. Give OAuth2/OIDC precedence when AUTH_CLIENT_ID is configured and ignore standalone credential sources and backend settings. Use only keyring credentials when keyring mode is selected, without reading, migrating, or modifying standalone credentials in configuration or environment values. Do not fall back between backends. Fixes #1353 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
nbmaiti
force-pushed
the
feat/jwt_sec_auth_keystore
branch
from
October 28, 2026 17:55
287303b to
93795fd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Add the configurable standalone authentication secret backend required by issue #1353.
Fixes #1353
Validation