diff options
author | Niall Sheridan <nsheridan@gmail.com> | 2016-10-04 14:37:01 -0700 |
---|---|---|
committer | Niall Sheridan <nsheridan@gmail.com> | 2016-10-06 22:02:39 -0500 |
commit | 17cd70cea546e287713a3d4c086528a85abefa2e (patch) | |
tree | f52ffa10f2065c47445bd6c37f07a57f68074100 /vendor/github.com/hashicorp/vault/api | |
parent | 294020406c257ad4eb1867a1e7fb8b694aefddd2 (diff) |
Add support for Hashicorp Vault
Vault is supported for the following:
As a well-known filesystem for TLS cert, TLS key and SSH signing key.
For configuration secrets for cookie_secret, csrf_secret, oauth_client_id and oauth_client_secret options.
Diffstat (limited to 'vendor/github.com/hashicorp/vault/api')
25 files changed, 2960 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/vault/api/SPEC.md b/vendor/github.com/hashicorp/vault/api/SPEC.md new file mode 100644 index 0000000..15345f3 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/SPEC.md @@ -0,0 +1,611 @@ +FORMAT: 1A + +# vault + +The Vault API gives you full access to the Vault project. + +If you're browsing this API specifiction in GitHub or in raw +format, please excuse some of the odd formatting. This document +is in api-blueprint format that is read by viewers such as +Apiary. + +## Sealed vs. Unsealed + +Whenever an individual Vault server is started, it is started +in the _sealed_ state. In this state, it knows where its data +is located, but the data is encrypted and Vault doesn't have the +encryption keys to access it. Before Vault can operate, it must +be _unsealed_. + +**Note:** Sealing/unsealing has no relationship to _authentication_ +which is separate and still required once the Vault is unsealed. + +Instead of being sealed with a single key, we utilize +[Shamir's Secret Sharing](http://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) +to shard a key into _n_ parts such that _t_ parts are required +to reconstruct the original key, where `t <= n`. This means that +Vault itself doesn't know the original key, and no single person +has the original key (unless `n = 1`, or `t` parts are given to +a single person). + +Unsealing is done via an unauthenticated +[unseal API](#reference/seal/unseal/unseal). This API takes a single +master shard and progresses the unsealing process. Once all shards +are given, the Vault is either unsealed or resets the unsealing +process if the key was invalid. + +The entire seal/unseal state is server-wide. This allows multiple +distinct operators to use the unseal API (or more likely the +`vault unseal` command) from separate computers/networks and never +have to transmit their key in order to unseal the vault in a +distributed fashion. + +## Transport + +The API is expected to be accessed over a TLS connection at +all times, with a valid certificate that is verified by a well +behaved client. + +## Authentication + +Once the Vault is unsealed, every other operation requires +authentication. There are multiple methods for authentication +that can be enabled (see +[authentication](#reference/authentication)). + +Authentication is done with the login endpoint. The login endpoint +returns an access token that is set as the `X-Vault-Token` header. + +## Help + +To retrieve the help for any API within Vault, including mounted +backends, credential providers, etc. then append `?help=1` to any +URL. If you have valid permission to access the path, then the help text +will be returned with the following structure: + + { + "help": "help text" + } + +## Error Response + +A common JSON structure is always returned to return errors: + + { + "errors": [ + "message", + "another message" + ] + } + +This structure will be sent down for any non-20x HTTP status. + +## HTTP Status Codes + +The following HTTP status codes are used throughout the API. + +- `200` - Success with data. +- `204` - Success, no data returned. +- `400` - Invalid request, missing or invalid data. +- `403` - Forbidden, your authentication details are either + incorrect or you don't have access to this feature. +- `404` - Invalid path. This can both mean that the path truly + doesn't exist or that you don't have permission to view a + specific path. We use 404 in some cases to avoid state leakage. +- `429` - Rate limit exceeded. Try again after waiting some period + of time. +- `500` - Internal server error. An internal error has occurred, + try again later. If the error persists, report a bug. +- `503` - Vault is down for maintenance or is currently sealed. + Try again later. + +# Group Initialization + +## Initialization [/sys/init] +### Initialization Status [GET] +Returns the status of whether the vault is initialized or not. The +vault doesn't have to be unsealed for this operation. + ++ Response 200 (application/json) + + { + "initialized": true + } + +### Initialize [POST] +Initialize the vault. This is an unauthenticated request to initially +setup a new vault. Although this is unauthenticated, it is still safe: +data cannot be in vault prior to initialization, and any future +authentication will fail if you didn't initialize it yourself. +Additionally, once initialized, a vault cannot be reinitialized. + +This API is the only time Vault will ever be aware of your keys, and +the only time the keys will ever be returned in one unit. Care should +be taken to ensure that the output of this request is never logged, +and that the keys are properly distributed. + +The response also contains the initial root token that can be used +as authentication in order to initially configure Vault once it is +unsealed. Just as with the unseal keys, this is the only time Vault is +ever aware of this token. + ++ Request (application/json) + + { + "secret_shares": 5, + "secret_threshold": 3, + } + ++ Response 200 (application/json) + + { + "keys": ["one", "two", "three"], + "root_token": "foo" + } + +# Group Seal/Unseal + +## Seal Status [/sys/seal-status] +### Seal Status [GET] +Returns the status of whether the vault is currently +sealed or not, as well as the progress of unsealing. + +The response has the following attributes: + +- sealed (boolean) - If true, the vault is sealed. Otherwise, + it is unsealed. +- t (int) - The "t" value for the master key, or the number + of shards needed total to unseal the vault. +- n (int) - The "n" value for the master key, or the total + number of shards of the key distributed. +- progress (int) - The number of master key shards that have + been entered so far towards unsealing the vault. + ++ Response 200 (application/json) + + { + "sealed": true, + "t": 3, + "n": 5, + "progress": 1 + } + +## Seal [/sys/seal] +### Seal [PUT] +Seal the vault. + +Sealing the vault locks Vault from any future operations on any +secrets or system configuration until the vault is once again +unsealed. Internally, sealing throws away the keys to access the +encrypted vault data, so Vault is unable to access the data without +unsealing to get the encryption keys. + ++ Response 204 + +## Unseal [/sys/unseal] +### Unseal [PUT] +Unseal the vault. + +Unseal the vault by entering a portion of the master key. The +response object will tell you if the unseal is complete or +only partial. + +If the vault is already unsealed, this does nothing. It is +not an error, the return value just says the vault is unsealed. +Due to the architecture of Vault, we cannot validate whether +any portion of the unseal key given is valid until all keys +are inputted, therefore unsealing an already unsealed vault +is still a success even if the input key is invalid. + ++ Request (application/json) + + { + "key": "value" + } + ++ Response 200 (application/json) + + { + "sealed": true, + "t": 3, + "n": 5, + "progress": 1 + } + +# Group Authentication + +## List Auth Methods [/sys/auth] +### List all auth methods [GET] +Lists all available authentication methods. + +This returns the name of the authentication method as well as +a human-friendly long-form help text for the method that can be +shown to the user as documentation. + ++ Response 200 (application/json) + + { + "token": { + "type": "token", + "description": "Token authentication" + }, + "oauth": { + "type": "oauth", + "description": "OAuth authentication" + } + } + +## Single Auth Method [/sys/auth/{id}] + ++ Parameters + + id (required, string) ... The ID of the auth method. + +### Enable an auth method [PUT] +Enables an authentication method. + +The body of the request depends on the authentication method +being used. Please reference the documentation for the specific +authentication method you're enabling in order to determine what +parameters you must give it. + +If an authentication method is already enabled, then this can be +used to change the configuration, including even the type of +the configuration. + ++ Request (application/json) + + { + "type": "type", + "key": "value", + "key2": "value2" + } + ++ Response 204 + +### Disable an auth method [DELETE] +Disables an authentication method. Previously authenticated sessions +are immediately invalidated. + ++ Response 204 + +# Group Policies + +Policies are named permission sets that identities returned by +credential stores are bound to. This separates _authentication_ +from _authorization_. + +## Policies [/sys/policy] +### List all Policies [GET] + +List all the policies. + ++ Response 200 (application/json) + + { + "policies": ["root"] + } + +## Single Policy [/sys/policy/{id}] + ++ Parameters + + id (required, string) ... The name of the policy + +### Upsert [PUT] + +Create or update a policy with the given ID. + ++ Request (application/json) + + { + "rules": "HCL" + } + ++ Response 204 + +### Delete [DELETE] + +Delete a policy with the given ID. Any identities bound to this +policy will immediately become "deny all" despite already being +authenticated. + ++ Response 204 + +# Group Mounts + +Logical backends are mounted at _mount points_, similar to +filesystems. This allows you to mount the "aws" logical backend +at the "aws-us-east" path, so all access is at `/aws-us-east/keys/foo` +for example. This enables multiple logical backends to be enabled. + +## Mounts [/sys/mounts] +### List all mounts [GET] + +Lists all the active mount points. + ++ Response 200 (application/json) + + { + "aws": { + "type": "aws", + "description": "AWS" + }, + "pg": { + "type": "postgresql", + "description": "PostgreSQL dynamic users" + } + } + +## Single Mount [/sys/mounts/{path}] +### New Mount [POST] + +Mount a logical backend to a new path. + +Configuration for this new backend is done via the normal +read/write mechanism once it is mounted. + ++ Request (application/json) + + { + "type": "aws", + "description": "EU AWS tokens" + } + ++ Response 204 + +### Unmount [DELETE] + +Unmount a mount point. + ++ Response 204 + +## Remount [/sys/remount] +### Remount [POST] + +Move an already-mounted backend to a new path. + ++ Request (application/json) + + { + "from": "aws", + "to": "aws-east" + } + ++ Response 204 + +# Group Audit Backends + +Audit backends are responsible for shuttling the audit logs that +Vault generates to a durable system for future querying. By default, +audit logs are not stored anywhere. + +## Audit Backends [/sys/audit] +### List Enabled Audit Backends [GET] + +List all the enabled audit backends + ++ Response 200 (application/json) + + { + "file": { + "type": "file", + "description": "Send audit logs to a file", + "options": {} + } + } + +## Single Audit Backend [/sys/audit/{path}] + ++ Parameters + + path (required, string) ... The path where the audit backend is mounted + +### Enable [PUT] + +Enable an audit backend. + ++ Request (application/json) + + { + "type": "file", + "description": "send to a file", + "options": { + "path": "/var/log/vault.audit.log" + } + } + ++ Response 204 + +### Disable [DELETE] + +Disable an audit backend. + ++ Request (application/json) + ++ Response 204 + +# Group Secrets + +## Generic [/{mount}/{path}] + +This group documents the general format of reading and writing +to Vault. The exact structure of the keyspace is defined by the +logical backends in use, so documentation related to +a specific backend should be referenced for details on what keys +and routes are expected. + +The path for examples are `/prefix/path`, but in practice +these will be defined by the backends that are mounted. For +example, reading an AWS key might be at the `/aws/root` path. +These paths are defined by the logical backends. + ++ Parameters + + mount (required, string) ... The mount point for the + logical backend. Example: `aws`. + + path (optional, string) ... The path within the backend + to read or write data. + +### Read [GET] + +Read data from vault. + +The data read from the vault can either be a secret or +arbitrary configuration data. The type of data returned +depends on the path, and is defined by the logical backend. + +If the return value is a secret, then the return structure +is a mixture of arbitrary key/value along with the following +fields which are guaranteed to exist: + +- `lease_id` (string) - A unique ID used for renewal and + revocation. + +- `renewable` (bool) - If true, then this key can be renewed. + If a key can't be renewed, then a new key must be requested + after the lease duration period. + +- `lease_duration` (int) - The time in seconds that a secret is + valid for before it must be renewed. + +- `lease_duration_max` (int) - The maximum amount of time in + seconds that a secret is valid for. This will always be + greater than or equal to `lease_duration`. The difference + between this and `lease_duration` is an overlap window + where multiple keys may be valid. + +If the return value is not a secret, then the return structure +is an arbitrary JSON object. + ++ Response 200 (application/json) + + { + "lease_id": "UUID", + "lease_duration": 3600, + "key": "value" + } + +### Write [PUT] + +Write data to vault. + +The behavior and arguments to the write are defined by +the logical backend. + ++ Request (application/json) + + { + "key": "value" + } + ++ Response 204 + +# Group Lease Management + +## Renew Key [/sys/renew/{id}] + ++ Parameters + + id (required, string) ... The `lease_id` of the secret + to renew. + +### Renew [PUT] + ++ Response 200 (application/json) + + { + "lease_id": "...", + "lease_duration": 3600, + "access_key": "foo", + "secret_key": "bar" + } + +## Revoke Key [/sys/revoke/{id}] + ++ Parameters + + id (required, string) ... The `lease_id` of the secret + to revoke. + +### Revoke [PUT] + ++ Response 204 + +# Group Backend: AWS + +## Root Key [/aws/root] +### Set the Key [PUT] + +Set the root key that the logical backend will use to create +new secrets, IAM policies, etc. + ++ Request (application/json) + + { + "access_key": "key", + "secret_key": "key", + "region": "us-east-1" + } + ++ Response 204 + +## Policies [/aws/policies] +### List Policies [GET] + +List all the policies that can be used to create keys. + ++ Response 200 (application/json) + + [{ + "name": "root", + "description": "Root access" + }, { + "name": "web-deploy", + "description": "Enough permissions to deploy the web app." + }] + +## Single Policy [/aws/policies/{name}] + ++ Parameters + + name (required, string) ... Name of the policy. + +### Read [GET] + +Read a policy. + ++ Response 200 (application/json) + + { + "policy": "base64-encoded policy" + } + +### Upsert [PUT] + +Create or update a policy. + ++ Request (application/json) + + { + "policy": "base64-encoded policy" + } + ++ Response 204 + +### Delete [DELETE] + +Delete the policy with the given name. + ++ Response 204 + +## Generate Access Keys [/aws/keys/{policy}] +### Create [GET] + +This generates a new keypair for the given policy. + ++ Parameters + + policy (required, string) ... The policy under which to create + the key pair. + ++ Response 200 (application/json) + + { + "lease_id": "...", + "lease_duration": 3600, + "access_key": "foo", + "secret_key": "bar" + } diff --git a/vendor/github.com/hashicorp/vault/api/auth.go b/vendor/github.com/hashicorp/vault/api/auth.go new file mode 100644 index 0000000..da870c1 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/auth.go @@ -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} +} diff --git a/vendor/github.com/hashicorp/vault/api/auth_token.go b/vendor/github.com/hashicorp/vault/api/auth_token.go new file mode 100644 index 0000000..aff10f4 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/auth_token.go @@ -0,0 +1,223 @@ +package api + +// 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 + } + + resp, err := c.c.RawRequest(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 + } + + resp, err := c.c.RawRequest(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 + } + + resp, err := c.c.RawRequest(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 + } + + resp, err := c.c.RawRequest(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 + } + resp, err := c.c.RawRequest(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") + + resp, err := c.c.RawRequest(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 + } + + resp, err := c.c.RawRequest(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 + } + + resp, err := c.c.RawRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + return ParseSecret(resp.Body) +} + +// RevokeAccessor revokes a token associated with the given accessor +// along with all the child tokens. +func (c *TokenAuth) RevokeAccessor(accessor string) error { + r := c.c.NewRequest("POST", "/v1/auth/token/revoke-accessor") + if err := r.SetJSONBody(map[string]interface{}{ + "accessor": accessor, + }); err != nil { + return err + } + resp, err := c.c.RawRequest(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 + } + + resp, err := c.c.RawRequest(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") + resp, err := c.c.RawRequest(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 + } + + resp, err := c.c.RawRequest(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"` +} diff --git a/vendor/github.com/hashicorp/vault/api/client.go b/vendor/github.com/hashicorp/vault/api/client.go new file mode 100644 index 0000000..4aee40c --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/client.go @@ -0,0 +1,416 @@ +package api + +import ( + "crypto/tls" + "fmt" + "net/http" + "net/url" + "os" + "strconv" + "strings" + "sync" + "time" + + "github.com/hashicorp/go-cleanhttp" + "github.com/hashicorp/go-rootcerts" + "github.com/sethgrid/pester" +) + +const EnvVaultAddress = "VAULT_ADDR" +const EnvVaultCACert = "VAULT_CACERT" +const EnvVaultCAPath = "VAULT_CAPATH" +const EnvVaultClientCert = "VAULT_CLIENT_CERT" +const EnvVaultClientKey = "VAULT_CLIENT_KEY" +const EnvVaultInsecure = "VAULT_SKIP_VERIFY" +const EnvVaultTLSServerName = "VAULT_TLS_SERVER_NAME" +const EnvVaultWrapTTL = "VAULT_WRAP_TTL" +const EnvVaultMaxRetries = "VAULT_MAX_RETRIES" + +// 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 { + // 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, which will currently always have the + // same values as http.DefaultClient. This is used to control redirect behavior. + HttpClient *http.Client + + redirectSetup sync.Once + + // MaxRetries controls the maximum number of times to retry when a 5xx error + // occurs. Set to 0 or less to disable retrying. + MaxRetries int +} + +// 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. +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 v := os.Getenv(EnvVaultAddress); v != "" { + config.Address = v + } + + config.MaxRetries = pester.DefaultClient.MaxRetries + + 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 { + return fmt.Errorf("config HTTP Client must be set") + } + + var clientCert tls.Certificate + foundClientCert := false + if t.CACert != "" || t.CAPath != "" || t.ClientCert != "" || t.ClientKey != "" || t.Insecure { + if t.ClientCert != "" && t.ClientKey != "" { + var err error + clientCert, err = tls.LoadX509KeyPair(t.ClientCert, t.ClientKey) + if err != nil { + return err + } + foundClientCert = true + } else if t.ClientCert != "" || t.ClientKey != "" { + return fmt.Errorf("Both client cert and client key must be provided") + } + } + + clientTLSConfig := c.HttpClient.Transport.(*http.Transport).TLSClientConfig + rootConfig := &rootcerts.Config{ + CAFile: t.CACert, + CAPath: t.CAPath, + } + if err := rootcerts.ConfigureTLS(clientTLSConfig, rootConfig); err != nil { + return err + } + + clientTLSConfig.InsecureSkipVerify = t.Insecure + + if foundClientCert { + clientTLSConfig.Certificates = []tls.Certificate{clientCert} + } + 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 envInsecure bool + var envTLSServerName string + var envMaxRetries *uint64 + + // 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(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, + } + if err := c.ConfigureTLS(t); err != nil { + return err + } + + if envAddress != "" { + c.Address = envAddress + } + + if envMaxRetries != nil { + c.MaxRetries = int(*envMaxRetries) + 1 + } + + return nil +} + +// Client is the client to the Vault API. Create a client with +// NewClient. +type Client struct { + addr *url.URL + config *Config + token string + wrappingLookupFunc WrappingLookupFunc +} + +// NewClient returns a new client for the given 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) { + if c == nil { + c = DefaultConfig() + if err := c.ReadEnvironment(); err != nil { + return nil, fmt.Errorf("error reading environment: %v", err) + } + } + + u, err := url.Parse(c.Address) + if err != nil { + return nil, err + } + + if c.HttpClient == nil { + c.HttpClient = DefaultConfig().HttpClient + } + + redirFunc := func() { + // 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 + c.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { + // Returning this value causes the Go net library to not close the + // response body and nil out the error. Otherwise pester tries + // three times on every redirect because it sees an error from this + // function being passed through. + return http.ErrUseLastResponse + } + } + + c.redirectSetup.Do(redirFunc) + + client := &Client{ + addr: u, + config: c, + } + + if token := os.Getenv("VAULT_TOKEN"); token != "" { + client.SetToken(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 { + var err error + if c.addr, err = url.Parse(addr); err != nil { + return fmt.Errorf("failed to set address: %v", err) + } + + return nil +} + +// SetWrappingLookupFunc sets a lookup function that returns desired wrap TTLs +// for a given operation and path +func (c *Client) SetWrappingLookupFunc(lookupFunc WrappingLookupFunc) { + c.wrappingLookupFunc = lookupFunc +} + +// 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 { + 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.token = v +} + +// ClearToken deletes the token if it is set or does nothing otherwise. +func (c *Client) ClearToken() { + c.token = "" +} + +// NewRequest creates a new raw request object to query the Vault server +// configured for this client. This is an advanced method and generally +// doesn't need to be called externally. +func (c *Client) NewRequest(method, path string) *Request { + req := &Request{ + Method: method, + URL: &url.URL{ + Scheme: c.addr.Scheme, + Host: c.addr.Host, + Path: path, + }, + ClientToken: c.token, + Params: make(map[string][]string), + } + + var lookupPath string + switch { + case strings.HasPrefix(path, "/v1/"): + lookupPath = strings.TrimPrefix(path, "/v1/") + case strings.HasPrefix(path, "v1/"): + lookupPath = strings.TrimPrefix(path, "v1/") + default: + lookupPath = path + } + if c.wrappingLookupFunc != nil { + req.WrapTTL = c.wrappingLookupFunc(method, lookupPath) + } else { + req.WrapTTL = DefaultWrappingLookupFunc(method, lookupPath) + } + + 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) { + redirectCount := 0 +START: + req, err := r.ToHTTP() + if err != nil { + return nil, err + } + + client := pester.NewExtendedClient(c.config.HttpClient) + client.Backoff = pester.LinearJitterBackoff + client.MaxRetries = c.config.MaxRetries + + 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 = fmt.Errorf( + "%s\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 +} diff --git a/vendor/github.com/hashicorp/vault/api/help.go b/vendor/github.com/hashicorp/vault/api/help.go new file mode 100644 index 0000000..b9ae100 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/help.go @@ -0,0 +1,25 @@ +package api + +import ( + "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") + resp, err := c.RawRequest(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"` +} diff --git a/vendor/github.com/hashicorp/vault/api/logical.go b/vendor/github.com/hashicorp/vault/api/logical.go new file mode 100644 index 0000000..f1cea7d --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/logical.go @@ -0,0 +1,176 @@ +package api + +import ( + "bytes" + "fmt" + "net/http" + "os" + + "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) + resp, err := c.c.RawRequest(r) + if resp != nil { + defer resp.Body.Close() + } + if resp != nil && resp.StatusCode == 404 { + 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") + resp, err := c.c.RawRequest(r) + if resp != nil { + defer resp.Body.Close() + } + if resp != nil && resp.StatusCode == 404 { + 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 + } + + resp, err := c.c.RawRequest(r) + if resp != nil { + defer resp.Body.Close() + } + if err != nil { + return nil, err + } + + if resp.StatusCode == 200 { + return ParseSecret(resp.Body) + } + + return nil, nil +} + +func (c *Logical) Delete(path string) (*Secret, error) { + r := c.c.NewRequest("DELETE", "/v1/"+path) + resp, err := c.c.RawRequest(r) + if resp != nil { + defer resp.Body.Close() + } + if err != nil { + return nil, err + } + + if resp.StatusCode == 200 { + return ParseSecret(resp.Body) + } + + return nil, nil +} + +func (c *Logical) Unwrap(wrappingToken string) (*Secret, error) { + var data map[string]interface{} + if wrappingToken != "" { + 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 + } + + resp, err := c.c.RawRequest(r) + if resp != nil { + defer resp.Body.Close() + } + if err != nil && resp.StatusCode != 404 { + return nil, err + } + + switch resp.StatusCode { + case http.StatusOK: // New method is supported + return ParseSecret(resp.Body) + case http.StatusNotFound: // Fall back to old method + default: + return nil, nil + } + + 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, fmt.Errorf("error reading %s: %s", wrappedResponseLocation, err) + } + if secret == nil { + return nil, fmt.Errorf("no value found at %s", 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, fmt.Errorf("error unmarshaling wrapped secret: %s", err) + } + + return wrappedSecret, nil +} diff --git a/vendor/github.com/hashicorp/vault/api/request.go b/vendor/github.com/hashicorp/vault/api/request.go new file mode 100644 index 0000000..8f22dd5 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/request.go @@ -0,0 +1,71 @@ +package api + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "net/url" +) + +// 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 + ClientToken string + WrapTTL string + Obj interface{} + Body io.Reader + BodySize int64 +} + +// SetJSONBody is used to set a request body that is a JSON-encoded value. +func (r *Request) SetJSONBody(val interface{}) error { + buf := bytes.NewBuffer(nil) + enc := json.NewEncoder(buf) + if err := enc.Encode(val); err != nil { + return err + } + + r.Obj = val + r.Body = buf + r.BodySize = int64(buf.Len()) + return nil +} + +// ResetJSONBody is used to reset the body for a redirect +func (r *Request) ResetJSONBody() error { + if r.Body == nil { + return nil + } + return r.SetJSONBody(r.Obj) +} + +// ToHTTP turns this request into a valid *http.Request for use with the +// net/http package. +func (r *Request) ToHTTP() (*http.Request, error) { + // Encode the query parameters + r.URL.RawQuery = r.Params.Encode() + + // Create the HTTP request + req, err := http.NewRequest(r.Method, r.URL.RequestURI(), r.Body) + if err != nil { + return nil, err + } + + req.URL.Scheme = r.URL.Scheme + req.URL.Host = r.URL.Host + req.Host = r.URL.Host + + 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) + } + + return req, nil +} diff --git a/vendor/github.com/hashicorp/vault/api/response.go b/vendor/github.com/hashicorp/vault/api/response.go new file mode 100644 index 0000000..7c8ac9f --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/response.go @@ -0,0 +1,72 @@ +package api + +import ( + "bytes" + "fmt" + "io" + "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 + if r.StatusCode >= 200 && r.StatusCode < 400 { + 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. + var bodyBuf bytes.Buffer + if _, err := io.Copy(&bodyBuf, r.Body); err != nil { + return err + } + + // 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 +} diff --git a/vendor/github.com/hashicorp/vault/api/secret.go b/vendor/github.com/hashicorp/vault/api/secret.go new file mode 100644 index 0000000..14924f9 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/secret.go @@ -0,0 +1,68 @@ +package api + +import ( + "io" + "time" + + "github.com/hashicorp/vault/helper/jsonutil" +) + +// 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"` +} + +// 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"` + TTL int `json:"ttl"` + CreationTime time.Time `json:"creation_time"` + 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"` + 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 decode the JSON into a map[string]interface{} + var secret Secret + if err := jsonutil.DecodeJSONFromReader(r, &secret); err != nil { + return nil, err + } + + return &secret, nil +} diff --git a/vendor/github.com/hashicorp/vault/api/ssh.go b/vendor/github.com/hashicorp/vault/api/ssh.go new file mode 100644 index 0000000..7c3e56b --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/ssh.go @@ -0,0 +1,38 @@ +package api + +import "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 + } + + resp, err := c.c.RawRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + return ParseSecret(resp.Body) +} diff --git a/vendor/github.com/hashicorp/vault/api/ssh_agent.go b/vendor/github.com/hashicorp/vault/api/ssh_agent.go new file mode 100644 index 0000000..729fd99 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/ssh_agent.go @@ -0,0 +1,257 @@ +package api + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "os" + + "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/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" structs:"message" mapstructure:"message"` + + // Username associated with the OTP + Username string `json:"username" structs:"username" mapstructure:"username"` + + // IP associated with the OTP + IP string `json:"ip" structs:"ip" mapstructure:"ip"` + + // Name of the role against which the OTP was issued + RoleName string `json:"role_name" structs:"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, fmt.Errorf("ssh_helper: error parsing config: %s", err) + } + + list, ok := root.Node.(*ast.ObjectList) + if !ok { + return nil, fmt.Errorf("ssh_helper: 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 := 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("ssh_helper: 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 + } + + resp, err := c.c.RawRequest(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 +} + +func checkHCLKeys(node ast.Node, valid []string) error { + var list *ast.ObjectList + switch n := node.(type) { + case *ast.ObjectList: + list = n + case *ast.ObjectType: + list = n.List + default: + return fmt.Errorf("cannot check HCL keys of type %T", n) + } + + validMap := make(map[string]struct{}, len(valid)) + for _, v := range valid { + validMap[v] = struct{}{} + } + + var result error + for _, item := range list.Items { + key := item.Keys[0].Token.Value().(string) + if _, ok := validMap[key]; !ok { + result = multierror.Append(result, fmt.Errorf( + "invalid key '%s' on line %d", key, item.Assign.Line)) + } + } + + return result +} diff --git a/vendor/github.com/hashicorp/vault/api/sys.go b/vendor/github.com/hashicorp/vault/api/sys.go new file mode 100644 index 0000000..5fb1118 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys.go @@ -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} +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_audit.go b/vendor/github.com/hashicorp/vault/api/sys_audit.go new file mode 100644 index 0000000..1ffdef8 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_audit.go @@ -0,0 +1,114 @@ +package api + +import ( + "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 + } + + resp, err := c.c.RawRequest(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") + resp, err := c.c.RawRequest(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 +} + +func (c *Sys) EnableAudit( + path string, auditType string, desc string, opts map[string]string) error { + body := map[string]interface{}{ + "type": auditType, + "description": desc, + "options": opts, + } + + r := c.c.NewRequest("PUT", fmt.Sprintf("/v1/sys/audit/%s", path)) + if err := r.SetJSONBody(body); err != nil { + return err + } + + resp, err := c.c.RawRequest(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)) + resp, err := c.c.RawRequest(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 Audit struct { + Path string + Type string + Description string + Options map[string]string +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_auth.go b/vendor/github.com/hashicorp/vault/api/sys_auth.go new file mode 100644 index 0000000..1940e84 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_auth.go @@ -0,0 +1,87 @@ +package api + +import ( + "fmt" + + "github.com/mitchellh/mapstructure" +) + +func (c *Sys) ListAuth() (map[string]*AuthMount, error) { + r := c.c.NewRequest("GET", "/v1/sys/auth") + resp, err := c.c.RawRequest(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 +} + +func (c *Sys) EnableAuth(path, authType, desc string) error { + body := map[string]string{ + "type": authType, + "description": desc, + } + + r := c.c.NewRequest("POST", fmt.Sprintf("/v1/sys/auth/%s", path)) + if err := r.SetJSONBody(body); err != nil { + return err + } + + resp, err := c.c.RawRequest(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)) + resp, err := c.c.RawRequest(r) + if err == nil { + defer resp.Body.Close() + } + return err +} + +// Structures for the requests/resposne are all down here. They aren't +// individually documentd because the map almost directly to the raw HTTP API +// documentation. Please refer to that documentation for more details. + +type AuthMount struct { + Type string `json:"type" structs:"type" mapstructure:"type"` + Description string `json:"description" structs:"description" mapstructure:"description"` + Config AuthConfigOutput `json:"config" structs:"config" mapstructure:"config"` +} + +type AuthConfigOutput struct { + DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` + MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_capabilities.go b/vendor/github.com/hashicorp/vault/api/sys_capabilities.go new file mode 100644 index 0000000..80f6218 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_capabilities.go @@ -0,0 +1,43 @@ +package api + +import "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 + } + + resp, err := c.c.RawRequest(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 capabilities []string + capabilitiesRaw := result["capabilities"].([]interface{}) + for _, capability := range capabilitiesRaw { + capabilities = append(capabilities, capability.(string)) + } + return capabilities, nil +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_generate_root.go b/vendor/github.com/hashicorp/vault/api/sys_generate_root.go new file mode 100644 index 0000000..8dc2095 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_generate_root.go @@ -0,0 +1,77 @@ +package api + +func (c *Sys) GenerateRootStatus() (*GenerateRootStatusResponse, error) { + r := c.c.NewRequest("GET", "/v1/sys/generate-root/attempt") + resp, err := c.c.RawRequest(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) { + body := map[string]interface{}{ + "otp": otp, + "pgp_key": pgpKey, + } + + r := c.c.NewRequest("PUT", "/v1/sys/generate-root/attempt") + if err := r.SetJSONBody(body); err != nil { + return nil, err + } + + resp, err := c.c.RawRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var result GenerateRootStatusResponse + err = resp.DecodeJSON(&result) + return &result, err +} + +func (c *Sys) GenerateRootCancel() error { + r := c.c.NewRequest("DELETE", "/v1/sys/generate-root/attempt") + resp, err := c.c.RawRequest(r) + if err == nil { + defer resp.Body.Close() + } + return err +} + +func (c *Sys) GenerateRootUpdate(shard, nonce string) (*GenerateRootStatusResponse, error) { + body := map[string]interface{}{ + "key": shard, + "nonce": nonce, + } + + r := c.c.NewRequest("PUT", "/v1/sys/generate-root/update") + if err := r.SetJSONBody(body); err != nil { + return nil, err + } + + resp, err := c.c.RawRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var result GenerateRootStatusResponse + err = resp.DecodeJSON(&result) + return &result, err +} + +type GenerateRootStatusResponse struct { + Nonce string + Started bool + Progress int + Required int + Complete bool + EncodedRootToken string `json:"encoded_root_token"` + PGPFingerprint string `json:"pgp_fingerprint"` +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_init.go b/vendor/github.com/hashicorp/vault/api/sys_init.go new file mode 100644 index 0000000..f824ab7 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_init.go @@ -0,0 +1,54 @@ +package api + +func (c *Sys) InitStatus() (bool, error) { + r := c.c.NewRequest("GET", "/v1/sys/init") + resp, err := c.c.RawRequest(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 + } + + resp, err := c.c.RawRequest(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"` +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_leader.go b/vendor/github.com/hashicorp/vault/api/sys_leader.go new file mode 100644 index 0000000..201ac73 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_leader.go @@ -0,0 +1,20 @@ +package api + +func (c *Sys) Leader() (*LeaderResponse, error) { + r := c.c.NewRequest("GET", "/v1/sys/leader") + resp, err := c.c.RawRequest(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"` +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_lease.go b/vendor/github.com/hashicorp/vault/api/sys_lease.go new file mode 100644 index 0000000..e5c19c4 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_lease.go @@ -0,0 +1,48 @@ +package api + +func (c *Sys) Renew(id string, increment int) (*Secret, error) { + r := c.c.NewRequest("PUT", "/v1/sys/renew") + + body := map[string]interface{}{ + "increment": increment, + "lease_id": id, + } + if err := r.SetJSONBody(body); err != nil { + return nil, err + } + + resp, err := c.c.RawRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + return ParseSecret(resp.Body) +} + +func (c *Sys) Revoke(id string) error { + r := c.c.NewRequest("PUT", "/v1/sys/revoke/"+id) + resp, err := c.c.RawRequest(r) + if err == nil { + defer resp.Body.Close() + } + return err +} + +func (c *Sys) RevokePrefix(id string) error { + r := c.c.NewRequest("PUT", "/v1/sys/revoke-prefix/"+id) + resp, err := c.c.RawRequest(r) + if err == nil { + defer resp.Body.Close() + } + return err +} + +func (c *Sys) RevokeForce(id string) error { + r := c.c.NewRequest("PUT", "/v1/sys/revoke-force/"+id) + resp, err := c.c.RawRequest(r) + if err == nil { + defer resp.Body.Close() + } + return err +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_mounts.go b/vendor/github.com/hashicorp/vault/api/sys_mounts.go new file mode 100644 index 0000000..ca5e427 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_mounts.go @@ -0,0 +1,142 @@ +package api + +import ( + "fmt" + + "github.com/fatih/structs" + "github.com/mitchellh/mapstructure" +) + +func (c *Sys) ListMounts() (map[string]*MountOutput, error) { + r := c.c.NewRequest("GET", "/v1/sys/mounts") + resp, err := c.c.RawRequest(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 { + body := structs.Map(mountInfo) + + r := c.c.NewRequest("POST", fmt.Sprintf("/v1/sys/mounts/%s", path)) + if err := r.SetJSONBody(body); err != nil { + return err + } + + resp, err := c.c.RawRequest(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)) + resp, err := c.c.RawRequest(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 + } + + resp, err := c.c.RawRequest(r) + if err == nil { + defer resp.Body.Close() + } + return err +} + +func (c *Sys) TuneMount(path string, config MountConfigInput) error { + body := structs.Map(config) + r := c.c.NewRequest("POST", fmt.Sprintf("/v1/sys/mounts/%s/tune", path)) + if err := r.SetJSONBody(body); err != nil { + return err + } + + resp, err := c.c.RawRequest(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)) + + resp, err := c.c.RawRequest(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" structs:"type"` + Description string `json:"description" structs:"description"` + Config MountConfigInput `json:"config" structs:"config"` +} + +type MountConfigInput struct { + DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` + MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` +} + +type MountOutput struct { + Type string `json:"type" structs:"type"` + Description string `json:"description" structs:"description"` + Config MountConfigOutput `json:"config" structs:"config"` +} + +type MountConfigOutput struct { + DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` + MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_policy.go b/vendor/github.com/hashicorp/vault/api/sys_policy.go new file mode 100644 index 0000000..ba0e17f --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_policy.go @@ -0,0 +1,95 @@ +package api + +import "fmt" + +func (c *Sys) ListPolicies() ([]string, error) { + r := c.c.NewRequest("GET", "/v1/sys/policy") + resp, err := c.c.RawRequest(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)) + resp, err := c.c.RawRequest(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 + } + + var ok bool + if _, ok = result["rules"]; !ok { + return "", fmt.Errorf("rules not found in response") + } + + return result["rules"].(string), nil +} + +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 + } + + resp, err := c.c.RawRequest(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)) + resp, err := c.c.RawRequest(r) + if err == nil { + defer resp.Body.Close() + } + return err +} + +type getPoliciesResp struct { + Rules string `json:"rules"` +} + +type listPoliciesResp struct { + Policies []string `json:"policies"` +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_rekey.go b/vendor/github.com/hashicorp/vault/api/sys_rekey.go new file mode 100644 index 0000000..e6d039e --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_rekey.go @@ -0,0 +1,202 @@ +package api + +func (c *Sys) RekeyStatus() (*RekeyStatusResponse, error) { + r := c.c.NewRequest("GET", "/v1/sys/rekey/init") + resp, err := c.c.RawRequest(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") + resp, err := c.c.RawRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var result RekeyStatusResponse + 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 + } + + resp, err := c.c.RawRequest(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 + } + + resp, err := c.c.RawRequest(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") + resp, err := c.c.RawRequest(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") + resp, err := c.c.RawRequest(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 + } + + resp, err := c.c.RawRequest(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 + } + + resp, err := c.c.RawRequest(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") + resp, err := c.c.RawRequest(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") + resp, err := c.c.RawRequest(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") + resp, err := c.c.RawRequest(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") + resp, err := c.c.RawRequest(r) + if err == nil { + defer resp.Body.Close() + } + + return err +} + +type RekeyInitRequest struct { + SecretShares int `json:"secret_shares"` + SecretThreshold int `json:"secret_threshold"` + PGPKeys []string `json:"pgp_keys"` + Backup bool +} + +type RekeyStatusResponse struct { + Nonce string + Started bool + T int + N int + Progress int + Required int + PGPFingerprints []string `json:"pgp_fingerprints"` + Backup bool +} + +type RekeyUpdateResponse struct { + Nonce string + Complete bool + Keys []string + KeysB64 []string `json:"keys_base64"` + PGPFingerprints []string `json:"pgp_fingerprints"` + Backup bool +} + +type RekeyRetrieveResponse struct { + Nonce string + Keys map[string][]string + KeysB64 map[string][]string `json:"keys_base64"` +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_rotate.go b/vendor/github.com/hashicorp/vault/api/sys_rotate.go new file mode 100644 index 0000000..8108dce --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_rotate.go @@ -0,0 +1,30 @@ +package api + +import "time" + +func (c *Sys) Rotate() error { + r := c.c.NewRequest("POST", "/v1/sys/rotate") + resp, err := c.c.RawRequest(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") + resp, err := c.c.RawRequest(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"` +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_seal.go b/vendor/github.com/hashicorp/vault/api/sys_seal.go new file mode 100644 index 0000000..b80e33a --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_seal.go @@ -0,0 +1,59 @@ +package api + +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") + resp, err := c.c.RawRequest(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) { + resp, err := c.c.RawRequest(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var result SealStatusResponse + err = resp.DecodeJSON(&result) + return &result, err +} + +type SealStatusResponse struct { + Sealed bool `json:"sealed"` + T int `json:"t"` + N int `json:"n"` + Progress int `json:"progress"` + Version string `json:"version"` + ClusterName string `json:"cluster_name,omitempty"` + ClusterID string `json:"cluster_id,omitempty"` +} diff --git a/vendor/github.com/hashicorp/vault/api/sys_stepdown.go b/vendor/github.com/hashicorp/vault/api/sys_stepdown.go new file mode 100644 index 0000000..421e5f1 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/api/sys_stepdown.go @@ -0,0 +1,10 @@ +package api + +func (c *Sys) StepDown() error { + r := c.c.NewRequest("PUT", "/v1/sys/step-down") + resp, err := c.c.RawRequest(r) + if err == nil { + defer resp.Body.Close() + } + return err +} |