Skip to content

Commit

Permalink
vault: support authentication in different namespaces (#431)
Browse files Browse the repository at this point in the history
  • Loading branch information
aead authored Jan 11, 2024
1 parent 9c7a5dc commit eb1faa7
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 34 deletions.
20 changes: 18 additions & 2 deletions internal/keystore/vault/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,15 @@ func (c *client) CheckStatus(ctx context.Context, delay time.Duration) {
// To renew the auth. token see: client.RenewToken(...).
func (c *client) AuthenticateWithAppRole(login *AppRole) authFunc {
return func() (*vaultapi.Secret, error) {
secret, err := c.Logical().Write(path.Join("auth", login.Engine, "login"), map[string]interface{}{
client := c.Client
switch {
case login.Namespace == "/": // Treat '/' as the root namespace
client = client.WithNamespace("") // Clear namespace
case login.Namespace != "":
client = client.WithNamespace(login.Namespace)
}

secret, err := client.Logical().Write(path.Join("auth", login.Engine, "login"), map[string]interface{}{
"role_id": login.ID,
"secret_id": login.Secret,
})
Expand All @@ -88,7 +96,15 @@ func (c *client) AuthenticateWithAppRole(login *AppRole) authFunc {

func (c *client) AuthenticateWithK8S(login *Kubernetes) authFunc {
return func() (*vaultapi.Secret, error) {
secret, err := c.Logical().Write(path.Join("auth", login.Engine, "login"), map[string]interface{}{
client := c.Client
switch {
case login.Namespace == "/": // Treat '/' as the root namespace
client = client.WithNamespace("") // Clear namespace
case login.Namespace != "":
client = client.WithNamespace(login.Namespace)
}

secret, err := client.Logical().Write(path.Join("auth", login.Engine, "login"), map[string]interface{}{
"role": login.Role,
"jwt": login.JWT,
})
Expand Down
40 changes: 34 additions & 6 deletions internal/keystore/vault/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@ type AppRole struct {
// mounted at arbitrary paths.
Engine string

// Namespace is the Vault namespace in which the AppRole
// authentication is performed. It can be used to authenticate
// in a different namespace compared to the secret engine
// namespace. For example, authenticate within the root
// namespace but use a team-specific namespace for the secret
// engine.
//
// If empty, the VaultKeyStore namespace is used, if set.
// A single "/" is treated as alias for the Vault root
// namespace such that no namespace header is sent as part
// of the request.
Namespace string

// ID is the AppRole authentication ID
ID string

Expand All @@ -62,9 +75,10 @@ func (a *AppRole) Clone() *AppRole {
return nil
}
return &AppRole{
Engine: a.Engine,
ID: a.ID,
Secret: a.Secret,
Engine: a.Engine,
Namespace: a.Namespace,
ID: a.ID,
Secret: a.Secret,
}
}

Expand All @@ -81,6 +95,19 @@ type Kubernetes struct {
// mounted at arbitrary paths.
Engine string

// Namespace is the Vault namespace in which the Kubernetes
// authentication is performed. It can be used to authenticate
// in a different namespace compared to the secret engine
// namespace. For example, authenticate within the root
// namespace but use a team-specific namespace for the secret
// engine.
//
// If empty, the VaultKeyStore namespace is used, if set.
// A single "/" is treated as alias for the Vault root
// namespace such that no namespace header is sent as part
// of the request.
Namespace string

// Role is the JWT role.
Role string

Expand All @@ -94,9 +121,10 @@ func (k *Kubernetes) Clone() *Kubernetes {
return nil
}
return &Kubernetes{
Engine: k.Engine,
Role: k.Role,
JWT: k.JWT,
Engine: k.Engine,
Namespace: k.Namespace,
Role: k.Role,
JWT: k.JWT,
}
}

Expand Down
28 changes: 16 additions & 12 deletions kesconf/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,17 @@ type ymlFile struct {
}

AppRole *struct {
Engine env[string] `yaml:"engine"`
ID env[string] `yaml:"id"`
Secret env[string] `yaml:"secret"`
Engine env[string] `yaml:"engine"`
Namespace env[string] `yaml:"namespace"`
ID env[string] `yaml:"id"`
Secret env[string] `yaml:"secret"`
} `yaml:"approle"`

Kubernetes *struct {
Engine env[string] `yaml:"engine"`
Role env[string] `yaml:"role"`
JWT env[string] `yaml:"jwt"` // Can be either a JWT or a path to a file containing a JWT
Engine env[string] `yaml:"engine"`
Namespace env[string] `yaml:"namespace"`
Role env[string] `yaml:"role"`
JWT env[string] `yaml:"jwt"` // Can be either a JWT or a path to a file containing a JWT
} `yaml:"kubernetes"`

TLS struct {
Expand Down Expand Up @@ -463,16 +465,18 @@ func ymlToKeyStore(y *ymlFile) (KeyStore, error) {
}
if y.KeyStore.Vault.AppRole != nil {
s.AppRole = &VaultAppRoleAuth{
Engine: y.KeyStore.Vault.AppRole.Engine.Value,
ID: y.KeyStore.Vault.AppRole.ID.Value,
Secret: y.KeyStore.Vault.AppRole.Secret.Value,
Engine: y.KeyStore.Vault.AppRole.Engine.Value,
Namespace: y.KeyStore.Vault.AppRole.Namespace.Value,
ID: y.KeyStore.Vault.AppRole.ID.Value,
Secret: y.KeyStore.Vault.AppRole.Secret.Value,
}
}
if y.KeyStore.Vault.Kubernetes != nil {
s.Kubernetes = &VaultKubernetesAuth{
Engine: y.KeyStore.Vault.Kubernetes.Engine.Value,
JWT: y.KeyStore.Vault.Kubernetes.JWT.Value,
Role: y.KeyStore.Vault.Kubernetes.Role.Value,
Engine: y.KeyStore.Vault.Kubernetes.Engine.Value,
Namespace: y.KeyStore.Vault.Kubernetes.Namespace.Value,
JWT: y.KeyStore.Vault.Kubernetes.JWT.Value,
Role: y.KeyStore.Vault.Kubernetes.Role.Value,
}
}
if y.KeyStore.Vault.Transit != nil {
Expand Down
40 changes: 34 additions & 6 deletions kesconf/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,19 @@ type VaultAppRoleAuth struct {
// If empty, defaults to "approle".
Engine string

// Namespace is the Vault namespace in which the AppRole
// authentication is performed. It can be used to authenticate
// in a different namespace compared to the secret engine
// namespace. For example, authenticate within the root
// namespace but use a team-specific namespace for the secret
// engine.
//
// If empty, the VaultKeyStore namespace is used, if set.
// A single "/" is treated as alias for the Vault root
// namespace such that no namespace header is sent as part
// of the request.
Namespace string

// AppRoleID is the AppRole access ID for authenticating
// to Hashicorp Vault via the AppRole method.
ID string
Expand All @@ -477,6 +490,19 @@ type VaultKubernetesAuth struct {
// If empty, defaults to "kubernetes".
Engine string

// Namespace is the Vault namespace in which the Kubernetes
// authentication is performed. It can be used to authenticate
// in a different namespace compared to the secret engine
// namespace. For example, authenticate within the root
// namespace but use a team-specific namespace for the secret
// engine.
//
// If empty, the VaultKeyStore namespace is used, if set.
// A single "/" is treated as alias for the Vault root
// namespace such that no namespace header is sent as part
// of the request.
Namespace string

// KubernetesRole is the login role for authenticating via the
// kubernetes authentication method.
Role string
Expand Down Expand Up @@ -519,16 +545,18 @@ func (s *VaultKeyStore) Connect(ctx context.Context) (kes.KeyStore, error) {
}
if s.AppRole != nil {
c.AppRole = &vault.AppRole{
Engine: s.AppRole.Engine,
ID: s.AppRole.ID,
Secret: s.AppRole.Secret,
Engine: s.AppRole.Engine,
Namespace: s.AppRole.Namespace,
ID: s.AppRole.ID,
Secret: s.AppRole.Secret,
}
}
if s.Kubernetes != nil {
c.K8S = &vault.Kubernetes{
Engine: s.Kubernetes.Engine,
Role: s.Kubernetes.Role,
JWT: s.Kubernetes.JWT,
Engine: s.Kubernetes.Engine,
Namespace: s.Kubernetes.Namespace,
Role: s.Kubernetes.Role,
JWT: s.Kubernetes.JWT,
}
}
if s.Transit != nil {
Expand Down
16 changes: 8 additions & 8 deletions server-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -249,15 +249,15 @@ keystore:
engine: "" # The path of the transit engine - e.g. "my-transit". If empty, defaults to: transit (Vault default)
key: "" # The key name that should be used to encrypt entries stored on the K/V engine.
approle: # AppRole credentials. See: https://www.vaultproject.io/docs/auth/approle.html
engine: "" # The path of the AppRole engine - e.g. authenticate. If empty, defaults to: approle. (Vault default)
id: "" # Your AppRole Role ID
secret: "" # Your AppRole Secret ID
retry: 15s # Duration until the server tries to re-authenticate after connection loss.
namespace: "" # Optional Vault namespace used just for authentication. A single "/" is an alias for the Vault root namespace.
engine: "" # The path of the AppRole engine - e.g. authenticate. If empty, defaults to: approle. (Vault default)
id: "" # Your AppRole Role ID
secret: "" # Your AppRole Secret ID
kubernetes: # Kubernetes credentials. See: https://www.vaultproject.io/docs/auth/kubernetes
engine: "" # The path of the Kubernetes engine e.g. authenticate. If empty, defaults to: kubernetes. (Vault default)
role: "" # The Kubernetes JWT role
jwt: "" # Either the JWT provided by K8S or a path to a K8S secret containing the JWT.
retry: 15s # Duration until the server tries to re-authenticate after connection loss.
namespace: "" # Optional Vault namespace used just for authentication. A single "/" is an alias for the Vault root namespace.
engine: "" # The path of the Kubernetes engine e.g. authenticate. If empty, defaults to: kubernetes. (Vault default)
role: "" # The Kubernetes JWT role
jwt: "" # Either the JWT provided by K8S or a path to a K8S secret containing the JWT.
tls: # The Vault client TLS configuration for mTLS authentication and certificate verification
key: "" # Path to the TLS client private key for mTLS authentication to Vault
cert: "" # Path to the TLS client certificate for mTLS authentication to Vault
Expand Down

0 comments on commit eb1faa7

Please sign in to comment.