266 lines
6.7 KiB
Go
266 lines
6.7 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package config
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/hashicorp/nomad/helper/pointer"
|
|
vault "github.com/hashicorp/vault/api"
|
|
)
|
|
|
|
const (
|
|
// DefaultVaultConnectRetryIntv is the retry interval between trying to
|
|
// connect to Vault
|
|
DefaultVaultConnectRetryIntv = 30 * time.Second
|
|
)
|
|
|
|
// VaultConfig contains the configuration information necessary to
|
|
// communicate with Vault in order to:
|
|
//
|
|
// - Renew Vault tokens/leases.
|
|
//
|
|
// - Pass a token for the Nomad Server to derive sub-tokens.
|
|
//
|
|
// - Create child tokens with policy subsets of the Server's token.
|
|
type VaultConfig struct {
|
|
|
|
// Enabled enables or disables Vault support.
|
|
Enabled *bool `hcl:"enabled"`
|
|
|
|
// Token is the Vault token given to Nomad such that it can
|
|
// derive child tokens. Nomad will renew this token at half its lease
|
|
// lifetime.
|
|
Token string `hcl:"token"`
|
|
|
|
// Role sets the role in which to create tokens from. The Token given to
|
|
// Nomad does not have to be created from this role but must have "update"
|
|
// capability on "auth/token/create/<create_from_role>". If this value is
|
|
// unset and the token is created from a role, the value is defaulted to the
|
|
// role the token is from.
|
|
Role string `hcl:"create_from_role"`
|
|
|
|
// Namespace sets the Vault namespace used for all calls against the
|
|
// Vault API. If this is unset, then Nomad does not use Vault namespaces.
|
|
Namespace string `mapstructure:"namespace"`
|
|
|
|
// AllowUnauthenticated allows users to submit jobs requiring Vault tokens
|
|
// without providing a Vault token proving they have access to these
|
|
// policies.
|
|
AllowUnauthenticated *bool `hcl:"allow_unauthenticated"`
|
|
|
|
// TaskTokenTTL is the TTL of the tokens created by Nomad Servers and used
|
|
// by the client. There should be a minimum time value such that the client
|
|
// does not have to renew with Vault at a very high frequency
|
|
TaskTokenTTL string `hcl:"task_token_ttl"`
|
|
|
|
// Addr is the address of the local Vault agent. This should be a complete
|
|
// URL such as "http://vault.example.com"
|
|
Addr string `hcl:"address"`
|
|
|
|
// ConnectionRetryIntv is the interval to wait before re-attempting to
|
|
// connect to Vault.
|
|
ConnectionRetryIntv time.Duration
|
|
|
|
// TLSCaFile is the path to a PEM-encoded CA cert file to use to verify the
|
|
// Vault server SSL certificate.
|
|
TLSCaFile string `hcl:"ca_file"`
|
|
|
|
// TLSCaFile is the path to a directory of PEM-encoded CA cert files to
|
|
// verify the Vault server SSL certificate.
|
|
TLSCaPath string `hcl:"ca_path"`
|
|
|
|
// TLSCertFile is the path to the certificate for Vault communication
|
|
TLSCertFile string `hcl:"cert_file"`
|
|
|
|
// TLSKeyFile is the path to the private key for Vault communication
|
|
TLSKeyFile string `hcl:"key_file"`
|
|
|
|
// TLSSkipVerify enables or disables SSL verification
|
|
TLSSkipVerify *bool `hcl:"tls_skip_verify"`
|
|
|
|
// TLSServerName, if set, is used to set the SNI host when connecting via TLS.
|
|
TLSServerName string `hcl:"tls_server_name"`
|
|
}
|
|
|
|
// DefaultVaultConfig returns the canonical defaults for the Nomad
|
|
// `vault` configuration.
|
|
func DefaultVaultConfig() *VaultConfig {
|
|
return &VaultConfig{
|
|
Addr: "https://vault.service.consul:8200",
|
|
ConnectionRetryIntv: DefaultVaultConnectRetryIntv,
|
|
AllowUnauthenticated: pointer.Of(true),
|
|
}
|
|
}
|
|
|
|
// IsEnabled returns whether the config enables Vault integration
|
|
func (c *VaultConfig) IsEnabled() bool {
|
|
return c.Enabled != nil && *c.Enabled
|
|
}
|
|
|
|
// AllowsUnauthenticated returns whether the config allows unauthenticated
|
|
// access to Vault
|
|
func (c *VaultConfig) AllowsUnauthenticated() bool {
|
|
return c.AllowUnauthenticated != nil && *c.AllowUnauthenticated
|
|
}
|
|
|
|
// Merge merges two Vault configurations together.
|
|
func (c *VaultConfig) Merge(b *VaultConfig) *VaultConfig {
|
|
result := *c
|
|
|
|
if b.Enabled != nil {
|
|
result.Enabled = b.Enabled
|
|
}
|
|
if b.Token != "" {
|
|
result.Token = b.Token
|
|
}
|
|
if b.Role != "" {
|
|
result.Role = b.Role
|
|
}
|
|
if b.Namespace != "" {
|
|
result.Namespace = b.Namespace
|
|
}
|
|
if b.AllowUnauthenticated != nil {
|
|
result.AllowUnauthenticated = b.AllowUnauthenticated
|
|
}
|
|
if b.TaskTokenTTL != "" {
|
|
result.TaskTokenTTL = b.TaskTokenTTL
|
|
}
|
|
if b.Addr != "" {
|
|
result.Addr = b.Addr
|
|
}
|
|
if b.ConnectionRetryIntv.Nanoseconds() != 0 {
|
|
result.ConnectionRetryIntv = b.ConnectionRetryIntv
|
|
}
|
|
if b.TLSCaFile != "" {
|
|
result.TLSCaFile = b.TLSCaFile
|
|
}
|
|
if b.TLSCaPath != "" {
|
|
result.TLSCaPath = b.TLSCaPath
|
|
}
|
|
if b.TLSCertFile != "" {
|
|
result.TLSCertFile = b.TLSCertFile
|
|
}
|
|
if b.TLSKeyFile != "" {
|
|
result.TLSKeyFile = b.TLSKeyFile
|
|
}
|
|
if b.TLSSkipVerify != nil {
|
|
result.TLSSkipVerify = b.TLSSkipVerify
|
|
}
|
|
if b.TLSServerName != "" {
|
|
result.TLSServerName = b.TLSServerName
|
|
}
|
|
|
|
return &result
|
|
}
|
|
|
|
// ApiConfig returns a usable Vault config that can be passed directly to
|
|
// hashicorp/vault/api.
|
|
func (c *VaultConfig) ApiConfig() (*vault.Config, error) {
|
|
conf := vault.DefaultConfig()
|
|
tlsConf := &vault.TLSConfig{
|
|
CACert: c.TLSCaFile,
|
|
CAPath: c.TLSCaPath,
|
|
ClientCert: c.TLSCertFile,
|
|
ClientKey: c.TLSKeyFile,
|
|
TLSServerName: c.TLSServerName,
|
|
}
|
|
if c.TLSSkipVerify != nil {
|
|
tlsConf.Insecure = *c.TLSSkipVerify
|
|
} else {
|
|
tlsConf.Insecure = false
|
|
}
|
|
|
|
if err := conf.ConfigureTLS(tlsConf); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conf.Address = c.Addr
|
|
return conf, nil
|
|
}
|
|
|
|
// Copy returns a copy of this Vault config.
|
|
func (c *VaultConfig) Copy() *VaultConfig {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
|
|
nc := new(VaultConfig)
|
|
*nc = *c
|
|
return nc
|
|
}
|
|
|
|
// Equal compares two Vault configurations and returns a boolean indicating
|
|
// if they are equal.
|
|
func (c *VaultConfig) Equal(b *VaultConfig) bool {
|
|
if c == nil && b != nil {
|
|
return false
|
|
}
|
|
if c != nil && b == nil {
|
|
return false
|
|
}
|
|
|
|
if c.Enabled == nil || b.Enabled == nil {
|
|
if c.Enabled != b.Enabled {
|
|
return false
|
|
}
|
|
} else if *c.Enabled != *b.Enabled {
|
|
return false
|
|
}
|
|
|
|
if c.Token != b.Token {
|
|
return false
|
|
}
|
|
if c.Role != b.Role {
|
|
return false
|
|
}
|
|
if c.Namespace != b.Namespace {
|
|
return false
|
|
}
|
|
|
|
if c.AllowUnauthenticated == nil || b.AllowUnauthenticated == nil {
|
|
if c.AllowUnauthenticated != b.AllowUnauthenticated {
|
|
return false
|
|
}
|
|
} else if *c.AllowUnauthenticated != *b.AllowUnauthenticated {
|
|
return false
|
|
}
|
|
|
|
if c.TaskTokenTTL != b.TaskTokenTTL {
|
|
return false
|
|
}
|
|
if c.Addr != b.Addr {
|
|
return false
|
|
}
|
|
if c.ConnectionRetryIntv.Nanoseconds() != b.ConnectionRetryIntv.Nanoseconds() {
|
|
return false
|
|
}
|
|
if c.TLSCaFile != b.TLSCaFile {
|
|
return false
|
|
}
|
|
if c.TLSCaPath != b.TLSCaPath {
|
|
return false
|
|
}
|
|
if c.TLSCertFile != b.TLSCertFile {
|
|
return false
|
|
}
|
|
if c.TLSKeyFile != b.TLSKeyFile {
|
|
return false
|
|
}
|
|
|
|
if c.TLSSkipVerify == nil || b.TLSSkipVerify == nil {
|
|
if c.TLSSkipVerify != b.TLSSkipVerify {
|
|
return false
|
|
}
|
|
} else if *c.TLSSkipVerify != *b.TLSSkipVerify {
|
|
return false
|
|
}
|
|
|
|
if c.TLSServerName != b.TLSServerName {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|