Add wrap support to API/CLI

This commit is contained in:
Jeff Mitchell 2016-05-02 01:58:58 -04:00
parent aba689a877
commit 1ffd5653c6
6 changed files with 63 additions and 12 deletions

View File

@ -26,6 +26,7 @@ const EnvVaultClientCert = "VAULT_CLIENT_CERT"
const EnvVaultClientKey = "VAULT_CLIENT_KEY"
const EnvVaultInsecure = "VAULT_SKIP_VERIFY"
const EnvVaultTLSServerName = "VAULT_TLS_SERVER_NAME"
const EnvVaultWrapTTL = "VAULT_WRAP_TTL"
var (
errRedirect = errors.New("redirect")
@ -39,6 +40,14 @@ type Config struct {
// HttpClient.
Address string
// WrapTTL, if specified, asks the Vault server to return the normal
// response wrapped in the cubbyhole of a token, with the TTL of the token
// being set to the lesser of this value or a value requested by the
// backend originating the response. Specified either as a number of
// seconds, or a string duration with a "s", "m", or "h" suffix for
// "seconds", "minutes", or "hours" respectively.
WrapTTL string
// HttpClient is the HTTP client to use, which will currently always have the
// same values as http.DefaultClient. This is used to control redirect behavior.
HttpClient *http.Client
@ -80,6 +89,7 @@ func (c *Config) ReadEnvironment() error {
var envCAPath string
var envClientCert string
var envClientKey string
var envWrapTTL string
var envInsecure bool
var foundInsecure bool
var envTLSServerName string
@ -103,6 +113,9 @@ func (c *Config) ReadEnvironment() error {
if v := os.Getenv(EnvVaultClientKey); v != "" {
envClientKey = v
}
if v := os.Getenv(EnvVaultWrapTTL); v != "" {
envWrapTTL = v
}
if v := os.Getenv(EnvVaultInsecure); v != "" {
var err error
envInsecure, err = strconv.ParseBool(v)
@ -141,6 +154,10 @@ func (c *Config) ReadEnvironment() error {
c.Address = envAddress
}
if envWrapTTL != "" {
c.WrapTTL = envWrapTTL
}
clientTLSConfig := c.HttpClient.Transport.(*http.Transport).TLSClientConfig
if foundInsecure {
clientTLSConfig.InsecureSkipVerify = envInsecure
@ -172,7 +189,6 @@ type Client struct {
// automatically added to the client. Otherwise, you must manually call
// `SetToken()`.
func NewClient(c *Config) (*Client, error) {
u, err := url.Parse(c.Address)
if err != nil {
return nil, err
@ -235,6 +251,7 @@ func (c *Client) NewRequest(method, path string) *Request {
Path: path,
},
ClientToken: c.token,
WrapTTL: c.config.WrapTTL,
Params: make(map[string][]string),
}

View File

@ -15,6 +15,7 @@ type Request struct {
URL *url.URL
Params url.Values
ClientToken string
WrapTTL string
Obj interface{}
Body io.Reader
BodySize int64
@ -62,5 +63,9 @@ func (r *Request) ToHTTP() (*http.Request, error) {
req.Header.Set("X-Vault-Token", r.ClientToken)
}
if len(r.WrapTTL) != 0 {
req.Header.Set("X-Vault-Wrap-TTL", r.WrapTTL)
}
return req, nil
}

View File

@ -23,6 +23,17 @@ type Secret struct {
// Auth, if non-nil, means that there was authentication information
// attached to this response.
Auth *SecretAuth `json:"auth,omitempty"`
// WrapInfo, if non-nil, means that the initial response was wrapped in the
// cubbyhole of the given token (which has a TTL of the given number of
// seconds)
WrapInfo *SecretWrapInfo `json:"wrap_info,omitempty"`
}
// SecretWrapInfo contains wrapping information if we have it.
type SecretWrapInfo struct {
Token string `json:"token"`
TTL int `json:"ttl"`
}
// SecretAuth is the structure containing auth information if we have it.

View File

@ -152,6 +152,11 @@ func (t TableFormatter) OutputSecret(ui cli.Ui, secret, s *api.Secret) error {
}
}
if s.WrapInfo != nil {
input = append(input, fmt.Sprintf("wrapping_token: %s %s", config.Delim, s.WrapInfo.Token))
input = append(input, fmt.Sprintf("wrapping_token_ttl: %s %d", config.Delim, s.WrapInfo.TTL))
}
keys := make([]string, 0, len(s.Data))
for k := range s.Data {
keys = append(keys, k)

View File

@ -46,6 +46,7 @@ type Meta struct {
flagCAPath string
flagClientCert string
flagClientKey string
flagWrapTTL string
flagInsecure bool
// Queried if no token can be found
@ -103,6 +104,10 @@ func (m *Meta) Client() (*api.Client, error) {
}
}
if m.flagWrapTTL != "" {
config.WrapTTL = m.flagWrapTTL
}
// Build the client
client, err := api.NewClient(config)
if err != nil {
@ -155,6 +160,7 @@ func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {
f.StringVar(&m.flagCAPath, "ca-path", "", "")
f.StringVar(&m.flagClientCert, "client-cert", "", "")
f.StringVar(&m.flagClientKey, "client-key", "", "")
f.StringVar(&m.flagWrapTTL, "wrap-ttl", "", "")
f.BoolVar(&m.flagInsecure, "insecure", false, "")
f.BoolVar(&m.flagInsecure, "tls-skip-verify", false, "")
}
@ -271,6 +277,14 @@ func GeneralOptionsUsage() string {
-tls-skip-verify Do not verify TLS certificate. This is highly
not recommended. Verification will also be skipped
if VAULT_SKIP_VERIFY is set.
-wrap-ttl Indiciates that the response should be wrapped in a
cubbyhole token with the requested TTL. The response
will live at "/response" in the cubbyhole of the
returned token with a key of "response" and can be
parsed as a normal API Secret. The backend can also
request wrapping; the lesser of the values is used.
May also be specified via VAULT_WRAP_TTL.
`
return general
}

View File

@ -12,8 +12,9 @@ import (
)
var (
// Value for memoizing whether cubbyhole is mounted, e.g. if we are in normal operation and not test mode
cubbyholeMounted *bool
// Value for memoizing whether cubbyhole is mounted, e.g. if we are in
// normal operation and not test mode
cubbyholeMounted bool
// mutex to ensure the same
cubbyholeMountedMutex sync.Mutex
@ -61,17 +62,14 @@ func (c *Core) HandleRequest(req *logical.Request) (resp *logical.Response, err
// In order to wrap, we need cubbyhole to be mounted, so we ensure that
// cubbyhole is actually mounted, as it may not be during tests. We memoize
// this response, since cubbyhole cannot be mounted or unmounted during
// a true response, since cubbyhole cannot be mounted or unmounted during
// normal operation.
if cubbyholeMounted == nil {
if !cubbyholeMounted {
cubbyholeMountedMutex.Lock()
cubbyholeMounted = new(bool)
// Ensure it wasn't changed by another goroutine
if cubbyholeMounted == nil {
if c.router.MatchingMount("cubbyhole") != "" {
*cubbyholeMounted = true
} else {
*cubbyholeMounted = false
if !cubbyholeMounted {
if c.router.MatchingMount("cubbyhole/") != "" {
cubbyholeMounted = true
}
}
cubbyholeMountedMutex.Unlock()
@ -80,7 +78,7 @@ func (c *Core) HandleRequest(req *logical.Request) (resp *logical.Response, err
// We are wrapping if there is anything to wrap (not a nil response) and a
// TTL was specified for the token, plus if cubbyhole is mounted (which
// will be the case normally)
wrapping := *cubbyholeMounted && resp != nil && resp.WrapInfo.TTL != 0
wrapping := cubbyholeMounted && resp != nil && resp.WrapInfo.TTL != 0
// If we are wrapping, the first part happens before auditing so that
// resp.WrapInfo.Token can contain the HMAC'd wrapping token ID in the
@ -150,6 +148,7 @@ func (c *Core) HandleRequest(req *logical.Request) (resp *logical.Response, err
wrappingResp := &logical.Response{
WrapInfo: logical.WrapInfo{
Token: resp.WrapInfo.Token,
TTL: resp.WrapInfo.TTL,
},
}
wrappingResp.CloneWarnings(resp)