mirror of
https://github.com/pgsty/minio.git
synced 2026-07-19 12:10:24 +03:00
Add support for SSE-S3 server side encryption with vault (#6192)
Add support for sse-s3 encryption with vault as KMS. Also refactoring code to make use of headers and functions defined in crypto package and clean up duplicated code.
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
||||
package api
|
||||
|
||||
// Auth is used to perform credential backend related operations.
|
||||
type Auth struct {
|
||||
c *Client
|
||||
}
|
||||
|
||||
// Auth is used to return the client for credential-backend API calls.
|
||||
func (c *Client) Auth() *Auth {
|
||||
return &Auth{c: c}
|
||||
}
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
package api
|
||||
|
||||
import "context"
|
||||
|
||||
// TokenAuth is used to perform token backend operations on Vault
|
||||
type TokenAuth struct {
|
||||
c *Client
|
||||
}
|
||||
|
||||
// Token is used to return the client for token-backend API calls
|
||||
func (a *Auth) Token() *TokenAuth {
|
||||
return &TokenAuth{c: a.c}
|
||||
}
|
||||
|
||||
func (c *TokenAuth) Create(opts *TokenCreateRequest) (*Secret, error) {
|
||||
r := c.c.NewRequest("POST", "/v1/auth/token/create")
|
||||
if err := r.SetJSONBody(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) CreateOrphan(opts *TokenCreateRequest) (*Secret, error) {
|
||||
r := c.c.NewRequest("POST", "/v1/auth/token/create-orphan")
|
||||
if err := r.SetJSONBody(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) CreateWithRole(opts *TokenCreateRequest, roleName string) (*Secret, error) {
|
||||
r := c.c.NewRequest("POST", "/v1/auth/token/create/"+roleName)
|
||||
if err := r.SetJSONBody(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) Lookup(token string) (*Secret, error) {
|
||||
r := c.c.NewRequest("POST", "/v1/auth/token/lookup")
|
||||
if err := r.SetJSONBody(map[string]interface{}{
|
||||
"token": token,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) LookupAccessor(accessor string) (*Secret, error) {
|
||||
r := c.c.NewRequest("POST", "/v1/auth/token/lookup-accessor")
|
||||
if err := r.SetJSONBody(map[string]interface{}{
|
||||
"accessor": accessor,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) LookupSelf() (*Secret, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/auth/token/lookup-self")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) Renew(token string, increment int) (*Secret, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/auth/token/renew")
|
||||
if err := r.SetJSONBody(map[string]interface{}{
|
||||
"token": token,
|
||||
"increment": increment,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *TokenAuth) RenewSelf(increment int) (*Secret, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/auth/token/renew-self")
|
||||
|
||||
body := map[string]interface{}{"increment": increment}
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, 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 {
|
||||
r := c.c.NewRequest("POST", "/v1/auth/token/revoke-accessor")
|
||||
if err := r.SetJSONBody(map[string]interface{}{
|
||||
"accessor": accessor,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeOrphan revokes a token without revoking the tree underneath it (so
|
||||
// child tokens are orphaned rather than revoked)
|
||||
func (c *TokenAuth) RevokeOrphan(token string) error {
|
||||
r := c.c.NewRequest("PUT", "/v1/auth/token/revoke-orphan")
|
||||
if err := r.SetJSONBody(map[string]interface{}{
|
||||
"token": token,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeSelf revokes the token making the call. The `token` parameter is kept
|
||||
// for backwards compatibility but is ignored; only the client's set token has
|
||||
// an effect.
|
||||
func (c *TokenAuth) RevokeSelf(token string) error {
|
||||
r := c.c.NewRequest("PUT", "/v1/auth/token/revoke-self")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeTree is the "normal" revoke operation that revokes the given token and
|
||||
// the entire tree underneath -- all of its child tokens, their child tokens,
|
||||
// etc.
|
||||
func (c *TokenAuth) RevokeTree(token string) error {
|
||||
r := c.c.NewRequest("PUT", "/v1/auth/token/revoke")
|
||||
if err := r.SetJSONBody(map[string]interface{}{
|
||||
"token": token,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TokenCreateRequest is the options structure for creating a token.
|
||||
type TokenCreateRequest struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Policies []string `json:"policies,omitempty"`
|
||||
Metadata map[string]string `json:"meta,omitempty"`
|
||||
Lease string `json:"lease,omitempty"`
|
||||
TTL string `json:"ttl,omitempty"`
|
||||
ExplicitMaxTTL string `json:"explicit_max_ttl,omitempty"`
|
||||
Period string `json:"period,omitempty"`
|
||||
NoParent bool `json:"no_parent,omitempty"`
|
||||
NoDefaultPolicy bool `json:"no_default_policy,omitempty"`
|
||||
DisplayName string `json:"display_name"`
|
||||
NumUses int `json:"num_uses"`
|
||||
Renewable *bool `json:"renewable,omitempty"`
|
||||
}
|
||||
+725
@@ -0,0 +1,725 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
retryablehttp "github.com/hashicorp/go-retryablehttp"
|
||||
"github.com/hashicorp/go-rootcerts"
|
||||
"github.com/hashicorp/vault/helper/parseutil"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
const EnvVaultAddress = "VAULT_ADDR"
|
||||
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"
|
||||
const EnvVaultMFA = "VAULT_MFA"
|
||||
const EnvRateLimit = "VAULT_RATE_LIMIT"
|
||||
|
||||
// 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.
|
||||
// "15s", or simply "15"). The path will not begin with "/v1/" or "v1/" or "/",
|
||||
// however, end-of-path forward slashes are not trimmed, so must match your
|
||||
// called path precisely.
|
||||
type WrappingLookupFunc func(operation, path string) string
|
||||
|
||||
// Config is used to configure the creation of the client.
|
||||
type Config struct {
|
||||
modifyLock sync.RWMutex
|
||||
|
||||
// Address is the address of the Vault server. This should be a complete
|
||||
// URL such as "http://vault.example.com". If you need a custom SSL
|
||||
// cert or want to enable insecure mode, you need to specify a custom
|
||||
// HttpClient.
|
||||
Address string
|
||||
|
||||
// HttpClient is the HTTP client to use. Vault sets sane defaults for the
|
||||
// http.Client and its associated http.Transport created in DefaultConfig.
|
||||
// If you must modify Vault's defaults, it is suggested that you start with
|
||||
// that client and modify as needed rather than start with an empty client
|
||||
// (or http.DefaultClient).
|
||||
HttpClient *http.Client
|
||||
|
||||
// MaxRetries controls the maximum number of times to retry when a 5xx
|
||||
// error occurs. Set to 0 to disable retrying. Defaults to 2 (for a total
|
||||
// of three tries).
|
||||
MaxRetries int
|
||||
|
||||
// Timeout is for setting custom timeout parameter in the HttpClient
|
||||
Timeout time.Duration
|
||||
|
||||
// If there is an error when creating the configuration, this will be the
|
||||
// error
|
||||
Error error
|
||||
|
||||
// The Backoff function to use; a default is used if not provided
|
||||
Backoff retryablehttp.Backoff
|
||||
|
||||
// Limiter is the rate limiter used by the client.
|
||||
// If this pointer is nil, then there will be no limit set.
|
||||
// In contrast, if this pointer is set, even to an empty struct,
|
||||
// then that limiter will be used. Note that an empty Limiter
|
||||
// is equivalent blocking all events.
|
||||
Limiter *rate.Limiter
|
||||
}
|
||||
|
||||
// TLSConfig contains the parameters needed to configure TLS on the HTTP client
|
||||
// used to communicate with Vault.
|
||||
type TLSConfig struct {
|
||||
// CACert is the path to a PEM-encoded CA cert file to use to verify the
|
||||
// Vault server SSL certificate.
|
||||
CACert string
|
||||
|
||||
// CAPath is the path to a directory of PEM-encoded CA cert files to verify
|
||||
// the Vault server SSL certificate.
|
||||
CAPath string
|
||||
|
||||
// ClientCert is the path to the certificate for Vault communication
|
||||
ClientCert string
|
||||
|
||||
// ClientKey is the path to the private key for Vault communication
|
||||
ClientKey string
|
||||
|
||||
// TLSServerName, if set, is used to set the SNI host when connecting via
|
||||
// TLS.
|
||||
TLSServerName string
|
||||
|
||||
// Insecure enables or disables SSL verification
|
||||
Insecure bool
|
||||
}
|
||||
|
||||
// DefaultConfig returns a default configuration for the client. It is
|
||||
// safe to modify the return value of this function.
|
||||
//
|
||||
// The default Address is https://127.0.0.1:8200, but this can be overridden by
|
||||
// setting the `VAULT_ADDR` environment variable.
|
||||
//
|
||||
// If an error is encountered, this will return nil.
|
||||
func DefaultConfig() *Config {
|
||||
config := &Config{
|
||||
Address: "https://127.0.0.1:8200",
|
||||
HttpClient: cleanhttp.DefaultClient(),
|
||||
}
|
||||
config.HttpClient.Timeout = time.Second * 60
|
||||
|
||||
transport := config.HttpClient.Transport.(*http.Transport)
|
||||
transport.TLSHandshakeTimeout = 10 * time.Second
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
if err := http2.ConfigureTransport(transport); err != nil {
|
||||
config.Error = err
|
||||
return config
|
||||
}
|
||||
|
||||
if err := config.ReadEnvironment(); err != nil {
|
||||
config.Error = err
|
||||
return config
|
||||
}
|
||||
|
||||
// Ensure redirects are not automatically followed
|
||||
// Note that this is sane for the API client as it has its own
|
||||
// redirect handling logic (and thus also for command/meta),
|
||||
// but in e.g. http_test actual redirect handling is necessary
|
||||
config.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 to nil out the error. Otherwise retry clients may
|
||||
// try three times on every redirect because it sees an error from this
|
||||
// function (to prevent redirects) passing through to it.
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
|
||||
config.Backoff = retryablehttp.LinearJitterBackoff
|
||||
config.MaxRetries = 2
|
||||
|
||||
return 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 {
|
||||
c.HttpClient = DefaultConfig().HttpClient
|
||||
}
|
||||
clientTLSConfig := c.HttpClient.Transport.(*http.Transport).TLSClientConfig
|
||||
|
||||
var clientCert tls.Certificate
|
||||
foundClientCert := false
|
||||
|
||||
switch {
|
||||
case t.ClientCert != "" && t.ClientKey != "":
|
||||
var err error
|
||||
clientCert, err = tls.LoadX509KeyPair(t.ClientCert, t.ClientKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
foundClientCert = true
|
||||
case t.ClientCert != "" || t.ClientKey != "":
|
||||
return fmt.Errorf("both client cert and client key must be provided")
|
||||
}
|
||||
|
||||
if t.CACert != "" || t.CAPath != "" {
|
||||
rootConfig := &rootcerts.Config{
|
||||
CAFile: t.CACert,
|
||||
CAPath: t.CAPath,
|
||||
}
|
||||
if err := rootcerts.ConfigureTLS(clientTLSConfig, rootConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if t.Insecure {
|
||||
clientTLSConfig.InsecureSkipVerify = true
|
||||
}
|
||||
|
||||
if foundClientCert {
|
||||
// We use this function to ignore the server's preferential list of
|
||||
// CAs, otherwise any CA used for the cert auth backend must be in the
|
||||
// server's CA pool
|
||||
clientTLSConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||
return &clientCert, nil
|
||||
}
|
||||
}
|
||||
|
||||
if t.TLSServerName != "" {
|
||||
clientTLSConfig.ServerName = t.TLSServerName
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadEnvironment reads configuration information from the environment. If
|
||||
// there is an error, no configuration value is updated.
|
||||
func (c *Config) ReadEnvironment() error {
|
||||
var envAddress string
|
||||
var envCACert string
|
||||
var envCAPath string
|
||||
var envClientCert string
|
||||
var envClientKey string
|
||||
var envClientTimeout time.Duration
|
||||
var envInsecure bool
|
||||
var envTLSServerName string
|
||||
var envMaxRetries *uint64
|
||||
var limit *rate.Limiter
|
||||
|
||||
// Parse the environment variables
|
||||
if v := os.Getenv(EnvVaultAddress); v != "" {
|
||||
envAddress = v
|
||||
}
|
||||
if v := os.Getenv(EnvVaultMaxRetries); v != "" {
|
||||
maxRetries, err := strconv.ParseUint(v, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
envMaxRetries = &maxRetries
|
||||
}
|
||||
if v := os.Getenv(EnvVaultCACert); v != "" {
|
||||
envCACert = v
|
||||
}
|
||||
if v := os.Getenv(EnvVaultCAPath); v != "" {
|
||||
envCAPath = v
|
||||
}
|
||||
if v := os.Getenv(EnvVaultClientCert); v != "" {
|
||||
envClientCert = v
|
||||
}
|
||||
if v := os.Getenv(EnvVaultClientKey); v != "" {
|
||||
envClientKey = v
|
||||
}
|
||||
if v := os.Getenv(EnvRateLimit); v != "" {
|
||||
rateLimit, burstLimit, err := parseRateLimit(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
limit = rate.NewLimiter(rate.Limit(rateLimit), burstLimit)
|
||||
}
|
||||
if t := os.Getenv(EnvVaultClientTimeout); t != "" {
|
||||
clientTimeout, err := parseutil.ParseDurationSecond(t)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse %q", EnvVaultClientTimeout)
|
||||
}
|
||||
envClientTimeout = clientTimeout
|
||||
}
|
||||
if v := os.Getenv(EnvVaultInsecure); v != "" {
|
||||
var err error
|
||||
envInsecure, err = strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse VAULT_SKIP_VERIFY")
|
||||
}
|
||||
}
|
||||
if v := os.Getenv(EnvVaultTLSServerName); v != "" {
|
||||
envTLSServerName = v
|
||||
}
|
||||
|
||||
// Configure the HTTP clients TLS configuration.
|
||||
t := &TLSConfig{
|
||||
CACert: envCACert,
|
||||
CAPath: envCAPath,
|
||||
ClientCert: envClientCert,
|
||||
ClientKey: envClientKey,
|
||||
TLSServerName: envTLSServerName,
|
||||
Insecure: envInsecure,
|
||||
}
|
||||
|
||||
c.modifyLock.Lock()
|
||||
defer c.modifyLock.Unlock()
|
||||
|
||||
c.Limiter = limit
|
||||
|
||||
if err := c.ConfigureTLS(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if envAddress != "" {
|
||||
c.Address = envAddress
|
||||
}
|
||||
|
||||
if envMaxRetries != nil {
|
||||
c.MaxRetries = int(*envMaxRetries)
|
||||
}
|
||||
|
||||
if envClientTimeout != 0 {
|
||||
c.Timeout = envClientTimeout
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseRateLimit(val string) (rate float64, burst int, err error) {
|
||||
|
||||
_, err = fmt.Sscanf(val, "%f:%d", &rate, &burst)
|
||||
if err != nil {
|
||||
rate, err = strconv.ParseFloat(val, 64)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%v was provided but incorrectly formatted", EnvRateLimit)
|
||||
}
|
||||
burst = int(rate)
|
||||
}
|
||||
|
||||
return rate, burst, err
|
||||
|
||||
}
|
||||
|
||||
// Client is the client to the Vault API. Create a client with NewClient.
|
||||
type Client struct {
|
||||
modifyLock sync.RWMutex
|
||||
addr *url.URL
|
||||
config *Config
|
||||
token string
|
||||
headers http.Header
|
||||
wrappingLookupFunc WrappingLookupFunc
|
||||
mfaCreds []string
|
||||
policyOverride bool
|
||||
}
|
||||
|
||||
// NewClient returns a new client for the given configuration.
|
||||
//
|
||||
// If the configuration is nil, Vault will use configuration from
|
||||
// DefaultConfig(), which is the recommended starting configuration.
|
||||
//
|
||||
// If the environment variable `VAULT_TOKEN` is present, the token will be
|
||||
// automatically added to the client. Otherwise, you must manually call
|
||||
// `SetToken()`.
|
||||
func NewClient(c *Config) (*Client, error) {
|
||||
def := DefaultConfig()
|
||||
if def == nil {
|
||||
return nil, fmt.Errorf("could not create/read default configuration")
|
||||
}
|
||||
if def.Error != nil {
|
||||
return nil, errwrap.Wrapf("error encountered setting up default configuration: {{err}}", def.Error)
|
||||
}
|
||||
|
||||
if c == nil {
|
||||
c = def
|
||||
}
|
||||
|
||||
c.modifyLock.Lock()
|
||||
defer c.modifyLock.Unlock()
|
||||
|
||||
u, err := url.Parse(c.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.HttpClient == nil {
|
||||
c.HttpClient = def.HttpClient
|
||||
}
|
||||
if c.HttpClient.Transport == nil {
|
||||
c.HttpClient.Transport = def.HttpClient.Transport
|
||||
}
|
||||
|
||||
client := &Client{
|
||||
addr: u,
|
||||
config: c,
|
||||
}
|
||||
|
||||
if token := os.Getenv(EnvVaultToken); token != "" {
|
||||
client.token = token
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// Sets the address of Vault in the client. The format of address should be
|
||||
// "<Scheme>://<Host>:<Port>". Setting this on a client will override the
|
||||
// value of VAULT_ADDR environment variable.
|
||||
func (c *Client) SetAddress(addr string) error {
|
||||
c.modifyLock.Lock()
|
||||
defer c.modifyLock.Unlock()
|
||||
|
||||
parsedAddr, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("failed to set address: {{err}}", err)
|
||||
}
|
||||
|
||||
c.addr = parsedAddr
|
||||
return nil
|
||||
}
|
||||
|
||||
// Address returns the Vault URL the client is configured to connect to
|
||||
func (c *Client) Address() string {
|
||||
c.modifyLock.RLock()
|
||||
defer c.modifyLock.RUnlock()
|
||||
|
||||
return c.addr.String()
|
||||
}
|
||||
|
||||
// SetLimiter will set the rate limiter for this client.
|
||||
// This method is thread-safe.
|
||||
// rateLimit and burst are specified according to https://godoc.org/golang.org/x/time/rate#NewLimiter
|
||||
func (c *Client) SetLimiter(rateLimit float64, burst int) {
|
||||
c.modifyLock.RLock()
|
||||
c.config.modifyLock.Lock()
|
||||
defer c.config.modifyLock.Unlock()
|
||||
c.modifyLock.RUnlock()
|
||||
|
||||
c.config.Limiter = rate.NewLimiter(rate.Limit(rateLimit), burst)
|
||||
}
|
||||
|
||||
// SetMaxRetries sets the number of retries that will be used in the case of certain errors
|
||||
func (c *Client) SetMaxRetries(retries int) {
|
||||
c.modifyLock.RLock()
|
||||
c.config.modifyLock.Lock()
|
||||
defer c.config.modifyLock.Unlock()
|
||||
c.modifyLock.RUnlock()
|
||||
|
||||
c.config.MaxRetries = retries
|
||||
}
|
||||
|
||||
// SetClientTimeout sets the client request timeout
|
||||
func (c *Client) SetClientTimeout(timeout time.Duration) {
|
||||
c.modifyLock.RLock()
|
||||
c.config.modifyLock.Lock()
|
||||
defer c.config.modifyLock.Unlock()
|
||||
c.modifyLock.RUnlock()
|
||||
|
||||
c.config.Timeout = timeout
|
||||
}
|
||||
|
||||
// CurrentWrappingLookupFunc sets a lookup function that returns desired wrap TTLs
|
||||
// for a given operation and path
|
||||
func (c *Client) CurrentWrappingLookupFunc() WrappingLookupFunc {
|
||||
c.modifyLock.RLock()
|
||||
defer c.modifyLock.RUnlock()
|
||||
|
||||
return c.wrappingLookupFunc
|
||||
}
|
||||
|
||||
// SetWrappingLookupFunc sets a lookup function that returns desired wrap TTLs
|
||||
// for a given operation and path
|
||||
func (c *Client) SetWrappingLookupFunc(lookupFunc WrappingLookupFunc) {
|
||||
c.modifyLock.Lock()
|
||||
defer c.modifyLock.Unlock()
|
||||
|
||||
c.wrappingLookupFunc = lookupFunc
|
||||
}
|
||||
|
||||
// SetMFACreds sets the MFA credentials supplied either via the environment
|
||||
// variable or via the command line.
|
||||
func (c *Client) SetMFACreds(creds []string) {
|
||||
c.modifyLock.Lock()
|
||||
defer c.modifyLock.Unlock()
|
||||
|
||||
c.mfaCreds = creds
|
||||
}
|
||||
|
||||
// Token returns the access token being used by this client. It will
|
||||
// return the empty string if there is no token set.
|
||||
func (c *Client) Token() string {
|
||||
c.modifyLock.RLock()
|
||||
defer c.modifyLock.RUnlock()
|
||||
|
||||
return c.token
|
||||
}
|
||||
|
||||
// SetToken sets the token directly. This won't perform any auth
|
||||
// verification, it simply sets the token properly for future requests.
|
||||
func (c *Client) SetToken(v string) {
|
||||
c.modifyLock.Lock()
|
||||
defer c.modifyLock.Unlock()
|
||||
|
||||
c.token = v
|
||||
}
|
||||
|
||||
// ClearToken deletes the token if it is set or does nothing otherwise.
|
||||
func (c *Client) ClearToken() {
|
||||
c.modifyLock.Lock()
|
||||
defer c.modifyLock.Unlock()
|
||||
|
||||
c.token = ""
|
||||
}
|
||||
|
||||
// SetHeaders sets the headers to be used for future requests.
|
||||
func (c *Client) SetHeaders(headers http.Header) {
|
||||
c.modifyLock.Lock()
|
||||
defer c.modifyLock.Unlock()
|
||||
|
||||
c.headers = headers
|
||||
}
|
||||
|
||||
// SetBackoff sets the backoff function to be used for future requests.
|
||||
func (c *Client) SetBackoff(backoff retryablehttp.Backoff) {
|
||||
c.modifyLock.RLock()
|
||||
c.config.modifyLock.Lock()
|
||||
defer c.config.modifyLock.Unlock()
|
||||
c.modifyLock.RUnlock()
|
||||
|
||||
c.config.Backoff = backoff
|
||||
}
|
||||
|
||||
// Clone creates a new client with the same configuration. Note that the same
|
||||
// underlying http.Client is used; modifying the client from more than one
|
||||
// goroutine at once may not be safe, so modify the client as needed and then
|
||||
// clone.
|
||||
func (c *Client) Clone() (*Client, error) {
|
||||
c.modifyLock.RLock()
|
||||
c.config.modifyLock.RLock()
|
||||
config := c.config
|
||||
c.modifyLock.RUnlock()
|
||||
|
||||
newConfig := &Config{
|
||||
Address: config.Address,
|
||||
HttpClient: config.HttpClient,
|
||||
MaxRetries: config.MaxRetries,
|
||||
Timeout: config.Timeout,
|
||||
Backoff: config.Backoff,
|
||||
Limiter: config.Limiter,
|
||||
}
|
||||
config.modifyLock.RUnlock()
|
||||
|
||||
return NewClient(newConfig)
|
||||
}
|
||||
|
||||
// SetPolicyOverride sets whether requests should be sent with the policy
|
||||
// override flag to request overriding soft-mandatory Sentinel policies (both
|
||||
// RGPs and EGPs)
|
||||
func (c *Client) SetPolicyOverride(override bool) {
|
||||
c.modifyLock.Lock()
|
||||
defer c.modifyLock.Unlock()
|
||||
|
||||
c.policyOverride = override
|
||||
}
|
||||
|
||||
// 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, requestPath string) *Request {
|
||||
c.modifyLock.RLock()
|
||||
addr := c.addr
|
||||
token := c.token
|
||||
mfaCreds := c.mfaCreds
|
||||
wrappingLookupFunc := c.wrappingLookupFunc
|
||||
headers := c.headers
|
||||
policyOverride := c.policyOverride
|
||||
c.modifyLock.RUnlock()
|
||||
|
||||
// if SRV records exist (see https://tools.ietf.org/html/draft-andrews-http-srv-02), lookup the SRV
|
||||
// record and take the highest match; this is not designed for high-availability, just discovery
|
||||
var host string = addr.Host
|
||||
if addr.Port() == "" {
|
||||
// Internet Draft specifies that the SRV record is ignored if a port is given
|
||||
_, addrs, err := net.LookupSRV("http", "tcp", addr.Hostname())
|
||||
if err == nil && len(addrs) > 0 {
|
||||
host = fmt.Sprintf("%s:%d", addrs[0].Target, addrs[0].Port)
|
||||
}
|
||||
}
|
||||
|
||||
req := &Request{
|
||||
Method: method,
|
||||
URL: &url.URL{
|
||||
User: addr.User,
|
||||
Scheme: addr.Scheme,
|
||||
Host: host,
|
||||
Path: path.Join(addr.Path, requestPath),
|
||||
},
|
||||
ClientToken: token,
|
||||
Params: make(map[string][]string),
|
||||
}
|
||||
|
||||
var lookupPath string
|
||||
switch {
|
||||
case strings.HasPrefix(requestPath, "/v1/"):
|
||||
lookupPath = strings.TrimPrefix(requestPath, "/v1/")
|
||||
case strings.HasPrefix(requestPath, "v1/"):
|
||||
lookupPath = strings.TrimPrefix(requestPath, "v1/")
|
||||
default:
|
||||
lookupPath = requestPath
|
||||
}
|
||||
|
||||
req.MFAHeaderVals = mfaCreds
|
||||
|
||||
if wrappingLookupFunc != nil {
|
||||
req.WrapTTL = wrappingLookupFunc(method, lookupPath)
|
||||
} else {
|
||||
req.WrapTTL = DefaultWrappingLookupFunc(method, lookupPath)
|
||||
}
|
||||
|
||||
if headers != nil {
|
||||
req.Headers = headers
|
||||
}
|
||||
|
||||
req.PolicyOverride = policyOverride
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
// RawRequest performs the raw request given. This request may be against
|
||||
// a Vault server not configured with this client. This is an advanced operation
|
||||
// that generally won't need to be called externally.
|
||||
func (c *Client) RawRequest(r *Request) (*Response, error) {
|
||||
return c.RawRequestWithContext(context.Background(), r)
|
||||
}
|
||||
|
||||
// RawRequestWithContext performs the raw request given. This request may be against
|
||||
// a Vault server not configured with this client. This is an advanced operation
|
||||
// that generally won't need to be called externally.
|
||||
func (c *Client) RawRequestWithContext(ctx context.Context, r *Request) (*Response, error) {
|
||||
c.modifyLock.RLock()
|
||||
token := c.token
|
||||
|
||||
c.config.modifyLock.RLock()
|
||||
limiter := c.config.Limiter
|
||||
maxRetries := c.config.MaxRetries
|
||||
backoff := c.config.Backoff
|
||||
httpClient := c.config.HttpClient
|
||||
timeout := c.config.Timeout
|
||||
c.config.modifyLock.RUnlock()
|
||||
|
||||
c.modifyLock.RUnlock()
|
||||
|
||||
if limiter != nil {
|
||||
limiter.Wait(ctx)
|
||||
}
|
||||
|
||||
// Sanity check the token before potentially erroring from the API
|
||||
idx := strings.IndexFunc(token, func(c rune) bool {
|
||||
return !unicode.IsPrint(c)
|
||||
})
|
||||
if idx != -1 {
|
||||
return nil, fmt.Errorf("configured Vault token contains non-printable characters and cannot be used")
|
||||
}
|
||||
|
||||
redirectCount := 0
|
||||
START:
|
||||
req, err := r.toRetryableHTTP()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req == nil {
|
||||
return nil, fmt.Errorf("nil request created")
|
||||
}
|
||||
|
||||
if timeout != 0 {
|
||||
ctx, _ = context.WithTimeout(ctx, timeout)
|
||||
}
|
||||
req.Request = req.Request.WithContext(ctx)
|
||||
|
||||
if backoff == nil {
|
||||
backoff = retryablehttp.LinearJitterBackoff
|
||||
}
|
||||
|
||||
client := &retryablehttp.Client{
|
||||
HTTPClient: httpClient,
|
||||
RetryWaitMin: 1000 * time.Millisecond,
|
||||
RetryWaitMax: 1500 * time.Millisecond,
|
||||
RetryMax: maxRetries,
|
||||
CheckRetry: retryablehttp.DefaultRetryPolicy,
|
||||
Backoff: backoff,
|
||||
ErrorHandler: retryablehttp.PassthroughErrorHandler,
|
||||
}
|
||||
|
||||
var result *Response
|
||||
resp, err := client.Do(req)
|
||||
if resp != nil {
|
||||
result = &Response{Response: resp}
|
||||
}
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "tls: oversized") {
|
||||
err = errwrap.Wrapf(
|
||||
"{{err}}\n\n"+
|
||||
"This error usually means that the server is running with TLS disabled\n"+
|
||||
"but the client is configured to use TLS. Please either enable TLS\n"+
|
||||
"on the server or run the client with -address set to an address\n"+
|
||||
"that uses the http protocol:\n\n"+
|
||||
" vault <command> -address http://<address>\n\n"+
|
||||
"You can also set the VAULT_ADDR environment variable:\n\n\n"+
|
||||
" VAULT_ADDR=http://<address> vault <command>\n\n"+
|
||||
"where <address> is replaced by the actual address to the server.",
|
||||
err)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Check for a redirect, only allowing for a single redirect
|
||||
if (resp.StatusCode == 301 || resp.StatusCode == 302 || resp.StatusCode == 307) && redirectCount == 0 {
|
||||
// Parse the updated location
|
||||
respLoc, err := resp.Location()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Ensure a protocol downgrade doesn't happen
|
||||
if req.URL.Scheme == "https" && respLoc.Scheme != "https" {
|
||||
return result, fmt.Errorf("redirect would cause protocol downgrade")
|
||||
}
|
||||
|
||||
// Update the request
|
||||
r.URL = respLoc
|
||||
|
||||
// Reset the request body if any
|
||||
if err := r.ResetJSONBody(); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Retry the request
|
||||
redirectCount++
|
||||
goto START
|
||||
}
|
||||
|
||||
if err := result.Error(); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Help reads the help information for the given path.
|
||||
func (c *Client) Help(path string) (*Help, error) {
|
||||
r := c.NewRequest("GET", fmt.Sprintf("/v1/%s", path))
|
||||
r.Params.Add("help", "1")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result Help
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
type Help struct {
|
||||
Help string `json:"help"`
|
||||
SeeAlso []string `json:"see_also"`
|
||||
}
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/vault/helper/jsonutil"
|
||||
)
|
||||
|
||||
const (
|
||||
wrappedResponseLocation = "cubbyhole/response"
|
||||
)
|
||||
|
||||
var (
|
||||
// The default TTL that will be used with `sys/wrapping/wrap`, can be
|
||||
// changed
|
||||
DefaultWrappingTTL = "5m"
|
||||
|
||||
// The default function used if no other function is set, which honors the
|
||||
// env var and wraps `sys/wrapping/wrap`
|
||||
DefaultWrappingLookupFunc = func(operation, path string) string {
|
||||
if os.Getenv(EnvVaultWrapTTL) != "" {
|
||||
return os.Getenv(EnvVaultWrapTTL)
|
||||
}
|
||||
|
||||
if (operation == "PUT" || operation == "POST") && path == "sys/wrapping/wrap" {
|
||||
return DefaultWrappingTTL
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
)
|
||||
|
||||
// Logical is used to perform logical backend operations on Vault.
|
||||
type Logical struct {
|
||||
c *Client
|
||||
}
|
||||
|
||||
// Logical is used to return the client for logical-backend API calls.
|
||||
func (c *Client) Logical() *Logical {
|
||||
return &Logical{c: c}
|
||||
}
|
||||
|
||||
func (c *Logical) Read(path string) (*Secret, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/"+path)
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if resp != nil && resp.StatusCode == 404 {
|
||||
secret, parseErr := ParseSecret(resp.Body)
|
||||
switch parseErr {
|
||||
case nil:
|
||||
case io.EOF:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
if secret != nil && (len(secret.Warnings) > 0 || len(secret.Data) > 0) {
|
||||
return secret, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *Logical) List(path string) (*Secret, error) {
|
||||
r := c.c.NewRequest("LIST", "/v1/"+path)
|
||||
// Set this for broader compatibility, but we use LIST above to be able to
|
||||
// handle the wrapping lookup function
|
||||
r.Method = "GET"
|
||||
r.Params.Set("list", "true")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if resp != nil && resp.StatusCode == 404 {
|
||||
secret, parseErr := ParseSecret(resp.Body)
|
||||
switch parseErr {
|
||||
case nil:
|
||||
case io.EOF:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
if secret != nil && (len(secret.Warnings) > 0 || len(secret.Data) > 0) {
|
||||
return secret, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *Logical) Write(path string, data map[string]interface{}) (*Secret, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/"+path)
|
||||
if err := r.SetJSONBody(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if resp != nil && resp.StatusCode == 404 {
|
||||
secret, parseErr := ParseSecret(resp.Body)
|
||||
switch parseErr {
|
||||
case nil:
|
||||
case io.EOF:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
if secret != nil && (len(secret.Warnings) > 0 || len(secret.Data) > 0) {
|
||||
return secret, err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *Logical) Delete(path string) (*Secret, error) {
|
||||
r := c.c.NewRequest("DELETE", "/v1/"+path)
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if resp != nil && resp.StatusCode == 404 {
|
||||
secret, parseErr := ParseSecret(resp.Body)
|
||||
switch parseErr {
|
||||
case nil:
|
||||
case io.EOF:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
if secret != nil && (len(secret.Warnings) > 0 || len(secret.Data) > 0) {
|
||||
return secret, err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *Logical) Unwrap(wrappingToken string) (*Secret, error) {
|
||||
var data map[string]interface{}
|
||||
if wrappingToken != "" {
|
||||
if c.c.Token() == "" {
|
||||
c.c.SetToken(wrappingToken)
|
||||
} else if wrappingToken != c.c.Token() {
|
||||
data = map[string]interface{}{
|
||||
"token": wrappingToken,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/wrapping/unwrap")
|
||||
if err := r.SetJSONBody(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
if resp == nil || resp.StatusCode != 404 {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
// In the 404 case this may actually be a wrapped 404 error
|
||||
secret, parseErr := ParseSecret(resp.Body)
|
||||
switch parseErr {
|
||||
case nil:
|
||||
case io.EOF:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
if secret != nil && (len(secret.Warnings) > 0 || len(secret.Data) > 0) {
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
// Otherwise this might be an old-style wrapping token so attempt the old
|
||||
// method
|
||||
if wrappingToken != "" {
|
||||
origToken := c.c.Token()
|
||||
defer c.c.SetToken(origToken)
|
||||
c.c.SetToken(wrappingToken)
|
||||
}
|
||||
|
||||
secret, err = c.Read(wrappedResponseLocation)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf(fmt.Sprintf("error reading %q: {{err}}", wrappedResponseLocation), err)
|
||||
}
|
||||
if secret == nil {
|
||||
return nil, fmt.Errorf("no value found at %q", wrappedResponseLocation)
|
||||
}
|
||||
if secret.Data == nil {
|
||||
return nil, fmt.Errorf("\"data\" not found in wrapping response")
|
||||
}
|
||||
if _, ok := secret.Data["response"]; !ok {
|
||||
return nil, fmt.Errorf("\"response\" not found in wrapping response \"data\" map")
|
||||
}
|
||||
|
||||
wrappedSecret := new(Secret)
|
||||
buf := bytes.NewBufferString(secret.Data["response"].(string))
|
||||
if err := jsonutil.DecodeJSONFromReader(buf, wrappedSecret); err != nil {
|
||||
return nil, errwrap.Wrapf("error unmarshalling wrapped secret: {{err}}", err)
|
||||
}
|
||||
|
||||
return wrappedSecret, nil
|
||||
}
|
||||
+349
@@ -0,0 +1,349 @@
|
||||
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")
|
||||
|
||||
// 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
|
||||
increment int
|
||||
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
|
||||
|
||||
// DEPRECATED: this does not do anything.
|
||||
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
|
||||
|
||||
// The new TTL, in seconds, that should be set on the lease. The TTL set
|
||||
// here may or may not be honored by the vault server, based on Vault
|
||||
// configuration or any associated max TTL values.
|
||||
Increment 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
|
||||
}
|
||||
|
||||
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,
|
||||
increment: i.Increment,
|
||||
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
|
||||
// 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()
|
||||
}
|
||||
|
||||
r.doneCh <- result
|
||||
}
|
||||
|
||||
// renewAuth is a helper for renewing authentication.
|
||||
func (r *Renewer) renewAuth() error {
|
||||
if !r.secret.Auth.Renewable || r.secret.Auth.ClientToken == "" {
|
||||
return ErrRenewerNotRenewable
|
||||
}
|
||||
|
||||
priorDuration := time.Duration(r.secret.Auth.LeaseDuration) * time.Second
|
||||
r.calculateGrace(priorDuration)
|
||||
|
||||
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, r.increment)
|
||||
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
|
||||
leaseDuration := time.Duration(renewal.Auth.LeaseDuration) * time.Second
|
||||
|
||||
// We keep evaluating a new grace period so long as the lease is
|
||||
// extending. Once it stops extending, we've hit the max and need to
|
||||
// rely on the grace duration.
|
||||
if leaseDuration > priorDuration {
|
||||
r.calculateGrace(leaseDuration)
|
||||
}
|
||||
priorDuration = leaseDuration
|
||||
|
||||
// The sleep duration is set to 2/3 of the current lease duration plus
|
||||
// 1/3 of the current grace period, which adds jitter.
|
||||
sleepDuration := time.Duration(float64(leaseDuration.Nanoseconds())*2/3 + float64(r.grace.Nanoseconds())/3)
|
||||
|
||||
// If we are within grace, return now; or, if the amount of time we
|
||||
// would sleep would land us in the grace period. This helps with short
|
||||
// tokens; for example, you don't want a current lease duration of 4
|
||||
// seconds, a grace period of 3 seconds, and end up sleeping for more
|
||||
// than three of those seconds and having a very small budget of time
|
||||
// to renew.
|
||||
if leaseDuration <= r.grace || leaseDuration-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
|
||||
}
|
||||
|
||||
priorDuration := time.Duration(r.secret.LeaseDuration) * time.Second
|
||||
r.calculateGrace(priorDuration)
|
||||
|
||||
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, r.increment)
|
||||
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
|
||||
leaseDuration := time.Duration(renewal.LeaseDuration) * time.Second
|
||||
|
||||
// We keep evaluating a new grace period so long as the lease is
|
||||
// extending. Once it stops extending, we've hit the max and need to
|
||||
// rely on the grace duration.
|
||||
if leaseDuration > priorDuration {
|
||||
r.calculateGrace(leaseDuration)
|
||||
}
|
||||
priorDuration = leaseDuration
|
||||
|
||||
// The sleep duration is set to 2/3 of the current lease duration plus
|
||||
// 1/3 of the current grace period, which adds jitter.
|
||||
sleepDuration := time.Duration(float64(leaseDuration.Nanoseconds())*2/3 + float64(r.grace.Nanoseconds())/3)
|
||||
|
||||
// If we are within grace, return now; or, if the amount of time we
|
||||
// would sleep would land us in the grace period. This helps with short
|
||||
// tokens; for example, you don't want a current lease duration of 4
|
||||
// seconds, a grace period of 3 seconds, and end up sleeping for more
|
||||
// than three of those seconds and having a very small budget of time
|
||||
// to renew.
|
||||
if leaseDuration <= r.grace || leaseDuration-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)
|
||||
}
|
||||
|
||||
// calculateGrace calculates the grace period based on a reasonable set of
|
||||
// assumptions given the total lease time; it also adds some jitter to not have
|
||||
// clients be in sync.
|
||||
func (r *Renewer) calculateGrace(leaseDuration time.Duration) {
|
||||
if leaseDuration == 0 {
|
||||
r.grace = 0
|
||||
return
|
||||
}
|
||||
|
||||
leaseNanos := float64(leaseDuration.Nanoseconds())
|
||||
jitterMax := 0.1 * leaseNanos
|
||||
|
||||
// For a given lease duration, we want to allow 80-90% of that to elapse,
|
||||
// so the remaining amount is the grace period
|
||||
r.grace = time.Duration(jitterMax) + time.Duration(uint64(r.random.Int63())%uint64(jitterMax))
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
retryablehttp "github.com/hashicorp/go-retryablehttp"
|
||||
)
|
||||
|
||||
// Request is a raw request configuration structure used to initiate
|
||||
// API requests to the Vault server.
|
||||
type Request struct {
|
||||
Method string
|
||||
URL *url.URL
|
||||
Params url.Values
|
||||
Headers http.Header
|
||||
ClientToken string
|
||||
MFAHeaderVals []string
|
||||
WrapTTL string
|
||||
Obj interface{}
|
||||
|
||||
// When possible, use BodyBytes as it is more efficient due to how the
|
||||
// retry logic works
|
||||
BodyBytes []byte
|
||||
|
||||
// Fallback
|
||||
Body io.Reader
|
||||
BodySize int64
|
||||
|
||||
// Whether to request overriding soft-mandatory Sentinel policies (RGPs and
|
||||
// EGPs). If set, the override flag will take effect for all policies
|
||||
// evaluated during the request.
|
||||
PolicyOverride bool
|
||||
}
|
||||
|
||||
// SetJSONBody is used to set a request body that is a JSON-encoded value.
|
||||
func (r *Request) SetJSONBody(val interface{}) error {
|
||||
buf, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Obj = val
|
||||
r.BodyBytes = buf
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetJSONBody is used to reset the body for a redirect
|
||||
func (r *Request) ResetJSONBody() error {
|
||||
if r.BodyBytes == nil {
|
||||
return nil
|
||||
}
|
||||
return r.SetJSONBody(r.Obj)
|
||||
}
|
||||
|
||||
// DEPRECATED: ToHTTP turns this request into a valid *http.Request for use
|
||||
// with the net/http package.
|
||||
func (r *Request) ToHTTP() (*http.Request, error) {
|
||||
req, err := r.toRetryableHTTP()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case r.BodyBytes == nil && r.Body == nil:
|
||||
// No body
|
||||
|
||||
case r.BodyBytes != nil:
|
||||
req.Request.Body = ioutil.NopCloser(bytes.NewReader(r.BodyBytes))
|
||||
|
||||
default:
|
||||
if c, ok := r.Body.(io.ReadCloser); ok {
|
||||
req.Request.Body = c
|
||||
} else {
|
||||
req.Request.Body = ioutil.NopCloser(r.Body)
|
||||
}
|
||||
}
|
||||
|
||||
return req.Request, nil
|
||||
}
|
||||
|
||||
func (r *Request) toRetryableHTTP() (*retryablehttp.Request, error) {
|
||||
// Encode the query parameters
|
||||
r.URL.RawQuery = r.Params.Encode()
|
||||
|
||||
// Create the HTTP request, defaulting to retryable
|
||||
var req *retryablehttp.Request
|
||||
|
||||
var err error
|
||||
var body interface{}
|
||||
|
||||
switch {
|
||||
case r.BodyBytes == nil && r.Body == nil:
|
||||
// No body
|
||||
|
||||
case r.BodyBytes != nil:
|
||||
// Use bytes, it's more efficient
|
||||
body = r.BodyBytes
|
||||
|
||||
default:
|
||||
body = r.Body
|
||||
}
|
||||
|
||||
req, err = retryablehttp.NewRequest(r.Method, r.URL.RequestURI(), body)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
if len(r.WrapTTL) != 0 {
|
||||
req.Header.Set("X-Vault-Wrap-TTL", r.WrapTTL)
|
||||
}
|
||||
|
||||
if len(r.MFAHeaderVals) != 0 {
|
||||
for _, mfaHeaderVal := range r.MFAHeaderVals {
|
||||
req.Header.Add("X-Vault-MFA", mfaHeaderVal)
|
||||
}
|
||||
}
|
||||
|
||||
if r.PolicyOverride {
|
||||
req.Header.Set("X-Vault-Policy-Override", "true")
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/vault/helper/jsonutil"
|
||||
)
|
||||
|
||||
// Response is a raw response that wraps an HTTP response.
|
||||
type Response struct {
|
||||
*http.Response
|
||||
}
|
||||
|
||||
// DecodeJSON will decode the response body to a JSON structure. This
|
||||
// will consume the response body, but will not close it. Close must
|
||||
// still be called.
|
||||
func (r *Response) DecodeJSON(out interface{}) error {
|
||||
return jsonutil.DecodeJSONFromReader(r.Body, out)
|
||||
}
|
||||
|
||||
// Error returns an error response if there is one. If there is an 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. 429 is the code for health status of
|
||||
// standby nodes.
|
||||
if (r.StatusCode >= 200 && r.StatusCode < 400) || r.StatusCode == 429 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// We have an error. Let's copy the body into our own buffer first,
|
||||
// so that if we can't decode JSON, we can at least copy it raw.
|
||||
bodyBuf := &bytes.Buffer{}
|
||||
if _, err := io.Copy(bodyBuf, r.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Body.Close()
|
||||
r.Body = ioutil.NopCloser(bodyBuf)
|
||||
|
||||
// Decode the error response if we can. Note that we wrap the bodyBuf
|
||||
// in a bytes.Reader here so that the JSON decoder doesn't move the
|
||||
// read pointer for the original buffer.
|
||||
var resp ErrorResponse
|
||||
if err := jsonutil.DecodeJSON(bodyBuf.Bytes(), &resp); err != nil {
|
||||
// Ignore the decoding error and just drop the raw response
|
||||
return fmt.Errorf(
|
||||
"Error making API request.\n\n"+
|
||||
"URL: %s %s\n"+
|
||||
"Code: %d. Raw Message:\n\n%s",
|
||||
r.Request.Method, r.Request.URL.String(),
|
||||
r.StatusCode, bodyBuf.String())
|
||||
}
|
||||
|
||||
var errBody bytes.Buffer
|
||||
errBody.WriteString(fmt.Sprintf(
|
||||
"Error making API request.\n\n"+
|
||||
"URL: %s %s\n"+
|
||||
"Code: %d. Errors:\n\n",
|
||||
r.Request.Method, r.Request.URL.String(),
|
||||
r.StatusCode))
|
||||
for _, err := range resp.Errors {
|
||||
errBody.WriteString(fmt.Sprintf("* %s", err))
|
||||
}
|
||||
|
||||
return fmt.Errorf(errBody.String())
|
||||
}
|
||||
|
||||
// ErrorResponse is the raw structure of errors when they're returned by the
|
||||
// HTTP API.
|
||||
type ErrorResponse struct {
|
||||
Errors []string
|
||||
}
|
||||
+320
@@ -0,0 +1,320 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/vault/helper/jsonutil"
|
||||
"github.com/hashicorp/vault/helper/parseutil"
|
||||
)
|
||||
|
||||
// Secret is the structure returned for every secret within Vault.
|
||||
type Secret struct {
|
||||
// The request ID that generated this response
|
||||
RequestID string `json:"request_id"`
|
||||
|
||||
LeaseID string `json:"lease_id"`
|
||||
LeaseDuration int `json:"lease_duration"`
|
||||
Renewable bool `json:"renewable"`
|
||||
|
||||
// Data is the actual contents of the secret. The format of the data
|
||||
// is arbitrary and up to the secret backend.
|
||||
Data map[string]interface{} `json:"data"`
|
||||
|
||||
// Warnings contains any warnings related to the operation. These
|
||||
// are not issues that caused the command to fail, but that the
|
||||
// client should be aware of.
|
||||
Warnings []string `json:"warnings"`
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// TokenID returns the standardized token ID (token) for the given secret.
|
||||
func (s *Secret) TokenID() (string, error) {
|
||||
if s == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if s.Auth != nil && len(s.Auth.ClientToken) > 0 {
|
||||
return s.Auth.ClientToken, nil
|
||||
}
|
||||
|
||||
if s.Data == nil || s.Data["id"] == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
id, ok := s.Data["id"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("token found but in the wrong format")
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// TokenAccessor returns the standardized token accessor for the given secret.
|
||||
// If the secret is nil or does not contain an accessor, this returns the empty
|
||||
// string.
|
||||
func (s *Secret) TokenAccessor() (string, error) {
|
||||
if s == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if s.Auth != nil && len(s.Auth.Accessor) > 0 {
|
||||
return s.Auth.Accessor, nil
|
||||
}
|
||||
|
||||
if s.Data == nil || s.Data["accessor"] == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
accessor, ok := s.Data["accessor"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("token found but in the wrong format")
|
||||
}
|
||||
|
||||
return accessor, nil
|
||||
}
|
||||
|
||||
// TokenRemainingUses returns the standardized remaining uses for the given
|
||||
// secret. If the secret is nil or does not contain the "num_uses", this
|
||||
// returns -1. On error, this will return -1 and a non-nil error.
|
||||
func (s *Secret) TokenRemainingUses() (int, error) {
|
||||
if s == nil || s.Data == nil || s.Data["num_uses"] == nil {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
uses, err := parseutil.ParseInt(s.Data["num_uses"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(uses), nil
|
||||
}
|
||||
|
||||
// TokenPolicies returns the standardized list of policies for the given secret.
|
||||
// If the secret is nil or does not contain any policies, this returns nil. It
|
||||
// also populates the secret's Auth info with identity/token policy info.
|
||||
func (s *Secret) TokenPolicies() ([]string, error) {
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if s.Auth != nil && len(s.Auth.Policies) > 0 {
|
||||
return s.Auth.Policies, nil
|
||||
}
|
||||
|
||||
if s.Data == nil || s.Data["policies"] == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var tokenPolicies []string
|
||||
|
||||
// Token policies
|
||||
{
|
||||
_, ok := s.Data["policies"]
|
||||
if !ok {
|
||||
goto TOKEN_DONE
|
||||
}
|
||||
|
||||
sList, ok := s.Data["policies"].([]string)
|
||||
if ok {
|
||||
tokenPolicies = sList
|
||||
goto TOKEN_DONE
|
||||
}
|
||||
|
||||
list, ok := s.Data["policies"].([]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to convert token policies to expected format")
|
||||
}
|
||||
for _, v := range list {
|
||||
p, ok := v.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to convert policy %v to string", v)
|
||||
}
|
||||
tokenPolicies = append(tokenPolicies, p)
|
||||
}
|
||||
}
|
||||
|
||||
TOKEN_DONE:
|
||||
var identityPolicies []string
|
||||
|
||||
// Identity policies
|
||||
{
|
||||
_, ok := s.Data["identity_policies"]
|
||||
if !ok {
|
||||
goto DONE
|
||||
}
|
||||
|
||||
sList, ok := s.Data["identity_policies"].([]string)
|
||||
if ok {
|
||||
identityPolicies = sList
|
||||
goto DONE
|
||||
}
|
||||
|
||||
list, ok := s.Data["identity_policies"].([]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to convert identity policies to expected format")
|
||||
}
|
||||
for _, v := range list {
|
||||
p, ok := v.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to convert policy %v to string", v)
|
||||
}
|
||||
identityPolicies = append(identityPolicies, p)
|
||||
}
|
||||
}
|
||||
|
||||
DONE:
|
||||
|
||||
if s.Auth == nil {
|
||||
s.Auth = &SecretAuth{}
|
||||
}
|
||||
|
||||
policies := append(tokenPolicies, identityPolicies...)
|
||||
|
||||
s.Auth.TokenPolicies = tokenPolicies
|
||||
s.Auth.IdentityPolicies = identityPolicies
|
||||
s.Auth.Policies = policies
|
||||
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
// TokenMetadata returns the map of metadata associated with this token, if any
|
||||
// exists. If the secret is nil or does not contain the "metadata" key, this
|
||||
// returns nil.
|
||||
func (s *Secret) TokenMetadata() (map[string]string, error) {
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if s.Auth != nil && len(s.Auth.Metadata) > 0 {
|
||||
return s.Auth.Metadata, nil
|
||||
}
|
||||
|
||||
if s.Data == nil || (s.Data["metadata"] == nil && s.Data["meta"] == nil) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
data, ok := s.Data["metadata"].(map[string]interface{})
|
||||
if !ok {
|
||||
data, ok = s.Data["meta"].(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to convert metadata field to expected format")
|
||||
}
|
||||
}
|
||||
|
||||
metadata := make(map[string]string, len(data))
|
||||
for k, v := range data {
|
||||
typed, ok := v.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to convert metadata value %v to string", v)
|
||||
}
|
||||
metadata[k] = typed
|
||||
}
|
||||
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
// TokenIsRenewable returns the standardized token renewability for the given
|
||||
// secret. If the secret is nil or does not contain the "renewable" key, this
|
||||
// returns false.
|
||||
func (s *Secret) TokenIsRenewable() (bool, error) {
|
||||
if s == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if s.Auth != nil && s.Auth.Renewable {
|
||||
return s.Auth.Renewable, nil
|
||||
}
|
||||
|
||||
if s.Data == nil || s.Data["renewable"] == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
renewable, err := parseutil.ParseBool(s.Data["renewable"])
|
||||
if err != nil {
|
||||
return false, errwrap.Wrapf("could not convert renewable value to a boolean: {{err}}", err)
|
||||
}
|
||||
|
||||
return renewable, nil
|
||||
}
|
||||
|
||||
// TokenTTL returns the standardized remaining token TTL for the given secret.
|
||||
// If the secret is nil or does not contain a TTL, this returns 0.
|
||||
func (s *Secret) TokenTTL() (time.Duration, error) {
|
||||
if s == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if s.Auth != nil && s.Auth.LeaseDuration > 0 {
|
||||
return time.Duration(s.Auth.LeaseDuration) * time.Second, nil
|
||||
}
|
||||
|
||||
if s.Data == nil || s.Data["ttl"] == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
ttl, err := parseutil.ParseDurationSecond(s.Data["ttl"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return ttl, nil
|
||||
}
|
||||
|
||||
// SecretWrapInfo contains wrapping information if we have it. If what is
|
||||
// contained is an authentication token, the accessor for the token will be
|
||||
// available in WrappedAccessor.
|
||||
type SecretWrapInfo struct {
|
||||
Token string `json:"token"`
|
||||
Accessor string `json:"accessor"`
|
||||
TTL int `json:"ttl"`
|
||||
CreationTime time.Time `json:"creation_time"`
|
||||
CreationPath string `json:"creation_path"`
|
||||
WrappedAccessor string `json:"wrapped_accessor"`
|
||||
}
|
||||
|
||||
// SecretAuth is the structure containing auth information if we have it.
|
||||
type SecretAuth struct {
|
||||
ClientToken string `json:"client_token"`
|
||||
Accessor string `json:"accessor"`
|
||||
Policies []string `json:"policies"`
|
||||
TokenPolicies []string `json:"token_policies"`
|
||||
IdentityPolicies []string `json:"identity_policies"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
|
||||
LeaseDuration int `json:"lease_duration"`
|
||||
Renewable bool `json:"renewable"`
|
||||
}
|
||||
|
||||
// ParseSecret is used to parse a secret value from JSON from an io.Reader.
|
||||
func ParseSecret(r io.Reader) (*Secret, error) {
|
||||
// First read the data into a buffer. Not super efficient but we want to
|
||||
// know if we actually have a body or not.
|
||||
var buf bytes.Buffer
|
||||
_, err := buf.ReadFrom(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf.Len() == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// First decode the JSON into a map[string]interface{}
|
||||
var secret Secret
|
||||
if err := jsonutil.DecodeJSONFromReader(&buf, &secret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &secret, nil
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// SSH is used to return a client to invoke operations on SSH backend.
|
||||
type SSH struct {
|
||||
c *Client
|
||||
MountPoint string
|
||||
}
|
||||
|
||||
// SSH returns the client for logical-backend API calls.
|
||||
func (c *Client) SSH() *SSH {
|
||||
return c.SSHWithMountPoint(SSHHelperDefaultMountPoint)
|
||||
}
|
||||
|
||||
// SSHWithMountPoint returns the client with specific SSH mount point.
|
||||
func (c *Client) SSHWithMountPoint(mountPoint string) *SSH {
|
||||
return &SSH{
|
||||
c: c,
|
||||
MountPoint: mountPoint,
|
||||
}
|
||||
}
|
||||
|
||||
// Credential invokes the SSH backend API to create a credential to establish an SSH session.
|
||||
func (c *SSH) Credential(role string, data map[string]interface{}) (*Secret, error) {
|
||||
r := c.c.NewRequest("PUT", fmt.Sprintf("/v1/%s/creds/%s", c.MountPoint, role))
|
||||
if err := r.SetJSONBody(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
// SignKey signs the given public key and returns a signed public key to pass
|
||||
// along with the SSH request.
|
||||
func (c *SSH) SignKey(role string, data map[string]interface{}) (*Secret, error) {
|
||||
r := c.c.NewRequest("PUT", fmt.Sprintf("/v1/%s/sign/%s", c.MountPoint, role))
|
||||
if err := r.SetJSONBody(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/go-rootcerts"
|
||||
"github.com/hashicorp/hcl"
|
||||
"github.com/hashicorp/hcl/hcl/ast"
|
||||
"github.com/hashicorp/vault/helper/hclutil"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
const (
|
||||
// SSHHelperDefaultMountPoint is the default path at which SSH backend will be
|
||||
// mounted in the Vault server.
|
||||
SSHHelperDefaultMountPoint = "ssh"
|
||||
|
||||
// VerifyEchoRequest is the echo request message sent as OTP by the helper.
|
||||
VerifyEchoRequest = "verify-echo-request"
|
||||
|
||||
// VerifyEchoResponse is the echo response message sent as a response to OTP
|
||||
// matching echo request.
|
||||
VerifyEchoResponse = "verify-echo-response"
|
||||
)
|
||||
|
||||
// SSHHelper is a structure representing a vault-ssh-helper which can talk to vault server
|
||||
// in order to verify the OTP entered by the user. It contains the path at which
|
||||
// SSH backend is mounted at the server.
|
||||
type SSHHelper struct {
|
||||
c *Client
|
||||
MountPoint string
|
||||
}
|
||||
|
||||
// SSHVerifyResponse is a structure representing the fields in Vault server's
|
||||
// response.
|
||||
type SSHVerifyResponse struct {
|
||||
// Usually empty. If the request OTP is echo request message, this will
|
||||
// be set to the corresponding echo response message.
|
||||
Message string `json:"message" mapstructure:"message"`
|
||||
|
||||
// Username associated with the OTP
|
||||
Username string `json:"username" mapstructure:"username"`
|
||||
|
||||
// IP associated with the OTP
|
||||
IP string `json:"ip" mapstructure:"ip"`
|
||||
|
||||
// Name of the role against which the OTP was issued
|
||||
RoleName string `json:"role_name" mapstructure:"role_name"`
|
||||
}
|
||||
|
||||
// SSHHelperConfig is a structure which represents the entries from the vault-ssh-helper's configuration file.
|
||||
type SSHHelperConfig struct {
|
||||
VaultAddr string `hcl:"vault_addr"`
|
||||
SSHMountPoint string `hcl:"ssh_mount_point"`
|
||||
CACert string `hcl:"ca_cert"`
|
||||
CAPath string `hcl:"ca_path"`
|
||||
AllowedCidrList string `hcl:"allowed_cidr_list"`
|
||||
AllowedRoles string `hcl:"allowed_roles"`
|
||||
TLSSkipVerify bool `hcl:"tls_skip_verify"`
|
||||
TLSServerName string `hcl:"tls_server_name"`
|
||||
}
|
||||
|
||||
// SetTLSParameters sets the TLS parameters for this SSH agent.
|
||||
func (c *SSHHelperConfig) SetTLSParameters(clientConfig *Config, certPool *x509.CertPool) {
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: c.TLSSkipVerify,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
RootCAs: certPool,
|
||||
ServerName: c.TLSServerName,
|
||||
}
|
||||
|
||||
transport := cleanhttp.DefaultTransport()
|
||||
transport.TLSClientConfig = tlsConfig
|
||||
clientConfig.HttpClient.Transport = transport
|
||||
}
|
||||
|
||||
// Returns true if any of the following conditions are true:
|
||||
// * CA cert is configured
|
||||
// * CA path is configured
|
||||
// * configured to skip certificate verification
|
||||
// * TLS server name is configured
|
||||
//
|
||||
func (c *SSHHelperConfig) shouldSetTLSParameters() bool {
|
||||
return c.CACert != "" || c.CAPath != "" || c.TLSServerName != "" || c.TLSSkipVerify
|
||||
}
|
||||
|
||||
// NewClient returns a new client for the configuration. This client will be used by the
|
||||
// vault-ssh-helper to communicate with Vault server and verify the OTP entered by user.
|
||||
// If the configuration supplies Vault SSL certificates, then the client will
|
||||
// have TLS configured in its transport.
|
||||
func (c *SSHHelperConfig) NewClient() (*Client, error) {
|
||||
// Creating a default client configuration for communicating with vault server.
|
||||
clientConfig := DefaultConfig()
|
||||
|
||||
// Pointing the client to the actual address of vault server.
|
||||
clientConfig.Address = c.VaultAddr
|
||||
|
||||
// Check if certificates are provided via config file.
|
||||
if c.shouldSetTLSParameters() {
|
||||
rootConfig := &rootcerts.Config{
|
||||
CAFile: c.CACert,
|
||||
CAPath: c.CAPath,
|
||||
}
|
||||
certPool, err := rootcerts.LoadCACerts(rootConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Enable TLS on the HTTP client information
|
||||
c.SetTLSParameters(clientConfig, certPool)
|
||||
}
|
||||
|
||||
// Creating the client object for the given configuration
|
||||
client, err := NewClient(clientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// LoadSSHHelperConfig loads ssh-helper's configuration from the file and populates the corresponding
|
||||
// in-memory structure.
|
||||
//
|
||||
// Vault address is a required parameter.
|
||||
// Mount point defaults to "ssh".
|
||||
func LoadSSHHelperConfig(path string) (*SSHHelperConfig, error) {
|
||||
contents, err := ioutil.ReadFile(path)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, multierror.Prefix(err, "ssh_helper:")
|
||||
}
|
||||
return ParseSSHHelperConfig(string(contents))
|
||||
}
|
||||
|
||||
// ParseSSHHelperConfig parses the given contents as a string for the SSHHelper
|
||||
// configuration.
|
||||
func ParseSSHHelperConfig(contents string) (*SSHHelperConfig, error) {
|
||||
root, err := hcl.Parse(string(contents))
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("error parsing config: {{err}}", err)
|
||||
}
|
||||
|
||||
list, ok := root.Node.(*ast.ObjectList)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error parsing config: file doesn't contain a root object")
|
||||
}
|
||||
|
||||
valid := []string{
|
||||
"vault_addr",
|
||||
"ssh_mount_point",
|
||||
"ca_cert",
|
||||
"ca_path",
|
||||
"allowed_cidr_list",
|
||||
"allowed_roles",
|
||||
"tls_skip_verify",
|
||||
"tls_server_name",
|
||||
}
|
||||
if err := hclutil.CheckHCLKeys(list, valid); err != nil {
|
||||
return nil, multierror.Prefix(err, "ssh_helper:")
|
||||
}
|
||||
|
||||
var c SSHHelperConfig
|
||||
c.SSHMountPoint = SSHHelperDefaultMountPoint
|
||||
if err := hcl.DecodeObject(&c, list); err != nil {
|
||||
return nil, multierror.Prefix(err, "ssh_helper:")
|
||||
}
|
||||
|
||||
if c.VaultAddr == "" {
|
||||
return nil, fmt.Errorf(`missing config "vault_addr"`)
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
// SSHHelper creates an SSHHelper object which can talk to Vault server with SSH backend
|
||||
// mounted at default path ("ssh").
|
||||
func (c *Client) SSHHelper() *SSHHelper {
|
||||
return c.SSHHelperWithMountPoint(SSHHelperDefaultMountPoint)
|
||||
}
|
||||
|
||||
// SSHHelperWithMountPoint creates an SSHHelper object which can talk to Vault server with SSH backend
|
||||
// mounted at a specific mount point.
|
||||
func (c *Client) SSHHelperWithMountPoint(mountPoint string) *SSHHelper {
|
||||
return &SSHHelper{
|
||||
c: c,
|
||||
MountPoint: mountPoint,
|
||||
}
|
||||
}
|
||||
|
||||
// Verify verifies if the key provided by user is present in Vault server. The response
|
||||
// will contain the IP address and username associated with the OTP. In case the
|
||||
// OTP matches the echo request message, instead of searching an entry for the OTP,
|
||||
// an echo response message is returned. This feature is used by ssh-helper to verify if
|
||||
// its configured correctly.
|
||||
func (c *SSHHelper) Verify(otp string) (*SSHVerifyResponse, error) {
|
||||
data := map[string]interface{}{
|
||||
"otp": otp,
|
||||
}
|
||||
verifyPath := fmt.Sprintf("/v1/%s/verify", c.MountPoint)
|
||||
r := c.c.NewRequest("PUT", verifyPath)
|
||||
if err := r.SetJSONBody(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
secret, err := ParseSecret(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if secret.Data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var verifyResp SSHVerifyResponse
|
||||
err = mapstructure.Decode(secret.Data, &verifyResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &verifyResp, nil
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package api
|
||||
|
||||
// Sys is used to perform system-related operations on Vault.
|
||||
type Sys struct {
|
||||
c *Client
|
||||
}
|
||||
|
||||
// Sys is used to return the client for sys-related API calls.
|
||||
func (c *Client) Sys() *Sys {
|
||||
return &Sys{c: c}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
func (c *Sys) AuditHash(path string, input string) (string, error) {
|
||||
body := map[string]interface{}{
|
||||
"input": input,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("PUT", fmt.Sprintf("/v1/sys/audit-hash/%s", path))
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
type d struct {
|
||||
Hash string `json:"hash"`
|
||||
}
|
||||
|
||||
var result d
|
||||
err = resp.DecodeJSON(&result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return result.Hash, err
|
||||
}
|
||||
|
||||
func (c *Sys) ListAudit() (map[string]*Audit, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/audit")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result map[string]interface{}
|
||||
err = resp.DecodeJSON(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mounts := map[string]*Audit{}
|
||||
for k, v := range result {
|
||||
switch v.(type) {
|
||||
case map[string]interface{}:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
var res Audit
|
||||
err = mapstructure.Decode(v, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Not a mount, some other api.Secret data
|
||||
if res.Type == "" {
|
||||
continue
|
||||
}
|
||||
mounts[k] = &res
|
||||
}
|
||||
|
||||
return mounts, nil
|
||||
}
|
||||
|
||||
// DEPRECATED: Use EnableAuditWithOptions instead
|
||||
func (c *Sys) EnableAudit(
|
||||
path string, auditType string, desc string, opts map[string]string) error {
|
||||
return c.EnableAuditWithOptions(path, &EnableAuditOptions{
|
||||
Type: auditType,
|
||||
Description: desc,
|
||||
Options: opts,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Sys) EnableAuditWithOptions(path string, options *EnableAuditOptions) error {
|
||||
r := c.c.NewRequest("PUT", fmt.Sprintf("/v1/sys/audit/%s", path))
|
||||
if err := r.SetJSONBody(options); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Sys) DisableAudit(path string) error {
|
||||
r := c.c.NewRequest("DELETE", fmt.Sprintf("/v1/sys/audit/%s", path))
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Structures for the requests/resposne are all down here. They aren't
|
||||
// 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"`
|
||||
Description string `json:"description"`
|
||||
Options map[string]string `json:"options"`
|
||||
Local bool `json:"local"`
|
||||
}
|
||||
|
||||
type Audit struct {
|
||||
Path string
|
||||
Type string
|
||||
Description string
|
||||
Options map[string]string
|
||||
Local bool
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
func (c *Sys) ListAuth() (map[string]*AuthMount, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/auth")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result map[string]interface{}
|
||||
err = resp.DecodeJSON(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mounts := map[string]*AuthMount{}
|
||||
for k, v := range result {
|
||||
switch v.(type) {
|
||||
case map[string]interface{}:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
var res AuthMount
|
||||
err = mapstructure.Decode(v, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Not a mount, some other api.Secret data
|
||||
if res.Type == "" {
|
||||
continue
|
||||
}
|
||||
mounts[k] = &res
|
||||
}
|
||||
|
||||
return mounts, nil
|
||||
}
|
||||
|
||||
// DEPRECATED: Use EnableAuthWithOptions instead
|
||||
func (c *Sys) EnableAuth(path, authType, desc string) error {
|
||||
return c.EnableAuthWithOptions(path, &EnableAuthOptions{
|
||||
Type: authType,
|
||||
Description: desc,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Sys) EnableAuthWithOptions(path string, options *EnableAuthOptions) error {
|
||||
r := c.c.NewRequest("POST", fmt.Sprintf("/v1/sys/auth/%s", path))
|
||||
if err := r.SetJSONBody(options); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Sys) DisableAuth(path string) error {
|
||||
r := c.c.NewRequest("DELETE", fmt.Sprintf("/v1/sys/auth/%s", path))
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Structures for the requests/resposne are all down here. They aren't
|
||||
// individually documented 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"`
|
||||
Description string `json:"description"`
|
||||
Config AuthConfigInput `json:"config"`
|
||||
Local bool `json:"local"`
|
||||
PluginName string `json:"plugin_name,omitempty"`
|
||||
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
|
||||
Options map[string]string `json:"options" mapstructure:"options"`
|
||||
}
|
||||
|
||||
type AuthConfigInput struct {
|
||||
DefaultLeaseTTL string `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||
MaxLeaseTTL string `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
|
||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
||||
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
||||
}
|
||||
|
||||
type AuthMount struct {
|
||||
Type string `json:"type" mapstructure:"type"`
|
||||
Description string `json:"description" mapstructure:"description"`
|
||||
Accessor string `json:"accessor" mapstructure:"accessor"`
|
||||
Config AuthConfigOutput `json:"config" mapstructure:"config"`
|
||||
Local bool `json:"local" mapstructure:"local"`
|
||||
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
|
||||
Options map[string]string `json:"options" mapstructure:"options"`
|
||||
}
|
||||
|
||||
type AuthConfigOutput struct {
|
||||
DefaultLeaseTTL int `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||
MaxLeaseTTL int `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
|
||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
||||
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (c *Sys) CapabilitiesSelf(path string) ([]string, error) {
|
||||
return c.Capabilities(c.c.Token(), path)
|
||||
}
|
||||
|
||||
func (c *Sys) Capabilities(token, path string) ([]string, error) {
|
||||
body := map[string]string{
|
||||
"token": token,
|
||||
"path": path,
|
||||
}
|
||||
|
||||
reqPath := "/v1/sys/capabilities"
|
||||
if token == c.c.Token() {
|
||||
reqPath = fmt.Sprintf("%s-self", reqPath)
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("POST", reqPath)
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result map[string]interface{}
|
||||
err = resp.DecodeJSON(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result["capabilities"] == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var capabilities []string
|
||||
capabilitiesRaw, ok := result["capabilities"].([]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error interpreting returned capabilities")
|
||||
}
|
||||
for _, capability := range capabilitiesRaw {
|
||||
capabilities = append(capabilities, capability.(string))
|
||||
}
|
||||
return capabilities, nil
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package api
|
||||
|
||||
import "context"
|
||||
|
||||
func (c *Sys) CORSStatus() (*CORSResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/config/cors")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, 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
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, 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")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, 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"`
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
package api
|
||||
|
||||
import "context"
|
||||
|
||||
func (c *Sys) GenerateRootStatus() (*GenerateRootStatusResponse, error) {
|
||||
return c.generateRootStatusCommon("/v1/sys/generate-root/attempt")
|
||||
}
|
||||
|
||||
func (c *Sys) GenerateDROperationTokenStatus() (*GenerateRootStatusResponse, error) {
|
||||
return c.generateRootStatusCommon("/v1/sys/replication/dr/secondary/generate-operation-token/attempt")
|
||||
}
|
||||
|
||||
func (c *Sys) generateRootStatusCommon(path string) (*GenerateRootStatusResponse, error) {
|
||||
r := c.c.NewRequest("GET", path)
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result GenerateRootStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) GenerateRootInit(otp, pgpKey string) (*GenerateRootStatusResponse, error) {
|
||||
return c.generateRootInitCommon("/v1/sys/generate-root/attempt", otp, pgpKey)
|
||||
}
|
||||
|
||||
func (c *Sys) GenerateDROperationTokenInit(otp, pgpKey string) (*GenerateRootStatusResponse, error) {
|
||||
return c.generateRootInitCommon("/v1/sys/replication/dr/secondary/generate-operation-token/attempt", otp, pgpKey)
|
||||
}
|
||||
|
||||
func (c *Sys) generateRootInitCommon(path, otp, pgpKey string) (*GenerateRootStatusResponse, error) {
|
||||
body := map[string]interface{}{
|
||||
"otp": otp,
|
||||
"pgp_key": pgpKey,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("PUT", path)
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result GenerateRootStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) GenerateRootCancel() error {
|
||||
return c.generateRootCancelCommon("/v1/sys/generate-root/attempt")
|
||||
}
|
||||
|
||||
func (c *Sys) GenerateDROperationTokenCancel() error {
|
||||
return c.generateRootCancelCommon("/v1/sys/replication/dr/secondary/generate-operation-token/attempt")
|
||||
}
|
||||
|
||||
func (c *Sys) generateRootCancelCommon(path string) error {
|
||||
r := c.c.NewRequest("DELETE", path)
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) GenerateRootUpdate(shard, nonce string) (*GenerateRootStatusResponse, error) {
|
||||
return c.generateRootUpdateCommon("/v1/sys/generate-root/update", shard, nonce)
|
||||
}
|
||||
|
||||
func (c *Sys) GenerateDROperationTokenUpdate(shard, nonce string) (*GenerateRootStatusResponse, error) {
|
||||
return c.generateRootUpdateCommon("/v1/sys/replication/dr/secondary/generate-operation-token/update", shard, nonce)
|
||||
}
|
||||
|
||||
func (c *Sys) generateRootUpdateCommon(path, shard, nonce string) (*GenerateRootStatusResponse, error) {
|
||||
body := map[string]interface{}{
|
||||
"key": shard,
|
||||
"nonce": nonce,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("PUT", path)
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result GenerateRootStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
type GenerateRootStatusResponse struct {
|
||||
Nonce string `json:"nonce"`
|
||||
Started bool `json:"started"`
|
||||
Progress int `json:"progress"`
|
||||
Required int `json:"required"`
|
||||
Complete bool `json:"complete"`
|
||||
EncodedToken string `json:"encoded_token"`
|
||||
EncodedRootToken string `json:"encoded_root_token"`
|
||||
PGPFingerprint string `json:"pgp_fingerprint"`
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package api
|
||||
|
||||
import "context"
|
||||
|
||||
func (c *Sys) Health() (*HealthResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/health")
|
||||
// If the code is 400 or above it will automatically turn into an error,
|
||||
// but the sys/health API defaults to returning 5xx when not sealed or
|
||||
// inited, so we force this code to be something else so we parse correctly
|
||||
r.Params.Add("uninitcode", "299")
|
||||
r.Params.Add("sealedcode", "299")
|
||||
r.Params.Add("standbycode", "299")
|
||||
r.Params.Add("drsecondarycode", "299")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, 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"`
|
||||
ReplicationPerformanceMode string `json:"replication_performance_mode"`
|
||||
ReplicationDRMode string `json:"replication_dr_mode"`
|
||||
ServerTimeUTC int64 `json:"server_time_utc"`
|
||||
Version string `json:"version"`
|
||||
ClusterName string `json:"cluster_name,omitempty"`
|
||||
ClusterID string `json:"cluster_id,omitempty"`
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package api
|
||||
|
||||
import "context"
|
||||
|
||||
func (c *Sys) InitStatus() (bool, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/init")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result InitStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return result.Initialized, err
|
||||
}
|
||||
|
||||
func (c *Sys) Init(opts *InitRequest) (*InitResponse, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/init")
|
||||
if err := r.SetJSONBody(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result InitResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
type InitRequest struct {
|
||||
SecretShares int `json:"secret_shares"`
|
||||
SecretThreshold int `json:"secret_threshold"`
|
||||
StoredShares int `json:"stored_shares"`
|
||||
PGPKeys []string `json:"pgp_keys"`
|
||||
RecoveryShares int `json:"recovery_shares"`
|
||||
RecoveryThreshold int `json:"recovery_threshold"`
|
||||
RecoveryPGPKeys []string `json:"recovery_pgp_keys"`
|
||||
RootTokenPGPKey string `json:"root_token_pgp_key"`
|
||||
}
|
||||
|
||||
type InitStatusResponse struct {
|
||||
Initialized bool
|
||||
}
|
||||
|
||||
type InitResponse struct {
|
||||
Keys []string `json:"keys"`
|
||||
KeysB64 []string `json:"keys_base64"`
|
||||
RecoveryKeys []string `json:"recovery_keys"`
|
||||
RecoveryKeysB64 []string `json:"recovery_keys_base64"`
|
||||
RootToken string `json:"root_token"`
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package api
|
||||
|
||||
import "context"
|
||||
|
||||
func (c *Sys) Leader() (*LeaderResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/leader")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result LeaderResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
type LeaderResponse struct {
|
||||
HAEnabled bool `json:"ha_enabled"`
|
||||
IsSelf bool `json:"is_self"`
|
||||
LeaderAddress string `json:"leader_address"`
|
||||
LeaderClusterAddress string `json:"leader_cluster_address"`
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
)
|
||||
|
||||
func (c *Sys) Renew(id string, increment int) (*Secret, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/leases/renew")
|
||||
|
||||
body := map[string]interface{}{
|
||||
"increment": increment,
|
||||
"lease_id": id,
|
||||
}
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ParseSecret(resp.Body)
|
||||
}
|
||||
|
||||
func (c *Sys) Revoke(id string) error {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke/"+id)
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RevokePrefix(id string) error {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke-prefix/"+id)
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RevokeForce(id string) error {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke-force/"+id)
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RevokeWithOptions(opts *RevokeOptions) error {
|
||||
if opts == nil {
|
||||
return errors.New("nil options provided")
|
||||
}
|
||||
|
||||
// Construct path
|
||||
path := "/v1/sys/leases/revoke/"
|
||||
switch {
|
||||
case opts.Force:
|
||||
path = "/v1/sys/leases/revoke-force/"
|
||||
case opts.Prefix:
|
||||
path = "/v1/sys/leases/revoke-prefix/"
|
||||
}
|
||||
path += opts.LeaseID
|
||||
|
||||
r := c.c.NewRequest("PUT", path)
|
||||
if !opts.Force {
|
||||
body := map[string]interface{}{
|
||||
"sync": opts.Sync,
|
||||
}
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type RevokeOptions struct {
|
||||
LeaseID string
|
||||
Force bool
|
||||
Prefix bool
|
||||
Sync bool
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
func (c *Sys) ListMounts() (map[string]*MountOutput, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/mounts")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result map[string]interface{}
|
||||
err = resp.DecodeJSON(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mounts := map[string]*MountOutput{}
|
||||
for k, v := range result {
|
||||
switch v.(type) {
|
||||
case map[string]interface{}:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
var res MountOutput
|
||||
err = mapstructure.Decode(v, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Not a mount, some other api.Secret data
|
||||
if res.Type == "" {
|
||||
continue
|
||||
}
|
||||
mounts[k] = &res
|
||||
}
|
||||
|
||||
return mounts, nil
|
||||
}
|
||||
|
||||
func (c *Sys) Mount(path string, mountInfo *MountInput) error {
|
||||
r := c.c.NewRequest("POST", fmt.Sprintf("/v1/sys/mounts/%s", path))
|
||||
if err := r.SetJSONBody(mountInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Sys) Unmount(path string) error {
|
||||
r := c.c.NewRequest("DELETE", fmt.Sprintf("/v1/sys/mounts/%s", path))
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) Remount(from, to string) error {
|
||||
body := map[string]interface{}{
|
||||
"from": from,
|
||||
"to": to,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("POST", "/v1/sys/remount")
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) TuneMount(path string, config MountConfigInput) error {
|
||||
r := c.c.NewRequest("POST", fmt.Sprintf("/v1/sys/mounts/%s/tune", path))
|
||||
if err := r.SetJSONBody(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) MountConfig(path string) (*MountConfigOutput, error) {
|
||||
r := c.c.NewRequest("GET", fmt.Sprintf("/v1/sys/mounts/%s/tune", path))
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result MountConfigOutput
|
||||
err = resp.DecodeJSON(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, err
|
||||
}
|
||||
|
||||
type MountInput struct {
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Config MountConfigInput `json:"config"`
|
||||
Options map[string]string `json:"options"`
|
||||
Local bool `json:"local"`
|
||||
PluginName string `json:"plugin_name,omitempty"`
|
||||
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
|
||||
}
|
||||
|
||||
type MountConfigInput struct {
|
||||
Options map[string]string `json:"options" mapstructure:"options"`
|
||||
DefaultLeaseTTL string `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||
Description *string `json:"description,omitempty" mapstructure:"description"`
|
||||
MaxLeaseTTL string `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||
ForceNoCache bool `json:"force_no_cache" mapstructure:"force_no_cache"`
|
||||
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
|
||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
||||
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
||||
}
|
||||
|
||||
type MountOutput struct {
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Accessor string `json:"accessor"`
|
||||
Config MountConfigOutput `json:"config"`
|
||||
Options map[string]string `json:"options"`
|
||||
Local bool `json:"local"`
|
||||
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
|
||||
}
|
||||
|
||||
type MountConfigOutput struct {
|
||||
DefaultLeaseTTL int `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
|
||||
MaxLeaseTTL int `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
|
||||
ForceNoCache bool `json:"force_no_cache" mapstructure:"force_no_cache"`
|
||||
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
|
||||
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
|
||||
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
|
||||
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
|
||||
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ListPluginsInput is used as input to the ListPlugins function.
|
||||
type ListPluginsInput struct{}
|
||||
|
||||
// ListPluginsResponse is the response from the ListPlugins call.
|
||||
type ListPluginsResponse struct {
|
||||
// Names is the list of names of the plugins.
|
||||
Names []string
|
||||
}
|
||||
|
||||
// ListPlugins lists all plugins in the catalog and returns their names as a
|
||||
// list of strings.
|
||||
func (c *Sys) ListPlugins(i *ListPluginsInput) (*ListPluginsResponse, error) {
|
||||
path := "/v1/sys/plugins/catalog"
|
||||
req := c.c.NewRequest("LIST", path)
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result struct {
|
||||
Data struct {
|
||||
Keys []string `json:"keys"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := resp.DecodeJSON(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ListPluginsResponse{Names: result.Data.Keys}, nil
|
||||
}
|
||||
|
||||
// GetPluginInput is used as input to the GetPlugin function.
|
||||
type GetPluginInput struct {
|
||||
Name string `json:"-"`
|
||||
}
|
||||
|
||||
// GetPluginResponse is the response from the GetPlugin call.
|
||||
type GetPluginResponse struct {
|
||||
Args []string `json:"args"`
|
||||
Builtin bool `json:"builtin"`
|
||||
Command string `json:"command"`
|
||||
Name string `json:"name"`
|
||||
SHA256 string `json:"sha256"`
|
||||
}
|
||||
|
||||
func (c *Sys) GetPlugin(i *GetPluginInput) (*GetPluginResponse, error) {
|
||||
path := fmt.Sprintf("/v1/sys/plugins/catalog/%s", i.Name)
|
||||
req := c.c.NewRequest(http.MethodGet, path)
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result struct {
|
||||
Data GetPluginResponse
|
||||
}
|
||||
err = resp.DecodeJSON(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result.Data, err
|
||||
}
|
||||
|
||||
// RegisterPluginInput is used as input to the RegisterPlugin function.
|
||||
type RegisterPluginInput struct {
|
||||
// Name is the name of the plugin. Required.
|
||||
Name string `json:"-"`
|
||||
|
||||
// Args is the list of args to spawn the process with.
|
||||
Args []string `json:"args,omitempty"`
|
||||
|
||||
// Command is the command to run.
|
||||
Command string `json:"command,omitempty"`
|
||||
|
||||
// SHA256 is the shasum of the plugin.
|
||||
SHA256 string `json:"sha256,omitempty"`
|
||||
}
|
||||
|
||||
// RegisterPlugin registers the plugin with the given information.
|
||||
func (c *Sys) RegisterPlugin(i *RegisterPluginInput) error {
|
||||
path := fmt.Sprintf("/v1/sys/plugins/catalog/%s", i.Name)
|
||||
req := c.c.NewRequest(http.MethodPut, path)
|
||||
if err := req.SetJSONBody(i); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, req)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// DeregisterPluginInput is used as input to the DeregisterPlugin function.
|
||||
type DeregisterPluginInput struct {
|
||||
// Name is the name of the plugin. Required.
|
||||
Name string `json:"-"`
|
||||
}
|
||||
|
||||
// DeregisterPlugin removes the plugin with the given name from the plugin
|
||||
// catalog.
|
||||
func (c *Sys) DeregisterPlugin(i *DeregisterPluginInput) error {
|
||||
path := fmt.Sprintf("/v1/sys/plugins/catalog/%s", i.Name)
|
||||
req := c.c.NewRequest(http.MethodDelete, path)
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, req)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (c *Sys) ListPolicies() ([]string, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/policy")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result map[string]interface{}
|
||||
err = resp.DecodeJSON(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if _, ok = result["policies"]; !ok {
|
||||
return nil, fmt.Errorf("policies not found in response")
|
||||
}
|
||||
|
||||
listRaw := result["policies"].([]interface{})
|
||||
var policies []string
|
||||
|
||||
for _, val := range listRaw {
|
||||
policies = append(policies, val.(string))
|
||||
}
|
||||
|
||||
return policies, err
|
||||
}
|
||||
|
||||
func (c *Sys) GetPolicy(name string) (string, error) {
|
||||
r := c.c.NewRequest("GET", fmt.Sprintf("/v1/sys/policy/%s", name))
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode == 404 {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var result map[string]interface{}
|
||||
err = resp.DecodeJSON(&result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if rulesRaw, ok := result["rules"]; ok {
|
||||
return rulesRaw.(string), nil
|
||||
}
|
||||
if policyRaw, ok := result["policy"]; ok {
|
||||
return policyRaw.(string), nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("no policy found in response")
|
||||
}
|
||||
|
||||
func (c *Sys) PutPolicy(name, rules string) error {
|
||||
body := map[string]string{
|
||||
"rules": rules,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("PUT", fmt.Sprintf("/v1/sys/policy/%s", name))
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Sys) DeletePolicy(name string) error {
|
||||
r := c.c.NewRequest("DELETE", fmt.Sprintf("/v1/sys/policy/%s", name))
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type getPoliciesResp struct {
|
||||
Rules string `json:"rules"`
|
||||
}
|
||||
|
||||
type listPoliciesResp struct {
|
||||
Policies []string `json:"policies"`
|
||||
}
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
package api
|
||||
|
||||
import "context"
|
||||
|
||||
func (c *Sys) RekeyStatus() (*RekeyStatusResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/rekey/init")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyStatus() (*RekeyStatusResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/rekey-recovery-key/init")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyVerificationStatus() (*RekeyVerificationStatusResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/rekey/verify")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyVerificationStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyVerificationStatus() (*RekeyVerificationStatusResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/rekey-recovery-key/verify")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyVerificationStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyInit(config *RekeyInitRequest) (*RekeyStatusResponse, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/rekey/init")
|
||||
if err := r.SetJSONBody(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyInit(config *RekeyInitRequest) (*RekeyStatusResponse, error) {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/rekey-recovery-key/init")
|
||||
if err := r.SetJSONBody(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyCancel() error {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/rekey/init")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyCancel() error {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/rekey-recovery-key/init")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyVerificationCancel() error {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/rekey/verify")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyVerificationCancel() error {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/rekey-recovery-key/verify")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyUpdate(shard, nonce string) (*RekeyUpdateResponse, error) {
|
||||
body := map[string]interface{}{
|
||||
"key": shard,
|
||||
"nonce": nonce,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/rekey/update")
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyUpdateResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyUpdate(shard, nonce string) (*RekeyUpdateResponse, error) {
|
||||
body := map[string]interface{}{
|
||||
"key": shard,
|
||||
"nonce": nonce,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/rekey-recovery-key/update")
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyUpdateResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRetrieveBackup() (*RekeyRetrieveResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/rekey/backup")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyRetrieveResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRetrieveRecoveryBackup() (*RekeyRetrieveResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/rekey/recovery-backup")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyRetrieveResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyDeleteBackup() error {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/rekey/backup")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyDeleteRecoveryBackup() error {
|
||||
r := c.c.NewRequest("DELETE", "/v1/sys/rekey/recovery-backup")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyVerificationUpdate(shard, nonce string) (*RekeyVerificationUpdateResponse, error) {
|
||||
body := map[string]interface{}{
|
||||
"key": shard,
|
||||
"nonce": nonce,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/rekey/verify")
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyVerificationUpdateResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func (c *Sys) RekeyRecoveryKeyVerificationUpdate(shard, nonce string) (*RekeyVerificationUpdateResponse, error) {
|
||||
body := map[string]interface{}{
|
||||
"key": shard,
|
||||
"nonce": nonce,
|
||||
}
|
||||
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/rekey-recovery-key/verify")
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result RekeyVerificationUpdateResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
type RekeyInitRequest struct {
|
||||
SecretShares int `json:"secret_shares"`
|
||||
SecretThreshold int `json:"secret_threshold"`
|
||||
StoredShares int `json:"stored_shares"`
|
||||
PGPKeys []string `json:"pgp_keys"`
|
||||
Backup bool
|
||||
RequireVerification bool `json:"require_verification"`
|
||||
}
|
||||
|
||||
type RekeyStatusResponse struct {
|
||||
Nonce string `json:"nonce"`
|
||||
Started bool `json:"started"`
|
||||
T int `json:"t"`
|
||||
N int `json:"n"`
|
||||
Progress int `json:"progress"`
|
||||
Required int `json:"required"`
|
||||
PGPFingerprints []string `json:"pgp_fingerprints"`
|
||||
Backup bool `json:"backup"`
|
||||
VerificationRequired bool `json:"verification_required"`
|
||||
VerificationNonce string `json:"verification_nonce"`
|
||||
}
|
||||
|
||||
type RekeyUpdateResponse struct {
|
||||
Nonce string `json:"nonce"`
|
||||
Complete bool `json:"complete"`
|
||||
Keys []string `json:"keys"`
|
||||
KeysB64 []string `json:"keys_base64"`
|
||||
PGPFingerprints []string `json:"pgp_fingerprints"`
|
||||
Backup bool `json:"backup"`
|
||||
VerificationRequired bool `json:"verification_required"`
|
||||
VerificationNonce string `json:"verification_nonce,omitempty"`
|
||||
}
|
||||
|
||||
type RekeyRetrieveResponse struct {
|
||||
Nonce string `json:"nonce"`
|
||||
Keys map[string][]string `json:"keys"`
|
||||
KeysB64 map[string][]string `json:"keys_base64"`
|
||||
}
|
||||
|
||||
type RekeyVerificationStatusResponse struct {
|
||||
Nonce string `json:"nonce"`
|
||||
Started bool `json:"started"`
|
||||
T int `json:"t"`
|
||||
N int `json:"n"`
|
||||
Progress int `json:"progress"`
|
||||
}
|
||||
|
||||
type RekeyVerificationUpdateResponse struct {
|
||||
Nonce string `json:"nonce"`
|
||||
Complete bool `json:"complete"`
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (c *Sys) Rotate() error {
|
||||
r := c.c.NewRequest("POST", "/v1/sys/rotate")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) KeyStatus() (*KeyStatus, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/key-status")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
result := new(KeyStatus)
|
||||
err = resp.DecodeJSON(result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
type KeyStatus struct {
|
||||
Term int `json:"term"`
|
||||
InstallTime time.Time `json:"install_time"`
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package api
|
||||
|
||||
import "context"
|
||||
|
||||
func (c *Sys) SealStatus() (*SealStatusResponse, error) {
|
||||
r := c.c.NewRequest("GET", "/v1/sys/seal-status")
|
||||
return sealStatusRequest(c, r)
|
||||
}
|
||||
|
||||
func (c *Sys) Seal() error {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/seal")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Sys) ResetUnsealProcess() (*SealStatusResponse, error) {
|
||||
body := map[string]interface{}{"reset": true}
|
||||
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/unseal")
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sealStatusRequest(c, r)
|
||||
}
|
||||
|
||||
func (c *Sys) Unseal(shard string) (*SealStatusResponse, error) {
|
||||
body := map[string]interface{}{"key": shard}
|
||||
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/unseal")
|
||||
if err := r.SetJSONBody(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sealStatusRequest(c, r)
|
||||
}
|
||||
|
||||
func sealStatusRequest(c *Sys, r *Request) (*SealStatusResponse, error) {
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var result SealStatusResponse
|
||||
err = resp.DecodeJSON(&result)
|
||||
return &result, err
|
||||
}
|
||||
|
||||
type SealStatusResponse struct {
|
||||
Type string `json:"type"`
|
||||
Sealed bool `json:"sealed"`
|
||||
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"`
|
||||
RecoverySeal bool `json:"recovery_seal"`
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package api
|
||||
|
||||
import "context"
|
||||
|
||||
func (c *Sys) StepDown() error {
|
||||
r := c.c.NewRequest("PUT", "/v1/sys/step-down")
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
resp, err := c.c.RawRequestWithContext(ctx, r)
|
||||
if err == nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
vault_addr="http://127.0.0.1:8200"
|
||||
ssh_mount_point="ssh"
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDtTCCAp2gAwIBAgIUf+jhKTFBnqSs34II0WS1L4QsbbAwDQYJKoZIhvcNAQEL
|
||||
BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTYwMjI5MDIyNzQxWhcNMjUw
|
||||
MTA1MTAyODExWjAbMRkwFwYDVQQDExBjZXJ0LmV4YW1wbGUuY29tMIIBIjANBgkq
|
||||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsZx0Svr82YJpFpIy4fJNW5fKA6B8mhxS
|
||||
TRAVnygAftetT8puHflY0ss7Y6X2OXjsU0PRn+1PswtivhKi+eLtgWkUF9cFYFGn
|
||||
SgMld6ZWRhNheZhA6ZfQmeM/BF2pa5HK2SDF36ljgjL9T+nWrru2Uv0BCoHzLAmi
|
||||
YYMiIWplidMmMO5NTRG3k+3AN0TkfakB6JVzjLGhTcXdOcVEMXkeQVqJMAuGouU5
|
||||
donyqtnaHuIJGuUdy54YDnX86txhOQhAv6r7dHXzZxS4pmLvw8UI1rsSf/GLcUVG
|
||||
B+5+AAGF5iuHC3N2DTl4xz3FcN4Cb4w9pbaQ7+mCzz+anqiJfyr2nwIDAQABo4H1
|
||||
MIHyMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUm++e
|
||||
HpyM3p708bgZJuRYEdX1o+UwHwYDVR0jBBgwFoAUncSzT/6HMexyuiU9/7EgHu+o
|
||||
k5swOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4x
|
||||
OjgyMDAvdjEvcGtpL2NhMCEGA1UdEQQaMBiCEGNlcnQuZXhhbXBsZS5jb22HBH8A
|
||||
AAEwMQYDVR0fBCowKDAmoCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3Br
|
||||
aS9jcmwwDQYJKoZIhvcNAQELBQADggEBABsuvmPSNjjKTVN6itWzdQy+SgMIrwfs
|
||||
X1Yb9Lefkkwmp9ovKFNQxa4DucuCuzXcQrbKwWTfHGgR8ct4rf30xCRoA7dbQWq4
|
||||
aYqNKFWrRaBRAaaYZ/O1ApRTOrXqRx9Eqr0H1BXLsoAq+mWassL8sf6siae+CpwA
|
||||
KqBko5G0dNXq5T4i2LQbmoQSVetIrCJEeMrU+idkuqfV2h1BQKgSEhFDABjFdTCN
|
||||
QDAHsEHsi2M4/jRW9fqEuhHSDfl2n7tkFUI8wTHUUCl7gXwweJ4qtaSXIwKXYzNj
|
||||
xqKHA8Purc1Yfybz4iE1JCROi9fInKlzr5xABq8nb9Qc/J9DIQM+Xmk=
|
||||
-----END CERTIFICATE-----
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAsZx0Svr82YJpFpIy4fJNW5fKA6B8mhxSTRAVnygAftetT8pu
|
||||
HflY0ss7Y6X2OXjsU0PRn+1PswtivhKi+eLtgWkUF9cFYFGnSgMld6ZWRhNheZhA
|
||||
6ZfQmeM/BF2pa5HK2SDF36ljgjL9T+nWrru2Uv0BCoHzLAmiYYMiIWplidMmMO5N
|
||||
TRG3k+3AN0TkfakB6JVzjLGhTcXdOcVEMXkeQVqJMAuGouU5donyqtnaHuIJGuUd
|
||||
y54YDnX86txhOQhAv6r7dHXzZxS4pmLvw8UI1rsSf/GLcUVGB+5+AAGF5iuHC3N2
|
||||
DTl4xz3FcN4Cb4w9pbaQ7+mCzz+anqiJfyr2nwIDAQABAoIBAHR7fFV0eAGaopsX
|
||||
9OD0TUGlsephBXb43g0GYHfJ/1Ew18w9oaxszJEqkl+PB4W3xZ3yG3e8ZomxDOhF
|
||||
RreF2WgG5xOfhDogMwu6NodbArfgnAvoC6JnW3qha8HMP4F500RFVyCRcd6A3Frd
|
||||
rFtaZn/UyCsBAN8/zkwPeYHayo7xX6d9kzgRl9HluEX5PXI5+3uiBDUiM085gkLI
|
||||
5Cmadh9fMdjfhDXI4x2JYmILpp/9Nlc/krB15s5n1MPNtn3yL0TI0tWp0WlwDCV7
|
||||
oUm1SfIM0F1fXGFyFDcqwoIr6JCQgXk6XtTg31YhH1xgUIclUVdtHqmAwAbLdIhQ
|
||||
GAiHn2kCgYEAwD4pZ8HfpiOG/EHNoWsMATc/5yC7O8F9WbvcHZQIymLY4v/7HKZb
|
||||
VyOR6UQ5/O2cztSGIuKSF6+OK1C34lOyCuTSOTFrjlgEYtLIXjdGLfFdtOO8GRQR
|
||||
akVXdwuzNAjTBaH5eXbG+NKcjmCvZL48dQVlfDTVulzFGbcsVTHIMQUCgYEA7IQI
|
||||
FVsKnY3KqpyGqXq92LMcsT3XgW6X1BIIV+YhJ5AFUFkFrjrbXs94/8XyLfi0xBQy
|
||||
efK+8g5sMs7koF8LyZEcAXWZJQduaKB71hoLlRaU4VQkL/dl2B6VFmAII/CsRCYh
|
||||
r9RmDN2PF/mp98Ih9dpC1VqcCDRGoTYsd7jLalMCgYAMgH5k1wDaZxkSMp1S0AlZ
|
||||
0uP+/evvOOgT+9mWutfPgZolOQx1koQCKLgGeX9j6Xf3I28NubpSfAI84uTyfQrp
|
||||
FnRtb79U5Hh0jMynA+U2e6niZ6UF5H41cQj9Hu+qhKBkj2IP+h96cwfnYnZFkPGR
|
||||
kqZE65KyqfHPeFATwkcImQKBgCdrfhlpGiTWXCABhKQ8s+WpPLAB2ahV8XJEKyXT
|
||||
UlVQuMIChGLcpnFv7P/cUxf8asx/fUY8Aj0/0CLLvulHziQjTmKj4gl86pb/oIQ3
|
||||
xRRtNhU0O+/OsSfLORgIm3K6C0w0esregL/GMbJSR1TnA1gBr7/1oSnw5JC8Ab9W
|
||||
injHAoGAJT1MGAiQrhlt9GCGe6Ajw4omdbY0wS9NXefnFhf7EwL0es52ezZ28zpU
|
||||
2LXqSFbtann5CHgpSLxiMYPDIf+er4xgg9Bz34tz1if1rDfP2Qrxdrpr4jDnrGT3
|
||||
gYC2qCpvVD9RRUMKFfnJTfl5gMQdBW/LINkHtJ82snAeLl3gjQ4=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
Key Value
|
||||
lease_id pki/issue/example-dot-com/d8214077-9976-8c68-9c07-6610da30aea4
|
||||
lease_duration 279359999
|
||||
lease_renewable false
|
||||
certificate -----BEGIN CERTIFICATE-----
|
||||
MIIDtTCCAp2gAwIBAgIUf+jhKTFBnqSs34II0WS1L4QsbbAwDQYJKoZIhvcNAQEL
|
||||
BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTYwMjI5MDIyNzQxWhcNMjUw
|
||||
MTA1MTAyODExWjAbMRkwFwYDVQQDExBjZXJ0LmV4YW1wbGUuY29tMIIBIjANBgkq
|
||||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsZx0Svr82YJpFpIy4fJNW5fKA6B8mhxS
|
||||
TRAVnygAftetT8puHflY0ss7Y6X2OXjsU0PRn+1PswtivhKi+eLtgWkUF9cFYFGn
|
||||
SgMld6ZWRhNheZhA6ZfQmeM/BF2pa5HK2SDF36ljgjL9T+nWrru2Uv0BCoHzLAmi
|
||||
YYMiIWplidMmMO5NTRG3k+3AN0TkfakB6JVzjLGhTcXdOcVEMXkeQVqJMAuGouU5
|
||||
donyqtnaHuIJGuUdy54YDnX86txhOQhAv6r7dHXzZxS4pmLvw8UI1rsSf/GLcUVG
|
||||
B+5+AAGF5iuHC3N2DTl4xz3FcN4Cb4w9pbaQ7+mCzz+anqiJfyr2nwIDAQABo4H1
|
||||
MIHyMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUm++e
|
||||
HpyM3p708bgZJuRYEdX1o+UwHwYDVR0jBBgwFoAUncSzT/6HMexyuiU9/7EgHu+o
|
||||
k5swOwYIKwYBBQUHAQEELzAtMCsGCCsGAQUFBzAChh9odHRwOi8vMTI3LjAuMC4x
|
||||
OjgyMDAvdjEvcGtpL2NhMCEGA1UdEQQaMBiCEGNlcnQuZXhhbXBsZS5jb22HBH8A
|
||||
AAEwMQYDVR0fBCowKDAmoCSgIoYgaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3Br
|
||||
aS9jcmwwDQYJKoZIhvcNAQELBQADggEBABsuvmPSNjjKTVN6itWzdQy+SgMIrwfs
|
||||
X1Yb9Lefkkwmp9ovKFNQxa4DucuCuzXcQrbKwWTfHGgR8ct4rf30xCRoA7dbQWq4
|
||||
aYqNKFWrRaBRAaaYZ/O1ApRTOrXqRx9Eqr0H1BXLsoAq+mWassL8sf6siae+CpwA
|
||||
KqBko5G0dNXq5T4i2LQbmoQSVetIrCJEeMrU+idkuqfV2h1BQKgSEhFDABjFdTCN
|
||||
QDAHsEHsi2M4/jRW9fqEuhHSDfl2n7tkFUI8wTHUUCl7gXwweJ4qtaSXIwKXYzNj
|
||||
xqKHA8Purc1Yfybz4iE1JCROi9fInKlzr5xABq8nb9Qc/J9DIQM+Xmk=
|
||||
-----END CERTIFICATE-----
|
||||
issuing_ca -----BEGIN CERTIFICATE-----
|
||||
MIIDPDCCAiSgAwIBAgIUb5id+GcaMeMnYBv3MvdTGWigyJ0wDQYJKoZIhvcNAQEL
|
||||
BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTYwMjI5MDIyNzI5WhcNMjYw
|
||||
MjI2MDIyNzU5WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAOxTMvhTuIRc2YhxZpmPwegP86cgnqfT1mXxi1A7
|
||||
Q7qax24Nqbf00I3oDMQtAJlj2RB3hvRSCb0/lkF7i1Bub+TGxuM7NtZqp2F8FgG0
|
||||
z2md+W6adwW26rlxbQKjmRvMn66G9YPTkoJmPmxt2Tccb9+apmwW7lslL5j8H48x
|
||||
AHJTMb+PMP9kbOHV5Abr3PT4jXUPUr/mWBvBiKiHG0Xd/HEmlyOEPeAThxK+I5tb
|
||||
6m+eB+7cL9BsvQpy135+2bRAxUphvFi5NhryJ2vlAvoJ8UqigsNK3E28ut60FAoH
|
||||
SWRfFUFFYtfPgTDS1yOKU/z/XMU2giQv2HrleWt0mp4jqBUCAwEAAaOBgTB/MA4G
|
||||
A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSdxLNP/ocx
|
||||
7HK6JT3/sSAe76iTmzAfBgNVHSMEGDAWgBSdxLNP/ocx7HK6JT3/sSAe76iTmzAc
|
||||
BgNVHREEFTATggtleGFtcGxlLmNvbYcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEA
|
||||
wHThDRsXJunKbAapxmQ6bDxSvTvkLA6m97TXlsFgL+Q3Jrg9HoJCNowJ0pUTwhP2
|
||||
U946dCnSCkZck0fqkwVi4vJ5EQnkvyEbfN4W5qVsQKOFaFVzep6Qid4rZT6owWPa
|
||||
cNNzNcXAee3/j6hgr6OQ/i3J6fYR4YouYxYkjojYyg+CMdn6q8BoV0BTsHdnw1/N
|
||||
ScbnBHQIvIZMBDAmQueQZolgJcdOuBLYHe/kRy167z8nGg+PUFKIYOL8NaOU1+CJ
|
||||
t2YaEibVq5MRqCbRgnd9a2vG0jr5a3Mn4CUUYv+5qIjP3hUusYenW1/EWtn1s/gk
|
||||
zehNe5dFTjFpylg1o6b8Ow==
|
||||
-----END CERTIFICATE-----
|
||||
private_key -----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAsZx0Svr82YJpFpIy4fJNW5fKA6B8mhxSTRAVnygAftetT8pu
|
||||
HflY0ss7Y6X2OXjsU0PRn+1PswtivhKi+eLtgWkUF9cFYFGnSgMld6ZWRhNheZhA
|
||||
6ZfQmeM/BF2pa5HK2SDF36ljgjL9T+nWrru2Uv0BCoHzLAmiYYMiIWplidMmMO5N
|
||||
TRG3k+3AN0TkfakB6JVzjLGhTcXdOcVEMXkeQVqJMAuGouU5donyqtnaHuIJGuUd
|
||||
y54YDnX86txhOQhAv6r7dHXzZxS4pmLvw8UI1rsSf/GLcUVGB+5+AAGF5iuHC3N2
|
||||
DTl4xz3FcN4Cb4w9pbaQ7+mCzz+anqiJfyr2nwIDAQABAoIBAHR7fFV0eAGaopsX
|
||||
9OD0TUGlsephBXb43g0GYHfJ/1Ew18w9oaxszJEqkl+PB4W3xZ3yG3e8ZomxDOhF
|
||||
RreF2WgG5xOfhDogMwu6NodbArfgnAvoC6JnW3qha8HMP4F500RFVyCRcd6A3Frd
|
||||
rFtaZn/UyCsBAN8/zkwPeYHayo7xX6d9kzgRl9HluEX5PXI5+3uiBDUiM085gkLI
|
||||
5Cmadh9fMdjfhDXI4x2JYmILpp/9Nlc/krB15s5n1MPNtn3yL0TI0tWp0WlwDCV7
|
||||
oUm1SfIM0F1fXGFyFDcqwoIr6JCQgXk6XtTg31YhH1xgUIclUVdtHqmAwAbLdIhQ
|
||||
GAiHn2kCgYEAwD4pZ8HfpiOG/EHNoWsMATc/5yC7O8F9WbvcHZQIymLY4v/7HKZb
|
||||
VyOR6UQ5/O2cztSGIuKSF6+OK1C34lOyCuTSOTFrjlgEYtLIXjdGLfFdtOO8GRQR
|
||||
akVXdwuzNAjTBaH5eXbG+NKcjmCvZL48dQVlfDTVulzFGbcsVTHIMQUCgYEA7IQI
|
||||
FVsKnY3KqpyGqXq92LMcsT3XgW6X1BIIV+YhJ5AFUFkFrjrbXs94/8XyLfi0xBQy
|
||||
efK+8g5sMs7koF8LyZEcAXWZJQduaKB71hoLlRaU4VQkL/dl2B6VFmAII/CsRCYh
|
||||
r9RmDN2PF/mp98Ih9dpC1VqcCDRGoTYsd7jLalMCgYAMgH5k1wDaZxkSMp1S0AlZ
|
||||
0uP+/evvOOgT+9mWutfPgZolOQx1koQCKLgGeX9j6Xf3I28NubpSfAI84uTyfQrp
|
||||
FnRtb79U5Hh0jMynA+U2e6niZ6UF5H41cQj9Hu+qhKBkj2IP+h96cwfnYnZFkPGR
|
||||
kqZE65KyqfHPeFATwkcImQKBgCdrfhlpGiTWXCABhKQ8s+WpPLAB2ahV8XJEKyXT
|
||||
UlVQuMIChGLcpnFv7P/cUxf8asx/fUY8Aj0/0CLLvulHziQjTmKj4gl86pb/oIQ3
|
||||
xRRtNhU0O+/OsSfLORgIm3K6C0w0esregL/GMbJSR1TnA1gBr7/1oSnw5JC8Ab9W
|
||||
injHAoGAJT1MGAiQrhlt9GCGe6Ajw4omdbY0wS9NXefnFhf7EwL0es52ezZ28zpU
|
||||
2LXqSFbtann5CHgpSLxiMYPDIf+er4xgg9Bz34tz1if1rDfP2Qrxdrpr4jDnrGT3
|
||||
gYC2qCpvVD9RRUMKFfnJTfl5gMQdBW/LINkHtJ82snAeLl3gjQ4=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
private_key_type rsa
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
Key Value
|
||||
lease_id pki/root/generate/exported/7bf99d76-dd3e-2c5b-04ce-5253062ad586
|
||||
lease_duration 315359999
|
||||
lease_renewable false
|
||||
certificate -----BEGIN CERTIFICATE-----
|
||||
MIIDPDCCAiSgAwIBAgIUb5id+GcaMeMnYBv3MvdTGWigyJ0wDQYJKoZIhvcNAQEL
|
||||
BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTYwMjI5MDIyNzI5WhcNMjYw
|
||||
MjI2MDIyNzU5WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAOxTMvhTuIRc2YhxZpmPwegP86cgnqfT1mXxi1A7
|
||||
Q7qax24Nqbf00I3oDMQtAJlj2RB3hvRSCb0/lkF7i1Bub+TGxuM7NtZqp2F8FgG0
|
||||
z2md+W6adwW26rlxbQKjmRvMn66G9YPTkoJmPmxt2Tccb9+apmwW7lslL5j8H48x
|
||||
AHJTMb+PMP9kbOHV5Abr3PT4jXUPUr/mWBvBiKiHG0Xd/HEmlyOEPeAThxK+I5tb
|
||||
6m+eB+7cL9BsvQpy135+2bRAxUphvFi5NhryJ2vlAvoJ8UqigsNK3E28ut60FAoH
|
||||
SWRfFUFFYtfPgTDS1yOKU/z/XMU2giQv2HrleWt0mp4jqBUCAwEAAaOBgTB/MA4G
|
||||
A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSdxLNP/ocx
|
||||
7HK6JT3/sSAe76iTmzAfBgNVHSMEGDAWgBSdxLNP/ocx7HK6JT3/sSAe76iTmzAc
|
||||
BgNVHREEFTATggtleGFtcGxlLmNvbYcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEA
|
||||
wHThDRsXJunKbAapxmQ6bDxSvTvkLA6m97TXlsFgL+Q3Jrg9HoJCNowJ0pUTwhP2
|
||||
U946dCnSCkZck0fqkwVi4vJ5EQnkvyEbfN4W5qVsQKOFaFVzep6Qid4rZT6owWPa
|
||||
cNNzNcXAee3/j6hgr6OQ/i3J6fYR4YouYxYkjojYyg+CMdn6q8BoV0BTsHdnw1/N
|
||||
ScbnBHQIvIZMBDAmQueQZolgJcdOuBLYHe/kRy167z8nGg+PUFKIYOL8NaOU1+CJ
|
||||
t2YaEibVq5MRqCbRgnd9a2vG0jr5a3Mn4CUUYv+5qIjP3hUusYenW1/EWtn1s/gk
|
||||
zehNe5dFTjFpylg1o6b8Ow==
|
||||
-----END CERTIFICATE-----
|
||||
expiration 1.772072879e+09
|
||||
issuing_ca -----BEGIN CERTIFICATE-----
|
||||
MIIDPDCCAiSgAwIBAgIUb5id+GcaMeMnYBv3MvdTGWigyJ0wDQYJKoZIhvcNAQEL
|
||||
BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTYwMjI5MDIyNzI5WhcNMjYw
|
||||
MjI2MDIyNzU5WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAOxTMvhTuIRc2YhxZpmPwegP86cgnqfT1mXxi1A7
|
||||
Q7qax24Nqbf00I3oDMQtAJlj2RB3hvRSCb0/lkF7i1Bub+TGxuM7NtZqp2F8FgG0
|
||||
z2md+W6adwW26rlxbQKjmRvMn66G9YPTkoJmPmxt2Tccb9+apmwW7lslL5j8H48x
|
||||
AHJTMb+PMP9kbOHV5Abr3PT4jXUPUr/mWBvBiKiHG0Xd/HEmlyOEPeAThxK+I5tb
|
||||
6m+eB+7cL9BsvQpy135+2bRAxUphvFi5NhryJ2vlAvoJ8UqigsNK3E28ut60FAoH
|
||||
SWRfFUFFYtfPgTDS1yOKU/z/XMU2giQv2HrleWt0mp4jqBUCAwEAAaOBgTB/MA4G
|
||||
A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSdxLNP/ocx
|
||||
7HK6JT3/sSAe76iTmzAfBgNVHSMEGDAWgBSdxLNP/ocx7HK6JT3/sSAe76iTmzAc
|
||||
BgNVHREEFTATggtleGFtcGxlLmNvbYcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEA
|
||||
wHThDRsXJunKbAapxmQ6bDxSvTvkLA6m97TXlsFgL+Q3Jrg9HoJCNowJ0pUTwhP2
|
||||
U946dCnSCkZck0fqkwVi4vJ5EQnkvyEbfN4W5qVsQKOFaFVzep6Qid4rZT6owWPa
|
||||
cNNzNcXAee3/j6hgr6OQ/i3J6fYR4YouYxYkjojYyg+CMdn6q8BoV0BTsHdnw1/N
|
||||
ScbnBHQIvIZMBDAmQueQZolgJcdOuBLYHe/kRy167z8nGg+PUFKIYOL8NaOU1+CJ
|
||||
t2YaEibVq5MRqCbRgnd9a2vG0jr5a3Mn4CUUYv+5qIjP3hUusYenW1/EWtn1s/gk
|
||||
zehNe5dFTjFpylg1o6b8Ow==
|
||||
-----END CERTIFICATE-----
|
||||
private_key -----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA7FMy+FO4hFzZiHFmmY/B6A/zpyCep9PWZfGLUDtDuprHbg2p
|
||||
t/TQjegMxC0AmWPZEHeG9FIJvT+WQXuLUG5v5MbG4zs21mqnYXwWAbTPaZ35bpp3
|
||||
BbbquXFtAqOZG8yfrob1g9OSgmY+bG3ZNxxv35qmbBbuWyUvmPwfjzEAclMxv48w
|
||||
/2Rs4dXkBuvc9PiNdQ9Sv+ZYG8GIqIcbRd38cSaXI4Q94BOHEr4jm1vqb54H7twv
|
||||
0Gy9CnLXfn7ZtEDFSmG8WLk2GvIna+UC+gnxSqKCw0rcTby63rQUCgdJZF8VQUVi
|
||||
18+BMNLXI4pT/P9cxTaCJC/YeuV5a3SaniOoFQIDAQABAoIBAQCoGZJC84JnnIgb
|
||||
ttZNWuWKBXbCJcDVDikOQJ9hBZbqsFg1X0CfGmQS3MHf9Ubc1Ro8zVjQh15oIEfn
|
||||
8lIpdzTeXcpxLdiW8ix3ekVJF20F6pnXY8ZP6UnTeOwamXY6QPZAtb0D9UXcvY+f
|
||||
nw+IVRD6082XS0Rmzu+peYWVXDy+FDN+HJRANBcdJZz8gOmNBIe0qDWx1b85d/s8
|
||||
2Kk1Wwdss1IwAGeSddTSwzBNaaHdItZaMZOqPW1gRyBfVSkcUQIE6zn2RKw2b70t
|
||||
grkIvyRcTdfmiKbqkkJ+eR+ITOUt0cBZSH4cDjlQA+r7hulvoBpQBRj068Toxkcc
|
||||
bTagHaPBAoGBAPWPGVkHqhTbJ/DjmqDIStxby2M1fhhHt4xUGHinhUYjQjGOtDQ9
|
||||
0mfaB7HObudRiSLydRAVGAHGyNJdQcTeFxeQbovwGiYKfZSA1IGpea7dTxPpGEdN
|
||||
ksA0pzSp9MfKzX/MdLuAkEtO58aAg5YzsgX9hDNxo4MhH/gremZhEGZlAoGBAPZf
|
||||
lqdYvAL0fjHGJ1FUEalhzGCGE9PH2iOqsxqLCXK7bDbzYSjvuiHkhYJHAOgVdiW1
|
||||
lB34UHHYAqZ1VVoFqJ05gax6DE2+r7K5VV3FUCaC0Zm3pavxchU9R/TKP82xRrBj
|
||||
AFWwdgDTxUyvQEmgPR9sqorftO71Iz2tiwyTpIfxAoGBAIhEMLzHFAse0rtKkrRG
|
||||
ccR27BbRyHeQ1Lp6sFnEHKEfT8xQdI/I/snCpCJ3e/PBu2g5Q9z416mktiyGs8ib
|
||||
thTNgYsGYnxZtfaCx2pssanoBcn2wBJRae5fSapf5gY49HDG9MBYR7qCvvvYtSzU
|
||||
4yWP2ZzyotpRt3vwJKxLkN5BAoGAORHpZvhiDNkvxj3da7Rqpu7VleJZA2y+9hYb
|
||||
iOF+HcqWhaAY+I+XcTRrTMM/zYLzLEcEeXDEyao86uwxCjpXVZw1kotvAC9UqbTO
|
||||
tnr3VwRkoxPsV4kFYTAh0+1pnC8dbcxxDmhi3Uww3tOVs7hfkEDuvF6XnebA9A+Y
|
||||
LyCgMzECgYEA6cCU8QODOivIKWFRXucvWckgE6MYDBaAwe6qcLsd1Q/gpE2e3yQc
|
||||
4RB3bcyiPROLzMLlXFxf1vSNJQdIaVfrRv+zJeGIiivLPU8+Eq4Lrb+tl1LepcOX
|
||||
OzQeADTSCn5VidOfjDkIst9UXjMlrFfV9/oJEw5Eiqa6lkNPCGDhfA8=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
private_key_type rsa
|
||||
serial_number 6f:98:9d:f8:67:1a:31:e3:27:60:1b:f7:32:f7:53:19:68:a0:c8:9d
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIBrjCBlzANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbRcN
|
||||
MTYwMjI5MDIyOTE3WhcNMjUwMTA1MTAyOTE3WjArMCkCFG+YnfhnGjHjJ2Ab9zL3
|
||||
UxlooMidFxExNjAyMjgyMTI5MTctMDUwMKAjMCEwHwYDVR0jBBgwFoAUncSzT/6H
|
||||
MexyuiU9/7EgHu+ok5swDQYJKoZIhvcNAQELBQADggEBAG9YDXpNe4LJroKZmVCn
|
||||
HqMhW8eyzyaPak2nPPGCVUnc6vt8rlBYQU+xlBizD6xatZQDMPgrT8sBl9W3ysXk
|
||||
RUlliHsT/SHddMz5dAZsBPRMJ7pYWLTx8jI4w2WRfbSyI4bY/6qTRNkEBUv+Fk8J
|
||||
xvwB89+EM0ENcVMhv9ghsUA8h7kOg673HKwRstLDAzxS/uLmEzFjj8SV2m5DbV2Y
|
||||
UUCKRSV20/kxJMIC9x2KikZhwOSyv1UE1otD+RQvbfAoZPUDmvp2FR/E0NGjBBOg
|
||||
1TtCPRrl63cjqU3s8KQ4uah9Vj+Cwcu9n/yIKKtNQq4NKHvagv8GlUsoJ4BdAxCw
|
||||
IA0=
|
||||
-----END X509 CRL-----
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDPDCCAiSgAwIBAgIUb5id+GcaMeMnYBv3MvdTGWigyJ0wDQYJKoZIhvcNAQEL
|
||||
BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTYwMjI5MDIyNzI5WhcNMjYw
|
||||
MjI2MDIyNzU5WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAOxTMvhTuIRc2YhxZpmPwegP86cgnqfT1mXxi1A7
|
||||
Q7qax24Nqbf00I3oDMQtAJlj2RB3hvRSCb0/lkF7i1Bub+TGxuM7NtZqp2F8FgG0
|
||||
z2md+W6adwW26rlxbQKjmRvMn66G9YPTkoJmPmxt2Tccb9+apmwW7lslL5j8H48x
|
||||
AHJTMb+PMP9kbOHV5Abr3PT4jXUPUr/mWBvBiKiHG0Xd/HEmlyOEPeAThxK+I5tb
|
||||
6m+eB+7cL9BsvQpy135+2bRAxUphvFi5NhryJ2vlAvoJ8UqigsNK3E28ut60FAoH
|
||||
SWRfFUFFYtfPgTDS1yOKU/z/XMU2giQv2HrleWt0mp4jqBUCAwEAAaOBgTB/MA4G
|
||||
A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSdxLNP/ocx
|
||||
7HK6JT3/sSAe76iTmzAfBgNVHSMEGDAWgBSdxLNP/ocx7HK6JT3/sSAe76iTmzAc
|
||||
BgNVHREEFTATggtleGFtcGxlLmNvbYcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEA
|
||||
wHThDRsXJunKbAapxmQ6bDxSvTvkLA6m97TXlsFgL+Q3Jrg9HoJCNowJ0pUTwhP2
|
||||
U946dCnSCkZck0fqkwVi4vJ5EQnkvyEbfN4W5qVsQKOFaFVzep6Qid4rZT6owWPa
|
||||
cNNzNcXAee3/j6hgr6OQ/i3J6fYR4YouYxYkjojYyg+CMdn6q8BoV0BTsHdnw1/N
|
||||
ScbnBHQIvIZMBDAmQueQZolgJcdOuBLYHe/kRy167z8nGg+PUFKIYOL8NaOU1+CJ
|
||||
t2YaEibVq5MRqCbRgnd9a2vG0jr5a3Mn4CUUYv+5qIjP3hUusYenW1/EWtn1s/gk
|
||||
zehNe5dFTjFpylg1o6b8Ow==
|
||||
-----END CERTIFICATE-----
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA7FMy+FO4hFzZiHFmmY/B6A/zpyCep9PWZfGLUDtDuprHbg2p
|
||||
t/TQjegMxC0AmWPZEHeG9FIJvT+WQXuLUG5v5MbG4zs21mqnYXwWAbTPaZ35bpp3
|
||||
BbbquXFtAqOZG8yfrob1g9OSgmY+bG3ZNxxv35qmbBbuWyUvmPwfjzEAclMxv48w
|
||||
/2Rs4dXkBuvc9PiNdQ9Sv+ZYG8GIqIcbRd38cSaXI4Q94BOHEr4jm1vqb54H7twv
|
||||
0Gy9CnLXfn7ZtEDFSmG8WLk2GvIna+UC+gnxSqKCw0rcTby63rQUCgdJZF8VQUVi
|
||||
18+BMNLXI4pT/P9cxTaCJC/YeuV5a3SaniOoFQIDAQABAoIBAQCoGZJC84JnnIgb
|
||||
ttZNWuWKBXbCJcDVDikOQJ9hBZbqsFg1X0CfGmQS3MHf9Ubc1Ro8zVjQh15oIEfn
|
||||
8lIpdzTeXcpxLdiW8ix3ekVJF20F6pnXY8ZP6UnTeOwamXY6QPZAtb0D9UXcvY+f
|
||||
nw+IVRD6082XS0Rmzu+peYWVXDy+FDN+HJRANBcdJZz8gOmNBIe0qDWx1b85d/s8
|
||||
2Kk1Wwdss1IwAGeSddTSwzBNaaHdItZaMZOqPW1gRyBfVSkcUQIE6zn2RKw2b70t
|
||||
grkIvyRcTdfmiKbqkkJ+eR+ITOUt0cBZSH4cDjlQA+r7hulvoBpQBRj068Toxkcc
|
||||
bTagHaPBAoGBAPWPGVkHqhTbJ/DjmqDIStxby2M1fhhHt4xUGHinhUYjQjGOtDQ9
|
||||
0mfaB7HObudRiSLydRAVGAHGyNJdQcTeFxeQbovwGiYKfZSA1IGpea7dTxPpGEdN
|
||||
ksA0pzSp9MfKzX/MdLuAkEtO58aAg5YzsgX9hDNxo4MhH/gremZhEGZlAoGBAPZf
|
||||
lqdYvAL0fjHGJ1FUEalhzGCGE9PH2iOqsxqLCXK7bDbzYSjvuiHkhYJHAOgVdiW1
|
||||
lB34UHHYAqZ1VVoFqJ05gax6DE2+r7K5VV3FUCaC0Zm3pavxchU9R/TKP82xRrBj
|
||||
AFWwdgDTxUyvQEmgPR9sqorftO71Iz2tiwyTpIfxAoGBAIhEMLzHFAse0rtKkrRG
|
||||
ccR27BbRyHeQ1Lp6sFnEHKEfT8xQdI/I/snCpCJ3e/PBu2g5Q9z416mktiyGs8ib
|
||||
thTNgYsGYnxZtfaCx2pssanoBcn2wBJRae5fSapf5gY49HDG9MBYR7qCvvvYtSzU
|
||||
4yWP2ZzyotpRt3vwJKxLkN5BAoGAORHpZvhiDNkvxj3da7Rqpu7VleJZA2y+9hYb
|
||||
iOF+HcqWhaAY+I+XcTRrTMM/zYLzLEcEeXDEyao86uwxCjpXVZw1kotvAC9UqbTO
|
||||
tnr3VwRkoxPsV4kFYTAh0+1pnC8dbcxxDmhi3Uww3tOVs7hfkEDuvF6XnebA9A+Y
|
||||
LyCgMzECgYEA6cCU8QODOivIKWFRXucvWckgE6MYDBaAwe6qcLsd1Q/gpE2e3yQc
|
||||
4RB3bcyiPROLzMLlXFxf1vSNJQdIaVfrRv+zJeGIiivLPU8+Eq4Lrb+tl1LepcOX
|
||||
OzQeADTSCn5VidOfjDkIst9UXjMlrFfV9/oJEw5Eiqa6lkNPCGDhfA8=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEEjCCAvqgAwIBAgIJAM7PFmA6Y+KeMA0GCSqGSIb3DQEBCwUAMIGWMQswCQYD
|
||||
VQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFDASBgNVBAcMC1N0b255IEJyb29r
|
||||
MRIwEAYDVQQKDAlIYXNoaUNvcnAxDjAMBgNVBAsMBVZhdWx0MRUwEwYDVQQDDAxW
|
||||
aXNoYWwgTmF5YWsxIzAhBgkqhkiG9w0BCQEWFHZpc2hhbEBoYXNoaWNvcnAuY29t
|
||||
MB4XDTE1MDgwNzE5MTk1OFoXDTE1MDkwNjE5MTk1OFowgZYxCzAJBgNVBAYTAlVT
|
||||
MREwDwYDVQQIDAhOZXcgWW9yazEUMBIGA1UEBwwLU3RvbnkgQnJvb2sxEjAQBgNV
|
||||
BAoMCUhhc2hpQ29ycDEOMAwGA1UECwwFVmF1bHQxFTATBgNVBAMMDFZpc2hhbCBO
|
||||
YXlhazEjMCEGCSqGSIb3DQEJARYUdmlzaGFsQGhhc2hpY29ycC5jb20wggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcGlPKIrsq5sDJAUB7mtLjnjbcfR0b
|
||||
dX1sDHUaTdT+2YBq0JvtoLZOmKw1iVwsMBhaLeXwnKP/O/n67sE8zvZPsuU3REw1
|
||||
NTjPof8IbepkENWNxR68KoSB2Vn5r4KiO3ux+KbkXssrZB62+k9khj0e7qIiwyZP
|
||||
y5+RQPOL2ESmX5DznX+90vH4mzAEF654PbXFI/qOBZcWvWZJ37i+lHkeyCqcB+sm
|
||||
5o5+zd1ua8jVlN0eLjyqa7FDvIuXPAFEX+r5DVQgIvS2++YaFRqTFCIxRXdDQXdw
|
||||
1xDMCuG1w4PGVWf3TtlpHeGSIU07DdrCgXsvIRYfW++aZ2pvXwJYCr8hAgMBAAGj
|
||||
YTBfMA8GA1UdEQQIMAaHBKwYFugwHQYDVR0OBBYEFPl+AkButpRfbblZE9Jb3xNj
|
||||
AyhkMB8GA1UdIwQYMBaAFPl+AkButpRfbblZE9Jb3xNjAyhkMAwGA1UdEwQFMAMB
|
||||
Af8wDQYJKoZIhvcNAQELBQADggEBADdIyyBJ3BVghW1shhxYsqQgg/gj2TagpO1P
|
||||
ulGNzS0aCfB4tzMD4MGWm7cTlL6QW9W6r9OuWKCd1ADherIX9j0gtVWgIMtWGx+i
|
||||
NbHrYin1xHr4rkB7/f6veCiJ3CtzBC9P/rEI6keyfOn1BfQBsOxfo3oGe/HDlSzD
|
||||
lpu0GlQECjTXD7dd4jrD0T/wdRQI0BmxcYjn9cZLgoJHtLHZwaS16TGVmKs4iRAW
|
||||
V9Aw5hLK4jJ59IID830/ly+Ndfc//QGgdE5PM44OrvVFO3Q8+zs7pwr1ql7uQWew
|
||||
MSuDfbL7EcEGajD/o085sj2u4xVUfkVBW+3TQvs4/pHYOxlhPjI=
|
||||
-----END CERTIFICATE-----
|
||||
Reference in New Issue
Block a user