mirror of
https://github.com/pgsty/minio.git
synced 2026-09-07 11:06:10 +03:00
fix: keep embedded Console login working over loopback TLS (#108)
The embedded Console reaches the S3/STS API at https://127.0.0.1:<port> (minioConfigToConsoleFeatures), a loopback endpoint whose TLS certificate is not expected to carry a 127.0.0.1 SAN. silo-console v2.3.x began verifying every outbound TLS peer, so the Console's STS AssumeRole handshake to that loopback endpoint now fails certificate validation and BOTH local and LDAP logins fail with a generic "invalid login". The failure happens in the Console HTTP client before any request reaches a server auth/STS/LDAP handler, so no server-side auth error is logged, matching the report. Restore the documented loopback bypass by opting the embedded Console into its endpoint-scoped CONSOLE_MINIO_SERVER_TLS_SKIP_VERIFY switch whenever the server falls back to the 127.0.0.1 endpoint under TLS. The exemption is scoped to that single loopback origin inside Console; every other HTTPS peer (IdP, Prometheus, webhooks) stays verified, preserving the v2.3.x hardening. An explicitly configured endpoint is reached under its own verified name and is never exempted. initConsoleServer unsets CONSOLE_* before re-deriving them, so the switch cannot be supplied by the operator on the embedded path; the server must assert it. Fixes #108 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L7qJqWwy8oFA6aCXWRzXQe Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
+30
-5
@@ -120,15 +120,40 @@ func init() {
|
||||
|
||||
const consolePrefix = "CONSOLE_"
|
||||
|
||||
// consoleMinIOServerEnv derives the CONSOLE_MINIO_SERVER value the embedded
|
||||
// Console uses to reach the S3/STS API, and whether TLS verification of that
|
||||
// endpoint must be skipped. With no explicit endpoint configured the Console
|
||||
// reaches the API over the loopback address, whose TLS certificate is not
|
||||
// expected to carry a 127.0.0.1 SAN; because Console verifies outbound TLS by
|
||||
// default, the loopback origin has to be exempted or embedded login (local and
|
||||
// LDAP alike) fails at the STS handshake. The exemption is endpoint-scoped in
|
||||
// Console, so every other HTTPS peer stays verified. An explicitly configured
|
||||
// endpoint is always reached under its own verified name and is never exempted.
|
||||
func consoleMinIOServerEnv(endpoint string, isTLS bool, port string) (server string, skipVerify bool) {
|
||||
if endpoint != "" {
|
||||
return endpoint, false
|
||||
}
|
||||
return fmt.Sprintf("%s://127.0.0.1:%s", getURLScheme(isTLS), port), isTLS
|
||||
}
|
||||
|
||||
func minioConfigToConsoleFeatures() {
|
||||
os.Setenv("CONSOLE_PBKDF_SALT", globalDeploymentID())
|
||||
os.Setenv("CONSOLE_PBKDF_PASSPHRASE", globalDeploymentID())
|
||||
if globalMinioEndpoint != "" {
|
||||
os.Setenv("CONSOLE_MINIO_SERVER", globalMinioEndpoint)
|
||||
consoleServer, skipVerify := consoleMinIOServerEnv(globalMinioEndpoint, globalIsTLS, globalMinioPort)
|
||||
os.Setenv("CONSOLE_MINIO_SERVER", consoleServer)
|
||||
if skipVerify {
|
||||
// The embedded Console reaches the loopback S3/STS endpoint above, whose
|
||||
// certificate is not expected to carry a 127.0.0.1 SAN. Console verifies
|
||||
// outbound TLS by default (silo-console v2.3.x), so opt into the
|
||||
// endpoint-scoped compatibility switch to preserve the documented loopback
|
||||
// bypass; every other HTTPS peer (IdP, Prometheus, webhooks, ...) stays
|
||||
// verified. initConsoleServer unsets CONSOLE_* before calling this, so the
|
||||
// switch cannot be supplied by the operator on the embedded path.
|
||||
os.Setenv("CONSOLE_MINIO_SERVER_TLS_SKIP_VERIFY", "on")
|
||||
} else {
|
||||
// Explicitly set 127.0.0.1 so Console will automatically bypass TLS verification to the local S3 API.
|
||||
// This will save users from providing a certificate with IP or FQDN SAN that points to the local host.
|
||||
os.Setenv("CONSOLE_MINIO_SERVER", fmt.Sprintf("%s://127.0.0.1:%s", getURLScheme(globalIsTLS), globalMinioPort))
|
||||
// An explicitly configured endpoint is reached under its own verified name;
|
||||
// never let a loopback exemption apply to it.
|
||||
os.Unsetenv("CONSOLE_MINIO_SERVER_TLS_SKIP_VERIFY")
|
||||
}
|
||||
if value := env.Get(config.EnvMinIOLogQueryURL, ""); value != "" {
|
||||
os.Setenv("CONSOLE_LOG_QUERY_URL", value)
|
||||
|
||||
@@ -372,3 +372,56 @@ func TestConfigEnvFileNamedTargetDiscovery(t *testing.T) {
|
||||
t.Fatalf("named target %q not discovered from %s: %v", "my-hook", key, targets)
|
||||
}
|
||||
}
|
||||
|
||||
// TestConsoleMinIOServerEnv locks in the loopback TLS exemption that keeps
|
||||
// embedded Console login working (issue #108) while ensuring an explicitly
|
||||
// configured endpoint is never silently exempted from TLS verification.
|
||||
func TestConsoleMinIOServerEnv(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
endpoint string
|
||||
isTLS bool
|
||||
port string
|
||||
wantServer string
|
||||
wantSkipVerify bool
|
||||
}{
|
||||
{
|
||||
name: "loopback TLS is exempted so embedded login works",
|
||||
isTLS: true,
|
||||
port: "9000",
|
||||
wantServer: "https://127.0.0.1:9000",
|
||||
wantSkipVerify: true,
|
||||
},
|
||||
{
|
||||
name: "loopback plain HTTP needs no exemption",
|
||||
isTLS: false,
|
||||
port: "9000",
|
||||
wantServer: "http://127.0.0.1:9000",
|
||||
},
|
||||
{
|
||||
name: "explicit https endpoint stays verified",
|
||||
endpoint: "https://silo.example:9000",
|
||||
isTLS: true,
|
||||
port: "9000",
|
||||
wantServer: "https://silo.example:9000",
|
||||
},
|
||||
{
|
||||
name: "explicit http endpoint stays verified",
|
||||
endpoint: "http://silo.example:9000",
|
||||
isTLS: false,
|
||||
port: "9000",
|
||||
wantServer: "http://silo.example:9000",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
server, skipVerify := consoleMinIOServerEnv(tt.endpoint, tt.isTLS, tt.port)
|
||||
if server != tt.wantServer {
|
||||
t.Fatalf("server = %q, want %q", server, tt.wantServer)
|
||||
}
|
||||
if skipVerify != tt.wantSkipVerify {
|
||||
t.Fatalf("skipVerify = %v, want %v", skipVerify, tt.wantSkipVerify)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user