vendor vault api
This commit is contained in:
parent
8df67563c7
commit
562ea52c8e
|
@ -480,6 +480,7 @@ func runnerConfig(config *config.Config, vaultToken string) (*ctconf.Config, err
|
|||
if config.VaultConfig != nil && config.VaultConfig.IsEnabled() {
|
||||
conf.Vault.Address = &config.VaultConfig.Addr
|
||||
conf.Vault.Token = &vaultToken
|
||||
// XXX
|
||||
|
||||
if strings.HasPrefix(config.VaultConfig.Addr, "https") || config.VaultConfig.TLSCertFile != "" {
|
||||
skipVerify := config.VaultConfig.TLSSkipVerify != nil && *config.VaultConfig.TLSSkipVerify
|
||||
|
|
|
@ -135,6 +135,26 @@ func (c *TokenAuth) RenewSelf(increment int) (*Secret, error) {
|
|||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
// RenewTokenAsSelf behaves like renew-self, but authenticates using a provided
|
||||
// token instead of the token attached to the client.
|
||||
func (c *TokenAuth) RenewTokenAsSelf(token string, increment int) (*Secret, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/auth/token/renew-self")
|
||||
r.ClientToken = token
|
||||
|
||||
body := map[string]interface{}{"increment": increment}
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
// RevokeAccessor revokes a token associated with the given accessor
|
||||
// along with all the child tokens.
|
||||
func (c *TokenAuth) RevokeAccessor(accessor string) error {
|
||||
|
|
|
@ -6,13 +6,17 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/go-rootcerts"
|
||||
"github.com/hashicorp/vault/helper/parseutil"
|
||||
"github.com/sethgrid/pester"
|
||||
)
|
||||
|
||||
|
@ -21,10 +25,12 @@ const EnvVaultCACert = "VAULT_CACERT"
|
|||
const EnvVaultCAPath = "VAULT_CAPATH"
|
||||
const EnvVaultClientCert = "VAULT_CLIENT_CERT"
|
||||
const EnvVaultClientKey = "VAULT_CLIENT_KEY"
|
||||
const EnvVaultClientTimeout = "VAULT_CLIENT_TIMEOUT"
|
||||
const EnvVaultInsecure = "VAULT_SKIP_VERIFY"
|
||||
const EnvVaultTLSServerName = "VAULT_TLS_SERVER_NAME"
|
||||
const EnvVaultWrapTTL = "VAULT_WRAP_TTL"
|
||||
const EnvVaultMaxRetries = "VAULT_MAX_RETRIES"
|
||||
const EnvVaultToken = "VAULT_TOKEN"
|
||||
|
||||
// WrappingLookupFunc is a function that, given an HTTP verb and a path,
|
||||
// returns an optional string duration to be used for response wrapping (e.g.
|
||||
|
@ -50,6 +56,9 @@ type Config struct {
|
|||
// MaxRetries controls the maximum number of times to retry when a 5xx error
|
||||
// occurs. Set to 0 or less to disable retrying. Defaults to 0.
|
||||
MaxRetries int
|
||||
|
||||
// Timeout is for setting custom timeout parameter in the HttpClient
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// TLSConfig contains the parameters needed to configure TLS on the HTTP client
|
||||
|
@ -85,7 +94,6 @@ type TLSConfig struct {
|
|||
func DefaultConfig() *Config {
|
||||
config := &Config{
|
||||
Address: "https://127.0.0.1:8200",
|
||||
|
||||
HttpClient: cleanhttp.DefaultClient(),
|
||||
}
|
||||
config.HttpClient.Timeout = time.Second * 60
|
||||
|
@ -104,9 +112,8 @@ func DefaultConfig() *Config {
|
|||
|
||||
// ConfigureTLS takes a set of TLS configurations and applies those to the the HTTP client.
|
||||
func (c *Config) ConfigureTLS(t *TLSConfig) error {
|
||||
|
||||
if c.HttpClient == nil {
|
||||
return fmt.Errorf("config HTTP Client must be set")
|
||||
c.HttpClient = DefaultConfig().HttpClient
|
||||
}
|
||||
|
||||
var clientCert tls.Certificate
|
||||
|
@ -154,6 +161,7 @@ func (c *Config) ReadEnvironment() error {
|
|||
var envCAPath string
|
||||
var envClientCert string
|
||||
var envClientKey string
|
||||
var envClientTimeout time.Duration
|
||||
var envInsecure bool
|
||||
var envTLSServerName string
|
||||
var envMaxRetries *uint64
|
||||
|
@ -181,6 +189,13 @@ func (c *Config) ReadEnvironment() error {
|
|||
if v := os.Getenv(EnvVaultClientKey); v != "" {
|
||||
envClientKey = v
|
||||
}
|
||||
if t := os.Getenv(EnvVaultClientTimeout); t != "" {
|
||||
clientTimeout, err := parseutil.ParseDurationSecond(t)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not parse %s", EnvVaultClientTimeout)
|
||||
}
|
||||
envClientTimeout = clientTimeout
|
||||
}
|
||||
if v := os.Getenv(EnvVaultInsecure); v != "" {
|
||||
var err error
|
||||
envInsecure, err = strconv.ParseBool(v)
|
||||
|
@ -213,6 +228,10 @@ func (c *Config) ReadEnvironment() error {
|
|||
c.MaxRetries = int(*envMaxRetries) + 1
|
||||
}
|
||||
|
||||
if envClientTimeout != 0 {
|
||||
c.Timeout = envClientTimeout
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -247,6 +266,11 @@ func NewClient(c *Config) (*Client, error) {
|
|||
c.HttpClient = DefaultConfig().HttpClient
|
||||
}
|
||||
|
||||
tp := c.HttpClient.Transport.(*http.Transport)
|
||||
if err := http2.ConfigureTransport(tp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
redirFunc := func() {
|
||||
// Ensure redirects are not automatically followed
|
||||
// Note that this is sane for the API client as it has its own
|
||||
|
@ -254,9 +278,9 @@ func NewClient(c *Config) (*Client, error) {
|
|||
// but in e.g. http_test actual redirect handling is necessary
|
||||
c.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
// Returning this value causes the Go net library to not close the
|
||||
// response body and nil out the error. Otherwise pester tries
|
||||
// response body and to nil out the error. Otherwise pester tries
|
||||
// three times on every redirect because it sees an error from this
|
||||
// function being passed through.
|
||||
// function (to prevent redirects) passing through to it.
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
}
|
||||
|
@ -268,7 +292,7 @@ func NewClient(c *Config) (*Client, error) {
|
|||
config: c,
|
||||
}
|
||||
|
||||
if token := os.Getenv("VAULT_TOKEN"); token != "" {
|
||||
if token := os.Getenv(EnvVaultToken); token != "" {
|
||||
client.SetToken(token)
|
||||
}
|
||||
|
||||
|
@ -292,6 +316,16 @@ func (c *Client) Address() string {
|
|||
return c.addr.String()
|
||||
}
|
||||
|
||||
// SetMaxRetries sets the number of retries that will be used in the case of certain errors
|
||||
func (c *Client) SetMaxRetries(retries int) {
|
||||
c.config.MaxRetries = retries
|
||||
}
|
||||
|
||||
// SetClientTimeout sets the client request timeout
|
||||
func (c *Client) SetClientTimeout(timeout time.Duration) {
|
||||
c.config.Timeout = timeout
|
||||
}
|
||||
|
||||
// SetWrappingLookupFunc sets a lookup function that returns desired wrap TTLs
|
||||
// for a given operation and path
|
||||
func (c *Client) SetWrappingLookupFunc(lookupFunc WrappingLookupFunc) {
|
||||
|
@ -315,16 +349,22 @@ func (c *Client) ClearToken() {
|
|||
c.token = ""
|
||||
}
|
||||
|
||||
// Clone creates a copy of this client.
|
||||
func (c *Client) Clone() (*Client, error) {
|
||||
return NewClient(c.config)
|
||||
}
|
||||
|
||||
// NewRequest creates a new raw request object to query the Vault server
|
||||
// configured for this client. This is an advanced method and generally
|
||||
// doesn't need to be called externally.
|
||||
func (c *Client) NewRequest(method, path string) *Request {
|
||||
func (c *Client) NewRequest(method, requestPath string) *Request {
|
||||
req := &Request{
|
||||
Method: method,
|
||||
URL: &url.URL{
|
||||
User: c.addr.User,
|
||||
Scheme: c.addr.Scheme,
|
||||
Host: c.addr.Host,
|
||||
Path: path,
|
||||
Path: path.Join(c.addr.Path, requestPath),
|
||||
},
|
||||
ClientToken: c.token,
|
||||
Params: make(map[string][]string),
|
||||
|
@ -332,18 +372,21 @@ func (c *Client) NewRequest(method, path string) *Request {
|
|||
|
||||
var lookupPath string
|
||||
switch {
|
||||
case strings.HasPrefix(path, "/v1/"):
|
||||
lookupPath = strings.TrimPrefix(path, "/v1/")
|
||||
case strings.HasPrefix(path, "v1/"):
|
||||
lookupPath = strings.TrimPrefix(path, "v1/")
|
||||
case strings.HasPrefix(requestPath, "/v1/"):
|
||||
lookupPath = strings.TrimPrefix(requestPath, "/v1/")
|
||||
case strings.HasPrefix(requestPath, "v1/"):
|
||||
lookupPath = strings.TrimPrefix(requestPath, "v1/")
|
||||
default:
|
||||
lookupPath = path
|
||||
lookupPath = requestPath
|
||||
}
|
||||
if c.wrappingLookupFunc != nil {
|
||||
req.WrapTTL = c.wrappingLookupFunc(method, lookupPath)
|
||||
} else {
|
||||
req.WrapTTL = DefaultWrappingLookupFunc(method, lookupPath)
|
||||
}
|
||||
if c.config.Timeout != 0 {
|
||||
c.config.HttpClient.Timeout = c.config.Timeout
|
||||
}
|
||||
|
||||
return req
|
||||
}
|
||||
|
|
|
@ -0,0 +1,302 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRenewerMissingInput = errors.New("missing input to renewer")
|
||||
ErrRenewerMissingSecret = errors.New("missing secret to renew")
|
||||
ErrRenewerNotRenewable = errors.New("secret is not renewable")
|
||||
ErrRenewerNoSecretData = errors.New("returned empty secret data")
|
||||
|
||||
// DefaultRenewerGrace is the default grace period
|
||||
DefaultRenewerGrace = 15 * time.Second
|
||||
|
||||
// DefaultRenewerRenewBuffer is the default size of the buffer for renew
|
||||
// messages on the channel.
|
||||
DefaultRenewerRenewBuffer = 5
|
||||
)
|
||||
|
||||
// Renewer is a process for renewing a secret.
|
||||
//
|
||||
// renewer, err := client.NewRenewer(&RenewerInput{
|
||||
// Secret: mySecret,
|
||||
// })
|
||||
// go renewer.Renew()
|
||||
// defer renewer.Stop()
|
||||
//
|
||||
// for {
|
||||
// select {
|
||||
// case err := <-renewer.DoneCh():
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// // Renewal is now over
|
||||
// case renewal := <-renewer.RenewCh():
|
||||
// log.Printf("Successfully renewed: %#v", renewal)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// The `DoneCh` will return if renewal fails or if the remaining lease duration
|
||||
// after a renewal is less than or equal to the grace (in number of seconds). In
|
||||
// both cases, the caller should attempt a re-read of the secret. Clients should
|
||||
// check the return value of the channel to see if renewal was successful.
|
||||
type Renewer struct {
|
||||
l sync.Mutex
|
||||
|
||||
client *Client
|
||||
secret *Secret
|
||||
grace time.Duration
|
||||
random *rand.Rand
|
||||
doneCh chan error
|
||||
renewCh chan *RenewOutput
|
||||
|
||||
stopped bool
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
// RenewerInput is used as input to the renew function.
|
||||
type RenewerInput struct {
|
||||
// Secret is the secret to renew
|
||||
Secret *Secret
|
||||
|
||||
// Grace is a minimum renewal before returning so the upstream client
|
||||
// can do a re-read. This can be used to prevent clients from waiting
|
||||
// too long to read a new credential and incur downtime.
|
||||
Grace time.Duration
|
||||
|
||||
// Rand is the randomizer to use for underlying randomization. If not
|
||||
// provided, one will be generated and seeded automatically. If provided, it
|
||||
// is assumed to have already been seeded.
|
||||
Rand *rand.Rand
|
||||
|
||||
// RenewBuffer is the size of the buffered channel where renew messages are
|
||||
// dispatched.
|
||||
RenewBuffer int
|
||||
}
|
||||
|
||||
// RenewOutput is the metadata returned to the client (if it's listening) to
|
||||
// renew messages.
|
||||
type RenewOutput struct {
|
||||
// RenewedAt is the timestamp when the renewal took place (UTC).
|
||||
RenewedAt time.Time
|
||||
|
||||
// Secret is the underlying renewal data. It's the same struct as all data
|
||||
// that is returned from Vault, but since this is renewal data, it will not
|
||||
// usually include the secret itself.
|
||||
Secret *Secret
|
||||
}
|
||||
|
||||
// NewRenewer creates a new renewer from the given input.
|
||||
func (c *Client) NewRenewer(i *RenewerInput) (*Renewer, error) {
|
||||
if i == nil {
|
||||
return nil, ErrRenewerMissingInput
|
||||
}
|
||||
|
||||
secret := i.Secret
|
||||
if secret == nil {
|
||||
return nil, ErrRenewerMissingSecret
|
||||
}
|
||||
|
||||
grace := i.Grace
|
||||
if grace == 0 {
|
||||
grace = DefaultRenewerGrace
|
||||
}
|
||||
|
||||
random := i.Rand
|
||||
if random == nil {
|
||||
random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
|
||||
}
|
||||
|
||||
renewBuffer := i.RenewBuffer
|
||||
if renewBuffer == 0 {
|
||||
renewBuffer = DefaultRenewerRenewBuffer
|
||||
}
|
||||
|
||||
return &Renewer{
|
||||
client: c,
|
||||
secret: secret,
|
||||
grace: grace,
|
||||
random: random,
|
||||
doneCh: make(chan error, 1),
|
||||
renewCh: make(chan *RenewOutput, renewBuffer),
|
||||
|
||||
stopped: false,
|
||||
stopCh: make(chan struct{}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DoneCh returns the channel where the renewer will publish when renewal stops.
|
||||
// If there is an error, this will be an error.
|
||||
func (r *Renewer) DoneCh() <-chan error {
|
||||
return r.doneCh
|
||||
}
|
||||
|
||||
// RenewCh is a channel that receives a message when a successful renewal takes
|
||||
// place and includes metadata about the renewal.
|
||||
func (r *Renewer) RenewCh() <-chan *RenewOutput {
|
||||
return r.renewCh
|
||||
}
|
||||
|
||||
// Stop stops the renewer.
|
||||
func (r *Renewer) Stop() {
|
||||
r.l.Lock()
|
||||
if !r.stopped {
|
||||
close(r.stopCh)
|
||||
r.stopped = true
|
||||
}
|
||||
r.l.Unlock()
|
||||
}
|
||||
|
||||
// Renew starts a background process for renewing this secret. When the secret
|
||||
// is has auth data, this attempts to renew the auth (token). When the secret
|
||||
// has a lease, this attempts to renew the lease.
|
||||
func (r *Renewer) Renew() {
|
||||
var result error
|
||||
if r.secret.Auth != nil {
|
||||
result = r.renewAuth()
|
||||
} else {
|
||||
result = r.renewLease()
|
||||
}
|
||||
|
||||
select {
|
||||
case r.doneCh <- result:
|
||||
case <-r.stopCh:
|
||||
}
|
||||
}
|
||||
|
||||
// renewAuth is a helper for renewing authentication.
|
||||
func (r *Renewer) renewAuth() error {
|
||||
if !r.secret.Auth.Renewable || r.secret.Auth.ClientToken == "" {
|
||||
return ErrRenewerNotRenewable
|
||||
}
|
||||
|
||||
client, token := r.client, r.secret.Auth.ClientToken
|
||||
|
||||
for {
|
||||
// Check if we are stopped.
|
||||
select {
|
||||
case <-r.stopCh:
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
// Renew the auth.
|
||||
renewal, err := client.Auth().Token().RenewTokenAsSelf(token, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Push a message that a renewal took place.
|
||||
select {
|
||||
case r.renewCh <- &RenewOutput{time.Now().UTC(), renewal}:
|
||||
default:
|
||||
}
|
||||
|
||||
// Somehow, sometimes, this happens.
|
||||
if renewal == nil || renewal.Auth == nil {
|
||||
return ErrRenewerNoSecretData
|
||||
}
|
||||
|
||||
// Do nothing if we are not renewable
|
||||
if !renewal.Auth.Renewable {
|
||||
return ErrRenewerNotRenewable
|
||||
}
|
||||
|
||||
// Grab the lease duration and sleep duration - note that we grab the auth
|
||||
// lease duration, not the secret lease duration.
|
||||
leaseDuration := time.Duration(renewal.Auth.LeaseDuration) * time.Second
|
||||
sleepDuration := r.sleepDuration(leaseDuration)
|
||||
|
||||
// If we are within grace, return now.
|
||||
if leaseDuration <= r.grace || sleepDuration <= r.grace {
|
||||
return nil
|
||||
}
|
||||
|
||||
select {
|
||||
case <-r.stopCh:
|
||||
return nil
|
||||
case <-time.After(sleepDuration):
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// renewLease is a helper for renewing a lease.
|
||||
func (r *Renewer) renewLease() error {
|
||||
if !r.secret.Renewable || r.secret.LeaseID == "" {
|
||||
return ErrRenewerNotRenewable
|
||||
}
|
||||
|
||||
client, leaseID := r.client, r.secret.LeaseID
|
||||
|
||||
for {
|
||||
// Check if we are stopped.
|
||||
select {
|
||||
case <-r.stopCh:
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
// Renew the lease.
|
||||
renewal, err := client.Sys().Renew(leaseID, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Push a message that a renewal took place.
|
||||
select {
|
||||
case r.renewCh <- &RenewOutput{time.Now().UTC(), renewal}:
|
||||
default:
|
||||
}
|
||||
|
||||
// Somehow, sometimes, this happens.
|
||||
if renewal == nil {
|
||||
return ErrRenewerNoSecretData
|
||||
}
|
||||
|
||||
// Do nothing if we are not renewable
|
||||
if !renewal.Renewable {
|
||||
return ErrRenewerNotRenewable
|
||||
}
|
||||
|
||||
// Grab the lease duration and sleep duration
|
||||
leaseDuration := time.Duration(renewal.LeaseDuration) * time.Second
|
||||
sleepDuration := r.sleepDuration(leaseDuration)
|
||||
|
||||
// If we are within grace, return now.
|
||||
if leaseDuration <= r.grace || sleepDuration <= r.grace {
|
||||
return nil
|
||||
}
|
||||
|
||||
select {
|
||||
case <-r.stopCh:
|
||||
return nil
|
||||
case <-time.After(sleepDuration):
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sleepDuration calculates the time to sleep given the base lease duration. The
|
||||
// base is the resulting lease duration. It will be reduced to 1/3 and
|
||||
// multiplied by a random float between 0.0 and 1.0. This extra randomness
|
||||
// prevents multiple clients from all trying to renew simultaneously.
|
||||
func (r *Renewer) sleepDuration(base time.Duration) time.Duration {
|
||||
sleep := float64(base)
|
||||
|
||||
// Renew at 1/3 the remaining lease. This will give us an opportunity to retry
|
||||
// at least one more time should the first renewal fail.
|
||||
sleep = sleep / 3.0
|
||||
|
||||
// Use a randomness so many clients do not hit Vault simultaneously.
|
||||
sleep = sleep * (r.random.Float64() + 1) / 2.0
|
||||
|
||||
return time.Duration(sleep)
|
||||
}
|
|
@ -14,6 +14,7 @@ type Request struct {
|
|||
Method string
|
||||
URL *url.URL
|
||||
Params url.Values
|
||||
Headers http.Header
|
||||
ClientToken string
|
||||
WrapTTL string
|
||||
Obj interface{}
|
||||
|
@ -55,10 +56,19 @@ func (r *Request) ToHTTP() (*http.Request, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
req.URL.User = r.URL.User
|
||||
req.URL.Scheme = r.URL.Scheme
|
||||
req.URL.Host = r.URL.Host
|
||||
req.Host = r.URL.Host
|
||||
|
||||
if r.Headers != nil {
|
||||
for header, vals := range r.Headers {
|
||||
for _, val := range vals {
|
||||
req.Header.Add(header, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(r.ClientToken) != 0 {
|
||||
req.Header.Set("X-Vault-Token", r.ClientToken)
|
||||
}
|
||||
|
|
|
@ -25,8 +25,9 @@ func (r *Response) DecodeJSON(out interface{}) error {
|
|||
// this will fully consume the response body, but will not close it. The
|
||||
// body must still be closed manually.
|
||||
func (r *Response) Error() error {
|
||||
// 200 to 399 are okay status codes
|
||||
if r.StatusCode >= 200 && r.StatusCode < 400 {
|
||||
// 200 to 399 are okay status codes. 429 is the code for health status of
|
||||
// standby nodes.
|
||||
if (r.StatusCode >= 200 && r.StatusCode < 400) || r.StatusCode == 429 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package api
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
|
@ -71,14 +72,19 @@ func (c *Sys) ListAudit() (map[string]*Audit, error) {
|
|||
return mounts, nil
|
||||
}
|
||||
|
||||
// DEPRECATED: Use EnableAuditWithOptions instead
|
||||
func (c *Sys) EnableAudit(
|
||||
path string, auditType string, desc string, opts map[string]string) error {
|
||||
body := map[string]interface{}{
|
||||
"type": auditType,
|
||||
"description": desc,
|
||||
"options": opts,
|
||||
return c.EnableAuditWithOptions(path, &EnableAuditOptions{
|
||||
Type: auditType,
|
||||
Description: desc,
|
||||
Options: opts,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Sys) EnableAuditWithOptions(path string, options *EnableAuditOptions) error {
|
||||
body := structs.Map(options)
|
||||
|
||||
r := c.c.NewRequest("PUT", fmt.Sprintf("/v1/sys/audit/%s", path))
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return err
|
||||
|
@ -106,9 +112,17 @@ func (c *Sys) DisableAudit(path string) error {
|
|||
// individually documented because the map almost directly to the raw HTTP API
|
||||
// documentation. Please refer to that documentation for more details.
|
||||
|
||||
type EnableAuditOptions struct {
|
||||
Type string `json:"type" structs:"type"`
|
||||
Description string `json:"description" structs:"description"`
|
||||
Options map[string]string `json:"options" structs:"options"`
|
||||
Local bool `json:"local" structs:"local"`
|
||||
}
|
||||
|
||||
type Audit struct {
|
||||
Path string
|
||||
Type string
|
||||
Description string
|
||||
Options map[string]string
|
||||
Local bool
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package api
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
|
@ -42,12 +43,17 @@ func (c *Sys) ListAuth() (map[string]*AuthMount, error) {
|
|||
return mounts, nil
|
||||
}
|
||||
|
||||
// DEPRECATED: Use EnableAuthWithOptions instead
|
||||
func (c *Sys) EnableAuth(path, authType, desc string) error {
|
||||
body := map[string]string{
|
||||
"type": authType,
|
||||
"description": desc,
|
||||
return c.EnableAuthWithOptions(path, &EnableAuthOptions{
|
||||
Type: authType,
|
||||
Description: desc,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Sys) EnableAuthWithOptions(path string, options *EnableAuthOptions) error {
|
||||
body := structs.Map(options)
|
||||
|
||||
r := c.c.NewRequest("POST", fmt.Sprintf("/v1/sys/auth/%s", path))
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return err
|
||||
|
@ -75,13 +81,23 @@ func (c *Sys) DisableAuth(path string) error {
|
|||
// individually documentd because the map almost directly to the raw HTTP API
|
||||
// documentation. Please refer to that documentation for more details.
|
||||
|
||||
type EnableAuthOptions struct {
|
||||
Type string `json:"type" structs:"type"`
|
||||
Description string `json:"description" structs:"description"`
|
||||
Local bool `json:"local" structs:"local"`
|
||||
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||
}
|
||||
|
||||
type AuthMount struct {
|
||||
Type string `json:"type" structs:"type" mapstructure:"type"`
|
||||
Description string `json:"description" structs:"description" mapstructure:"description"`
|
||||
Accessor string `json:"accessor" structs:"accessor" mapstructure:"accessor"`
|
||||
Config AuthConfigOutput `json:"config" structs:"config" mapstructure:"config"`
|
||||
Local bool `json:"local" structs:"local" mapstructure:"local"`
|
||||
}
|
||||
|
||||
type AuthConfigOutput struct {
|
||||
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package api
|
||||
|
||||
func (c *Sys) CORSStatus() (*CORSResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/config/cors")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result CORSResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) ConfigureCORS(req *CORSRequest) (*CORSResponse, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/config/cors")
|
||||
if err := r.SetJSONBody(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result CORSResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) DisableCORS() (*CORSResponse, error) {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/config/cors")
|
||||
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result CORSResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
|
||||
}
|
||||
|
||||
type CORSRequest struct {
|
||||
AllowedOrigins string `json:"allowed_origins"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type CORSResponse struct {
|
||||
AllowedOrigins string `json:"allowed_origins"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package api
|
||||
|
||||
func (c *Sys) Health() (*HealthResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/health")
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result HealthResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
type HealthResponse struct {
|
||||
Initialized bool `json:"initialized"`
|
||||
Sealed bool `json:"sealed"`
|
||||
Standby bool `json:"standby"`
|
||||
ServerTimeUTC int64 `json:"server_time_utc"`
|
||||
Version string `json:"version"`
|
||||
ClusterName string `json:"cluster_name,omitempty"`
|
||||
ClusterID string `json:"cluster_id,omitempty"`
|
||||
}
|
|
@ -17,4 +17,5 @@ type LeaderResponse struct {
|
|||
HAEnabled bool `json:"ha_enabled"`
|
||||
IsSelf bool `json:"is_self"`
|
||||
LeaderAddress string `json:"leader_address"`
|
||||
LeaderClusterAddress string `json:"leader_cluster_address"`
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package api
|
||||
|
||||
func (c *Sys) Renew(id string, increment int) (*Secret, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/renew")
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/leases/renew")
|
||||
|
||||
body := map[string]interface{}{
|
||||
"increment": increment,
|
||||
|
@ -21,7 +21,7 @@ func (c *Sys) Renew(id string, increment int) (*Secret, error) {
|
|||
}
|
||||
|
||||
func (c *Sys) Revoke(id string) error {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/revoke/"+id)
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke/"+id)
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
|
@ -30,7 +30,7 @@ func (c *Sys) Revoke(id string) error {
|
|||
}
|
||||
|
||||
func (c *Sys) RevokePrefix(id string) error {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/revoke-prefix/"+id)
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke-prefix/"+id)
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
|
@ -39,7 +39,7 @@ func (c *Sys) RevokePrefix(id string) error {
|
|||
}
|
||||
|
||||
func (c *Sys) RevokeForce(id string) error {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/revoke-force/"+id)
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke-force/"+id)
|
||||
resp, err := c.c.RawRequest(r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
|
@ -123,20 +123,27 @@ type MountInput struct {
|
|||
Type string `json:"type" structs:"type"`
|
||||
Description string `json:"description" structs:"description"`
|
||||
Config MountConfigInput `json:"config" structs:"config"`
|
||||
Local bool `json:"local" structs:"local"`
|
||||
}
|
||||
|
||||
type MountConfigInput struct {
|
||||
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
||||
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||
}
|
||||
|
||||
type MountOutput struct {
|
||||
Type string `json:"type" structs:"type"`
|
||||
Description string `json:"description" structs:"description"`
|
||||
Accessor string `json:"accessor" structs:"accessor"`
|
||||
Config MountConfigOutput `json:"config" structs:"config"`
|
||||
Local bool `json:"local" structs:"local"`
|
||||
}
|
||||
|
||||
type MountConfigOutput struct {
|
||||
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
|
||||
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ type SealStatusResponse struct {
|
|||
T int `json:"t"`
|
||||
N int `json:"n"`
|
||||
Progress int `json:"progress"`
|
||||
Nonce string `json:"nonce"`
|
||||
Version string `json:"version"`
|
||||
ClusterName string `json:"cluster_name,omitempty"`
|
||||
ClusterID string `json:"cluster_id,omitempty"`
|
||||
|
|
|
@ -2,13 +2,19 @@ package jsonutil
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/hashicorp/vault/helper/compressutil"
|
||||
)
|
||||
|
||||
// Encodes/Marshals the given object into JSON
|
||||
func EncodeJSON(in interface{}) ([]byte, error) {
|
||||
if in == nil {
|
||||
return nil, fmt.Errorf("input for encoding is nil")
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
if err := enc.Encode(in); err != nil {
|
||||
|
@ -17,15 +23,60 @@ func EncodeJSON(in interface{}) ([]byte, error) {
|
|||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// Decodes/Unmarshals the given JSON into a desired object
|
||||
// EncodeJSONAndCompress encodes the given input into JSON and compresses the
|
||||
// encoded value (using Gzip format BestCompression level, by default). A
|
||||
// canary byte is placed at the beginning of the returned bytes for the logic
|
||||
// in decompression method to identify compressed input.
|
||||
func EncodeJSONAndCompress(in interface{}, config *compressutil.CompressionConfig) ([]byte, error) {
|
||||
if in == nil {
|
||||
return nil, fmt.Errorf("input for encoding is nil")
|
||||
}
|
||||
|
||||
// First JSON encode the given input
|
||||
encodedBytes, err := EncodeJSON(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
config = &compressutil.CompressionConfig{
|
||||
Type: compressutil.CompressionTypeGzip,
|
||||
GzipCompressionLevel: gzip.BestCompression,
|
||||
}
|
||||
}
|
||||
|
||||
return compressutil.Compress(encodedBytes, config)
|
||||
}
|
||||
|
||||
// DecodeJSON tries to decompress the given data. The call to decompress, fails
|
||||
// if the content was not compressed in the first place, which is identified by
|
||||
// a canary byte before the compressed data. If the data is not compressed, it
|
||||
// is JSON decoded directly. Otherwise the decompressed data will be JSON
|
||||
// decoded.
|
||||
func DecodeJSON(data []byte, out interface{}) error {
|
||||
if data == nil {
|
||||
if data == nil || len(data) == 0 {
|
||||
return fmt.Errorf("'data' being decoded is nil")
|
||||
}
|
||||
if out == nil {
|
||||
return fmt.Errorf("output parameter 'out' is nil")
|
||||
}
|
||||
|
||||
// Decompress the data if it was compressed in the first place
|
||||
decompressedBytes, uncompressed, err := compressutil.Decompress(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decompress JSON: err: %v", err)
|
||||
}
|
||||
if !uncompressed && (decompressedBytes == nil || len(decompressedBytes) == 0) {
|
||||
return fmt.Errorf("decompressed data being decoded is invalid")
|
||||
}
|
||||
|
||||
// If the input supplied failed to contain the compression canary, it
|
||||
// will be notified by the compression utility. Decode the decompressed
|
||||
// input.
|
||||
if !uncompressed {
|
||||
data = decompressedBytes
|
||||
}
|
||||
|
||||
return DecodeJSONFromReader(bytes.NewReader(data), out)
|
||||
}
|
||||
|
||||
|
|
|
@ -911,16 +911,16 @@
|
|||
"revisionTime": "2016-08-21T23:40:57Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "31yBeS6U3xm7VJ7ZvDxRgBxXP0A=",
|
||||
"checksumSHA1": "hLIXn9iQhPcjY+/G64j3mIlLlK8=",
|
||||
"path": "github.com/hashicorp/vault/api",
|
||||
"revision": "f4adc7fa960ed8e828f94bc6785bcdbae8d1b263",
|
||||
"revisionTime": "2016-12-16T21:07:16Z"
|
||||
"revision": "0c3e14f047aede0a70256e1e8b321610910b246e",
|
||||
"revisionTime": "2017-08-01T15:50:41Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "5lR6EdY0ARRdKAq3hZcL38STD8Q=",
|
||||
"checksumSHA1": "yUiSTPf0QUuL2r/81sjuytqBoeQ=",
|
||||
"path": "github.com/hashicorp/vault/helper/jsonutil",
|
||||
"revision": "bcf98fa8d61d1870c3af689f1b090b29a9c12d8c",
|
||||
"revisionTime": "2016-08-02T20:35:37Z"
|
||||
"revision": "0c3e14f047aede0a70256e1e8b321610910b246e",
|
||||
"revisionTime": "2017-08-01T15:50:41Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "VMaF3Q7RIrRzvbnPbqxuSLryOvc=",
|
||||
|
|
Loading…
Reference in New Issue