mirror of
https://github.com/versity/versitygw.git
synced 2026-01-07 12:15:18 +00:00
auth/vault: add Vault namespace support
New CLI flags: - --iam-vault-namespace - --iam-vault-auth-namespace - --iam-vault-secret-storage-namespace Behavior: - Auth requests use the auth namespace - KV operations use the secret storage namespace - If a specific namespace is not set, the shared namespace is used - With AppRole, different auth and secret namespaces are rejected
This commit is contained in:
79
auth/iam.go
79
auth/iam.go
@@ -107,42 +107,45 @@ var (
|
||||
)
|
||||
|
||||
type Opts struct {
|
||||
RootAccount Account
|
||||
Dir string
|
||||
LDAPServerURL string
|
||||
LDAPBindDN string
|
||||
LDAPPassword string
|
||||
LDAPQueryBase string
|
||||
LDAPObjClasses string
|
||||
LDAPAccessAtr string
|
||||
LDAPSecretAtr string
|
||||
LDAPRoleAtr string
|
||||
LDAPUserIdAtr string
|
||||
LDAPGroupIdAtr string
|
||||
VaultEndpointURL string
|
||||
VaultSecretStoragePath string
|
||||
VaultAuthMethod string
|
||||
VaultMountPath string
|
||||
VaultRootToken string
|
||||
VaultRoleId string
|
||||
VaultRoleSecret string
|
||||
VaultServerCert string
|
||||
VaultClientCert string
|
||||
VaultClientCertKey string
|
||||
S3Access string
|
||||
S3Secret string
|
||||
S3Region string
|
||||
S3Bucket string
|
||||
S3Endpoint string
|
||||
S3DisableSSlVerfiy bool
|
||||
CacheDisable bool
|
||||
CacheTTL int
|
||||
CachePrune int
|
||||
IpaHost string
|
||||
IpaVaultName string
|
||||
IpaUser string
|
||||
IpaPassword string
|
||||
IpaInsecure bool
|
||||
RootAccount Account
|
||||
Dir string
|
||||
LDAPServerURL string
|
||||
LDAPBindDN string
|
||||
LDAPPassword string
|
||||
LDAPQueryBase string
|
||||
LDAPObjClasses string
|
||||
LDAPAccessAtr string
|
||||
LDAPSecretAtr string
|
||||
LDAPRoleAtr string
|
||||
LDAPUserIdAtr string
|
||||
LDAPGroupIdAtr string
|
||||
VaultEndpointURL string
|
||||
VaultNamespace string
|
||||
VaultSecretStoragePath string
|
||||
VaultSecretStorageNamespace string
|
||||
VaultAuthMethod string
|
||||
VaultAuthNamespace string
|
||||
VaultMountPath string
|
||||
VaultRootToken string
|
||||
VaultRoleId string
|
||||
VaultRoleSecret string
|
||||
VaultServerCert string
|
||||
VaultClientCert string
|
||||
VaultClientCertKey string
|
||||
S3Access string
|
||||
S3Secret string
|
||||
S3Region string
|
||||
S3Bucket string
|
||||
S3Endpoint string
|
||||
S3DisableSSlVerfiy bool
|
||||
CacheDisable bool
|
||||
CacheTTL int
|
||||
CachePrune int
|
||||
IpaHost string
|
||||
IpaVaultName string
|
||||
IpaUser string
|
||||
IpaPassword string
|
||||
IpaInsecure bool
|
||||
}
|
||||
|
||||
func New(o *Opts) (IAMService, error) {
|
||||
@@ -164,8 +167,8 @@ func New(o *Opts) (IAMService, error) {
|
||||
fmt.Printf("initializing S3 IAM with '%v/%v'\n",
|
||||
o.S3Endpoint, o.S3Bucket)
|
||||
case o.VaultEndpointURL != "":
|
||||
svc, err = NewVaultIAMService(o.RootAccount, o.VaultEndpointURL, o.VaultSecretStoragePath,
|
||||
o.VaultAuthMethod, o.VaultMountPath, o.VaultRootToken, o.VaultRoleId, o.VaultRoleSecret,
|
||||
svc, err = NewVaultIAMService(o.RootAccount, o.VaultEndpointURL, o.VaultNamespace, o.VaultSecretStoragePath, o.VaultSecretStorageNamespace,
|
||||
o.VaultAuthMethod, o.VaultAuthNamespace, o.VaultMountPath, o.VaultRootToken, o.VaultRoleId, o.VaultRoleSecret,
|
||||
o.VaultServerCert, o.VaultClientCert, o.VaultClientCertKey)
|
||||
fmt.Printf("initializing Vault IAM with %q\n", o.VaultEndpointURL)
|
||||
case o.IpaHost != "":
|
||||
|
||||
@@ -38,15 +38,39 @@ type VaultIAMService struct {
|
||||
creds schema.AppRoleLoginRequest
|
||||
}
|
||||
|
||||
type VaultIAMNamespace struct {
|
||||
Auth string
|
||||
SecretStorage string
|
||||
}
|
||||
|
||||
// Resolve empty specific namespaces to the fallback.
|
||||
// Empty result means root namespace.
|
||||
func resolveVaultNamespaces(authNamespace, secretStorageNamespace, fallback string) VaultIAMNamespace {
|
||||
ns := VaultIAMNamespace{
|
||||
Auth: authNamespace,
|
||||
SecretStorage: secretStorageNamespace,
|
||||
}
|
||||
|
||||
if ns.Auth == "" {
|
||||
ns.Auth = fallback
|
||||
}
|
||||
if ns.SecretStorage == "" {
|
||||
ns.SecretStorage = fallback
|
||||
}
|
||||
|
||||
return ns
|
||||
}
|
||||
|
||||
var _ IAMService = &VaultIAMService{}
|
||||
|
||||
func NewVaultIAMService(rootAcc Account, endpoint, secretStoragePath,
|
||||
authMethod, mountPath, rootToken, roleID, roleSecret, serverCert,
|
||||
func NewVaultIAMService(rootAcc Account, endpoint, namespace, secretStoragePath, secretStorageNamespace,
|
||||
authMethod, authNamespace, mountPath, rootToken, roleID, roleSecret, serverCert,
|
||||
clientCert, clientCertKey string) (IAMService, error) {
|
||||
opts := []vault.ClientOption{
|
||||
vault.WithAddress(endpoint),
|
||||
vault.WithRequestTimeout(requestTimeout),
|
||||
}
|
||||
|
||||
if serverCert != "" {
|
||||
tls := vault.TLSConfiguration{}
|
||||
|
||||
@@ -80,6 +104,28 @@ func NewVaultIAMService(rootAcc Account, endpoint, secretStoragePath,
|
||||
kvReqOpts = append(kvReqOpts, vault.WithMountPath(mountPath))
|
||||
}
|
||||
|
||||
// Resolve namespaces using optional generic fallback "namespace"
|
||||
ns := resolveVaultNamespaces(authNamespace, secretStorageNamespace, namespace)
|
||||
|
||||
// Guard: AppRole tokens are namespace scoped. If using AppRole and namespaces differ, error early.
|
||||
// Root token can span namespaces because each request carries X-Vault-Namespace.
|
||||
if rootToken == "" && ns.Auth != "" && ns.SecretStorage != "" && ns.Auth != ns.SecretStorage {
|
||||
return nil, fmt.Errorf(
|
||||
"approle tokens are namespace scoped. auth namespace %q and secret storage namespace %q differ. "+
|
||||
"use the same namespace or authenticate with a root token",
|
||||
ns.Auth, ns.SecretStorage,
|
||||
)
|
||||
}
|
||||
|
||||
// Apply namespaces to the correct request option sets.
|
||||
// For root token we do not need an auth namespace since we are not logging in via auth.
|
||||
if rootToken == "" && ns.Auth != "" {
|
||||
authReqOpts = append(authReqOpts, vault.WithNamespace(ns.Auth))
|
||||
}
|
||||
if ns.SecretStorage != "" {
|
||||
kvReqOpts = append(kvReqOpts, vault.WithNamespace(ns.SecretStorage))
|
||||
}
|
||||
|
||||
creds := schema.AppRoleLoginRequest{
|
||||
RoleId: roleID,
|
||||
SecretId: roleSecret,
|
||||
@@ -179,6 +225,10 @@ func (vt *VaultIAMService) CreateAccount(account Account) error {
|
||||
if strings.Contains(err.Error(), "check-and-set") {
|
||||
return ErrUserExists
|
||||
}
|
||||
if vault.IsErrorStatus(err, http.StatusForbidden) {
|
||||
return fmt.Errorf("vault 403 permission denied on path %q. check KV mount path and policy. original: %w",
|
||||
vt.secretStoragePath+"/"+account.Access, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user