Files
versitygw/cmd/internal/gwcli/iam.go
T
niksis02 658c37907d feat: add OIDC endpoint relaxations for private/isolated networks
Closes #2364

`AssumeRoleWithWebIdentity` only ever trusted an `OIDC` provider reachable over verified `https`, at a publicly routable address, on the implicit `:443`. That posture is right for an internet-facing IdP but rejects every address an internal one can have, so a `SPIFFE/SPIRE` OIDC discovery provider in the same cluster — or as a sidecar in the same pod — could never be registered, let alone verified against, and no setting could express "this private address is the IdP".

Two opt-in flags on `versitygw iam`, both off by default:

`--oidc-allow-private-endpoints`
Permit a provider `Url` resolving to a loopback/private/link-local address, and an explicit port. Transport is unchanged: still `https`, still fully verified (a self-signed in-cluster cert is trusted the way AWS documents, through `ThumbprintList`).

`--oidc-allow-insecure-transport`
Additionally permit plaintext `http` provider URLs, discovery/JWKS endpoints and redirects, and drop TLS verification (`thumbprint` pinning included) for `https` ones.

Both apply uniformly to the thumbprint auto-fetch at `CreateOpenIDConnectProvider` time and to the discovery-document plus `JWKS` fetch at `AssumeRoleWithWebIdentity` time. Neither weakens anything past the endpoint: signature verification, issuer matching, audience and trust policy evaluation are untouched, and the DNS-resolve-once/dial-the-resolved-IP shape stays in place so a rebind still cannot redirect a connection.

An `http` provider keeps its scheme in its stored `Url`, `ARN` and `iss` matching, rather than being stripped like an `https` one — otherwise `"http://host"` and `"https://host"` would collapse onto a single ARN and storage key and each could satisfy the other's trust policy. It also stores an empty `ThumbprintList` rather than failing: a plaintext provider presents no certificate to thumbprint.

Helm: `iamServer.oidc.{allowPrivateEndpoints,allowInsecureTransport}`, alongside `disableThumbprintAutofetch` moved into the same block (the flat `iamServer.disableOidcThumbprintAutofetch` stays honored). Chart `0.4.1 -> 0.4.2`.

The WebUI's create-provider form no longer rejects `http` URLs and ports client-side; it cannot see the service's settings, so those two rules are left to the server, whose error surfaces as a toast like any other.
2026-09-08 16:30:37 +04:00

155 lines
6.3 KiB
Go

// Copyright 2026 Versity Software
// This file is licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package gwcli
import (
"github.com/urfave/cli/v2"
)
// RunIAM starts the standalone IAM API server for the given command
// context. The hosting binary's main package must set this before running
// the "iam" command.
var RunIAM func(ctx *cli.Context) error
// IAMCommand returns the "iam" subcommand, common to all versitygw binaries.
func IAMCommand() *cli.Command {
return &cli.Command{
Name: "iam",
Usage: "IAM API server",
Description: "Run the standalone IAM API server.",
Action: func(ctx *cli.Context) error {
return RunIAM(ctx)
},
Flags: []cli.Flag{
&cli.StringFlag{
Name: "dir",
Usage: "directory path for file-backed IAM storage",
EnvVars: []string{"VGW_IAM_DIR"},
},
&cli.StringFlag{
Name: "vault-endpoint-url",
Usage: "vault server url for IAM storage",
EnvVars: []string{"VGW_IAM_VAULT_ENDPOINT_URL"},
},
&cli.StringFlag{
Name: "vault-namespace",
Usage: "fallback vault namespace for IAM storage (overridden by vault-auth-namespace / vault-secret-storage-namespace)",
EnvVars: []string{"VGW_IAM_VAULT_NAMESPACE"},
},
&cli.StringFlag{
Name: "vault-secret-storage-path",
Usage: "vault KV v2 path prefix for IAM user storage (default: iam)",
EnvVars: []string{"VGW_IAM_VAULT_SECRET_STORAGE_PATH"},
},
&cli.StringFlag{
Name: "vault-secret-storage-namespace",
Usage: "vault namespace for KV v2 IAM storage (overrides vault-namespace)",
EnvVars: []string{"VGW_IAM_VAULT_SECRET_STORAGE_NAMESPACE"},
},
&cli.StringFlag{
Name: "vault-auth-method",
Usage: "vault auth method mount path (default: approle)",
EnvVars: []string{"VGW_IAM_VAULT_AUTH_METHOD"},
},
&cli.StringFlag{
Name: "vault-auth-namespace",
Usage: "vault namespace for AppRole login (overrides vault-namespace)",
EnvVars: []string{"VGW_IAM_VAULT_AUTH_NAMESPACE"},
},
&cli.StringFlag{
Name: "vault-mount-path",
Usage: "vault KV v2 engine mount path (default: kv-v2)",
EnvVars: []string{"VGW_IAM_VAULT_MOUNT_PATH"},
},
&cli.StringFlag{
Name: "vault-root-token",
Usage: "vault root token for authentication (mutually exclusive with vault-role-id/vault-role-secret)",
EnvVars: []string{"VGW_IAM_VAULT_ROOT_TOKEN"},
},
&cli.StringFlag{
Name: "vault-role-id",
Usage: "vault AppRole role ID for authentication",
EnvVars: []string{"VGW_IAM_VAULT_ROLE_ID"},
},
&cli.StringFlag{
Name: "vault-role-secret",
Usage: "vault AppRole secret ID for authentication",
EnvVars: []string{"VGW_IAM_VAULT_ROLE_SECRET"},
},
&cli.StringFlag{
Name: "vault-server-cert",
Usage: "PEM-encoded vault server TLS certificate for verification",
EnvVars: []string{"VGW_IAM_VAULT_SERVER_CERT"},
},
&cli.StringFlag{
Name: "vault-client-cert",
Usage: "PEM-encoded client TLS certificate presented to vault",
EnvVars: []string{"VGW_IAM_VAULT_CLIENT_CERT"},
},
&cli.StringFlag{
Name: "vault-client-cert-key",
Usage: "PEM-encoded private key for vault-client-cert",
EnvVars: []string{"VGW_IAM_VAULT_CLIENT_CERT_KEY"},
},
&cli.BoolFlag{
Name: "quiet",
Usage: "silence stdout request logging output",
EnvVars: []string{"VGW_QUIET"},
Aliases: []string{"q"},
},
&cli.BoolFlag{
Name: "disable-oidc-thumbprint-autofetch",
Usage: "reject CreateOpenIDConnectProvider requests that omit ThumbprintList instead of auto-fetching it over an outbound TLS connection",
EnvVars: []string{"VGW_IAM_DISABLE_OIDC_THUMBPRINT_AUTOFETCH"},
},
&cli.BoolFlag{
Name: "oidc-allow-private-endpoints",
Usage: "allow OIDC provider URLs that resolve to loopback/private/link-local addresses and that carry an explicit port; needed for an identity provider that only exists on an internal network, and also re-permits cloud metadata endpoints as fetch targets",
EnvVars: []string{"VGW_IAM_OIDC_ALLOW_PRIVATE_ENDPOINTS"},
},
&cli.BoolFlag{
Name: "oidc-allow-insecure-transport",
Usage: "allow plaintext http OIDC provider URLs and skip TLS certificate verification (thumbprint pinning included) for https ones; only for an identity provider reached over an already-trusted path, such as a loopback-bound sidecar",
EnvVars: []string{"VGW_IAM_OIDC_ALLOW_INSECURE_TRANSPORT"},
},
&cli.StringSliceFlag{
Name: "private-ports",
Usage: "private endpoint listen address: a unix socket path, or <ip>:<port>/:<port> when mTLS (--private-cert/--private-cert-key/--private-client-ca) is also configured — refuses to start otherwise (can be specified multiple times)",
EnvVars: []string{"VGW_IAM_PRIVATE_PORTS"},
},
&cli.StringFlag{
Name: "private-cert",
Usage: "TLS server certificate for the private endpoint listener (required for a non-unix-socket --private-ports address)",
EnvVars: []string{"VGW_IAM_PRIVATE_CERT"},
},
&cli.StringFlag{
Name: "private-cert-key",
Usage: "TLS private key for --private-cert",
EnvVars: []string{"VGW_IAM_PRIVATE_CERT_KEY"},
},
&cli.StringFlag{
Name: "private-client-ca",
Usage: "PEM-encoded CA bundle used to verify the S3 gateway's client certificate on the private endpoint listener (required for a non-unix-socket --private-ports address, together with --private-cert/--private-cert-key)",
EnvVars: []string{"VGW_IAM_PRIVATE_CLIENT_CA"},
},
&cli.StringFlag{
Name: "private-socket-perm",
Usage: "octal file-mode permission for a file-backed unix-socket --private-ports address (e.g. '0660'); no effect on TCP or abstract-namespace sockets",
EnvVars: []string{"VGW_IAM_PRIVATE_SOCKET_PERM"},
},
},
}
}