Files
versitygw/cmd/internal/gwcli/iam.go
T
niksis02 1c1272c8a5 feat: add per-provider OIDC discovery URL override
`AssumeRoleWithWebIdentity` always fetched a provider's discovery document from `<provider url>/.well-known/openid-configuration`, so an identity provider that issues tokens naming a public issuer while serving its metadata and keys on a cluster-internal path could not be used: reaching it meant relaxing the endpoint checks for every registered provider. `--oidc-discovery-url` moves that one fetch to an operator-named endpoint, which is how keys can be looked up over an optimized private path while the tokens themselves stay verifiable from the public internet against the issuer alone, as the JWT spec requires.

The flag takes `<provider url>=<discovery url>` pairs, can be repeated once per provider, and is also read from `VGW_IAM_OIDC_DISCOVERY_URLS` as a comma-separated list; the Helm chart exposes the same list as `iamServer.oidc.discoveryUrls`. The discovery URL is fetched exactly as written, so it must carry the `/.well-known/openid-configuration` path when the provider serves it there. A malformed pair is rejected at startup rather than at the first assume-role call.

Only the fetch moves. The provider URL is still what a token's `iss` claim is matched against, the fetched document's own `issuer` field must still equal it, and the key set still comes from the `jwks_uri` that document publishes. A configured discovery endpoint is named by the operator at startup rather than by a request, so it and the `jwks_uri` it publishes waive the private-address check for that provider's fetch chain only, without `--oidc-allow-private-endpoints` and its far broader effect on every other provider. Transport rules are unchanged: a plaintext discovery URL still requires `--oidc-allow-insecure-transport`.

Thumbprint auto-fetch follows the override and pins the discovery endpoint's certificate chain, since that is the host every later fetch is verified against.
2026-09-14 15:12:51 +04:00

160 lines
6.9 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: "oidc-discovery-url",
Usage: "fetch one OIDC provider's discovery document from somewhere other than the provider URL itself, as '<provider url>=<discovery url>' (can be specified multiple times); the discovery URL is fetched exactly as given, so include the '/.well-known/openid-configuration' path, and it may be a private in-cluster address without --oidc-allow-private-endpoints. Tokens are still matched against the provider URL, and keys are still read from the jwks_uri the fetched document publishes",
EnvVars: []string{"VGW_IAM_OIDC_DISCOVERY_URLS"},
},
&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"},
},
},
}
}