Skip to content

Commit

Permalink
agent: support providing certificate information in cert's config map (
Browse files Browse the repository at this point in the history
…#9819)

* agent: support providing certificate information in cert's config map

* update TestCertEndToEnd

* remove URL reference on warning message
  • Loading branch information
calvn committed Aug 25, 2020
1 parent 4e69f5b commit cca1149
Show file tree
Hide file tree
Showing 7 changed files with 678 additions and 503 deletions.
34 changes: 25 additions & 9 deletions command/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,14 +344,14 @@ func (c *AgentCommand) Run(args []string) int {
switch sc.Type {
case "file":
config := &sink.SinkConfig{
Logger: c.logger.Named("sink.file"),
Config: sc.Config,
Client: client,
WrapTTL: sc.WrapTTL,
DHType: sc.DHType,
Logger: c.logger.Named("sink.file"),
Config: sc.Config,
Client: client,
WrapTTL: sc.WrapTTL,
DHType: sc.DHType,
DeriveKey: sc.DeriveKey,
DHPath: sc.DHPath,
AAD: sc.AAD,
DHPath: sc.DHPath,
AAD: sc.AAD,
}
s, err := file.NewFileSink(config)
if err != nil {
Expand Down Expand Up @@ -411,9 +411,25 @@ func (c *AgentCommand) Run(args []string) int {
}
}

// Output the header that the server has started
// Warn if cache _and_ cert auto-auth is enabled but certificates were not
// provided in the auto_auth.method["cert"].config stanza.
if config.Cache != nil && (config.AutoAuth != nil && config.AutoAuth.Method != nil && config.AutoAuth.Method.Type == "cert") {
_, okCertFile := config.AutoAuth.Method.Config["client_cert"]
_, okCertKey := config.AutoAuth.Method.Config["client_key"]

// If neither of these exists in the cert stanza, agent will use the
// certs from the vault stanza.
if !okCertFile && !okCertKey {
c.UI.Warn(wrapAtLength("WARNING! Cache is enabled and using the same certificates " +
"from the 'cert' auto-auth method specified in the 'vault' stanza. Consider " +
"specifying certificate information in the 'cert' auto-auth's config stanza."))
}

}

// Output the header that the agent has started
if !c.flagCombineLogs {
c.UI.Output("==> Vault server started! Log data will stream in below:\n")
c.UI.Output("==> Vault agent started! Log data will stream in below:\n")
}

// Inform any tests that the server is ready
Expand Down
29 changes: 26 additions & 3 deletions command/agent/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"github.com/hashicorp/vault/sdk/helper/jsonutil"
)

// AuthMethod is the interface that auto-auth methods implement for the agent
// to use.
type AuthMethod interface {
// Authenticate returns a mount path, header, request body, and error.
// The header may be nil if no special header is needed.
Expand All @@ -20,6 +22,13 @@ type AuthMethod interface {
Shutdown()
}

// AuthMethodWithClient is an extended interface that can return an API client
// for use during the authentication call.
type AuthMethodWithClient interface {
AuthMethod
AuthClient(client *api.Client) (*api.Client, error)
}

type AuthConfig struct {
Logger hclog.Logger
MountPath string
Expand Down Expand Up @@ -122,16 +131,30 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) {
backoff := 2*time.Second + time.Duration(ah.random.Int63()%int64(time.Second*2)-int64(time.Second))

ah.logger.Info("authenticating")

path, header, data, err := am.Authenticate(ctx, ah.client)
if err != nil {
ah.logger.Error("error getting path or data from method", "error", err, "backoff", backoff.Seconds())
backoffOrQuit(ctx, backoff)
continue
}

clientToUse := ah.client
var clientToUse *api.Client

switch am.(type) {
case AuthMethodWithClient:
clientToUse, err = am.(AuthMethodWithClient).AuthClient(ah.client)
if err != nil {
ah.logger.Error("error creating client for authentication call", "error", err, "backoff", backoff.Seconds())
backoffOrQuit(ctx, backoff)
continue
}
default:
clientToUse = ah.client
}

if ah.wrapTTL > 0 {
wrapClient, err := ah.client.Clone()
wrapClient, err := clientToUse.Clone()
if err != nil {
ah.logger.Error("error creating client for wrapped call", "error", err, "backoff", backoff.Seconds())
backoffOrQuit(ctx, backoff)
Expand Down Expand Up @@ -216,7 +239,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) {
watcher.Stop()
}

watcher, err = ah.client.NewLifetimeWatcher(&api.LifetimeWatcherInput{
watcher, err = clientToUse.NewLifetimeWatcher(&api.LifetimeWatcherInput{
Secret: secret,
})
if err != nil {
Expand Down
78 changes: 77 additions & 1 deletion command/agent/auth/cert/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ type certMethod struct {
logger hclog.Logger
mountPath string
name string

caCert string
clientCert string
clientKey string

// Client is the cached client to use if cert info was provided.
client *api.Client
}

var _ auth.AuthMethodWithClient = &certMethod{}

func NewCertAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) {
if conf == nil {
return nil, errors.New("empty config")
Expand All @@ -28,7 +37,6 @@ func NewCertAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) {
c := &certMethod{
logger: conf.Logger,
mountPath: conf.MountPath,
name: "",
}

if conf.Config != nil {
Expand All @@ -40,6 +48,30 @@ func NewCertAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) {
if !ok {
return nil, errors.New("could not convert 'name' config value to string")
}

caCertRaw, ok := conf.Config["ca_cert"]
if ok {
c.caCert, ok = caCertRaw.(string)
if !ok {
return nil, errors.New("could not convert 'ca_cert' config value to string")
}
}

clientCertRaw, ok := conf.Config["client_cert"]
if ok {
c.clientCert, ok = clientCertRaw.(string)
if !ok {
return nil, errors.New("could not convert 'cert_file' config value to string")
}
}

clientKeyRaw, ok := conf.Config["client_key"]
if ok {
c.clientKey, ok = clientKeyRaw.(string)
if !ok {
return nil, errors.New("could not convert 'cert_key' config value to string")
}
}
}

return c, nil
Expand All @@ -64,3 +96,47 @@ func (c *certMethod) NewCreds() chan struct{} {
func (c *certMethod) CredSuccess() {}

func (c *certMethod) Shutdown() {}

// AuthClient uses the existing client's address and returns a new client with
// the auto-auth method's certificate information if that's provided in its
// config map.
func (c *certMethod) AuthClient(client *api.Client) (*api.Client, error) {
c.logger.Trace("deriving auth client to use")

clientToAuth := client

if c.caCert != "" || (c.clientKey != "" && c.clientCert != "") {
// Return cached client if present
if c.client != nil {
return client, nil
}

config := api.DefaultConfig()
if config.Error != nil {
return nil, config.Error
}
config.Address = client.Address()

t := &api.TLSConfig{
CACert: c.caCert,
ClientCert: c.clientCert,
ClientKey: c.clientKey,
}

// Setup TLS config
if err := config.ConfigureTLS(t); err != nil {
return nil, err
}

var err error
clientToAuth, err = api.NewClient(config)
if err != nil {
return nil, err
}

// Cache the client for future use
c.client = clientToAuth
}

return clientToAuth, nil
}
Loading

0 comments on commit cca1149

Please sign in to comment.