Merge branch 'master' into database-refactor

This commit is contained in:
Brian Kassouf 2017-04-12 14:29:10 -07:00
commit f2401c0128
306 changed files with 13849 additions and 15001 deletions

View File

@ -1,4 +1,24 @@
## 0.7.0 (Unreleased)
## 0.7.1 (Unreleased)
IMPROVEMENTS:
* storage/s3: Support `max_parallel` option to limit concurrent outstanding
requests [GH-2466]
BUG FIXES:
* storage/etcd3: Ensure locks are released if client is improperly shut down
[GH-2526]
## 0.7.0 (March 21th, 2017)
SECURITY:
* Common name not being validated when `exclude_cn_from_sans` option used in
`pki` backend: When using a role in the `pki` backend that specified the
`exclude_cn_from_sans` option, the common name would not then be properly
validated against the role's constraints. This has been fixed. We recommend
any users of this feature to upgrade to 0.7 as soon as feasible.
DEPRECATIONS/CHANGES:
@ -56,6 +76,10 @@ FEATURES:
IMPROVEMENTS:
* api/request: Passing username and password information in API request
[GH-2469]
* audit: Logging the token's use count with authentication response and
logging the remaining uses of the client token with request [GH-2437]
* auth/approle: Support for restricting the number of uses on the tokens
issued [GH-2435]
* auth/aws-ec2: AWS EC2 auth backend now supports constraints for VPC ID,
@ -66,16 +90,23 @@ IMPROVEMENTS:
* audit: Support adding a configurable prefix (such as `@cee`) before each
line [GH-2359]
* core: Canonicalize list operations to use a trailing slash [GH-2390]
* core: Add option to disable caching on a per-mount level [GH-2455]
* core: Add ability to require valid client certs in listener config [GH-2457]
* physical/dynamodb: Implement a session timeout to avoid having to use
recovery mode in the case of an unclean shutdown, which makes HA much safer
[GH-2141]
* secret/pki: O (Organization) values can now be set to role-defined values
for issued/signed certificates [GH-2369]
* secret/pki: Certificates issued/signed from PKI backend does not generate
* secret/pki: Certificates issued/signed from PKI backend do not generate
leases by default [GH-2403]
* secret/pki: When using DER format, still return the private key type
[GH-2405]
* secret/pki: Add an intermediate to the CA chain even if it lacks an
authority key ID [GH-2465]
* secret/pki: Add role option to use CSR SANs [GH-2489]
* secret/ssh: SSH backend as CA to sign user and host certificates [GH-2208]
* secret/ssh: Support reading of SSH CA public key from `config/ca` endpoint
and also return it when CA key pair is generated [GH-2483]
BUG FIXES:

View File

@ -22,7 +22,7 @@ dev-dynamic: generate
# test runs the unit tests and vets the code
test: generate
CGO_ENABLED=0 VAULT_TOKEN= VAULT_ACC= go test -tags='$(BUILD_TAGS)' $(TEST) $(TESTARGS) -timeout=10m -parallel=4
CGO_ENABLED=0 VAULT_TOKEN= VAULT_ACC= go test -tags='$(BUILD_TAGS)' $(TEST) $(TESTARGS) -timeout=20m -parallel=4
testcompile: generate
@for pkg in $(TEST) ; do \

View File

@ -9,7 +9,7 @@ Vault [![Build Status](https://travis-ci.org/hashicorp/vault.svg)](https://travi
- Announcement list: [Google Groups](https://groups.google.com/group/hashicorp-announce)
- Discussion list: [Google Groups](https://groups.google.com/group/vault-tool)
![Vault](https://raw.githubusercontent.com/hashicorp/vault/master/website/source/assets/images/logo-big.png?token=AAAFE8XmW6YF5TNuk3cosDGBK-sUGPEjks5VSAa2wA%3D%3D)
<img width="300" alt="Vault Logo" src="https://cloud.githubusercontent.com/assets/416727/24112835/03b57de4-0d58-11e7-81f5-9056cac5b427.png">
Vault is a tool for securely accessing secrets. A secret is anything that you want to tightly control access to, such as API keys, passwords, certificates, and more. Vault provides a unified interface to any secret, while providing tight access control and recording a detailed audit log.

View File

@ -333,6 +333,7 @@ func (c *Client) NewRequest(method, path string) *Request {
req := &Request{
Method: method,
URL: &url.URL{
User: c.addr.User,
Scheme: c.addr.Scheme,
Host: c.addr.Host,
Path: path,

View File

@ -55,6 +55,7 @@ func (r *Request) ToHTTP() (*http.Request, error) {
return nil, err
}
req.URL.User = r.URL.User
req.URL.Scheme = r.URL.Scheme
req.URL.Host = r.URL.Host
req.Host = r.URL.Host

View File

@ -129,6 +129,7 @@ type MountInput struct {
type MountConfigInput struct {
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
}
type MountOutput struct {
@ -139,6 +140,7 @@ type MountOutput struct {
}
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"`
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
}

View File

@ -102,9 +102,10 @@ func (f *AuditFormatter) FormatRequest(
Error: errString,
Auth: AuditAuth{
DisplayName: auth.DisplayName,
Policies: auth.Policies,
Metadata: auth.Metadata,
DisplayName: auth.DisplayName,
Policies: auth.Policies,
Metadata: auth.Metadata,
RemainingUses: req.ClientTokenRemainingUses,
},
Request: AuditRequest{
@ -255,6 +256,7 @@ func (f *AuditFormatter) FormatResponse(
DisplayName: resp.Auth.DisplayName,
Policies: resp.Auth.Policies,
Metadata: resp.Auth.Metadata,
NumUses: resp.Auth.NumUses,
}
}
@ -362,11 +364,13 @@ type AuditResponse struct {
}
type AuditAuth struct {
ClientToken string `json:"client_token"`
Accessor string `json:"accessor"`
DisplayName string `json:"display_name"`
Policies []string `json:"policies"`
Metadata map[string]string `json:"metadata"`
ClientToken string `json:"client_token"`
Accessor string `json:"accessor"`
DisplayName string `json:"display_name"`
Policies []string `json:"policies"`
Metadata map[string]string `json:"metadata"`
NumUses int `json:"num_uses,omitempty"`
RemainingUses int `json:"remaining_uses,omitempty"`
}
type AuditSecret struct {

View File

@ -10,7 +10,7 @@ import (
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/vault/audit"
"github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/helper/parseutil"
"github.com/hashicorp/vault/logical"
)
@ -33,7 +33,7 @@ func Factory(conf *audit.BackendConfig) (audit.Backend, error) {
if !ok {
writeDeadline = "2s"
}
writeDuration, err := duration.ParseDurationSecond(writeDeadline)
writeDuration, err := parseutil.ParseDurationSecond(writeDeadline)
if err != nil {
return nil, err
}

View File

@ -1,7 +1,6 @@
package approle
import (
"fmt"
"sync"
"github.com/hashicorp/vault/helper/locksutil"
@ -23,28 +22,26 @@ type backend struct {
// Guard to clean-up the expired SecretID entries
tidySecretIDCASGuard uint32
// Map of locks to make changes to role entries. These will be
// initialized to a predefined number of locks when the backend is
// created, and will be indexed based on salted role names.
roleLocksMap map[string]*sync.RWMutex
// Map of locks to make changes to the storage entries of RoleIDs
// generated. These will be initialized to a predefined number of locks
// when the backend is created, and will be indexed based on the salted
// RoleIDs.
roleIDLocksMap map[string]*sync.RWMutex
// Map of locks to make changes to the storage entries of SecretIDs
// generated. These will be initialized to a predefined number of locks
// when the backend is created, and will be indexed based on the HMAC-ed
// SecretIDs.
secretIDLocksMap map[string]*sync.RWMutex
// Map of locks to make changes to the storage entries of
// SecretIDAccessors generated. These will be initialized to a
// Locks to make changes to role entries. These will be initialized to a
// predefined number of locks when the backend is created, and will be
// indexed based on the SecretIDAccessors itself.
secretIDAccessorLocksMap map[string]*sync.RWMutex
// indexed based on salted role names.
roleLocks []*locksutil.LockEntry
// Locks to make changes to the storage entries of RoleIDs generated. These
// will be initialized to a predefined number of locks when the backend is
// created, and will be indexed based on the salted RoleIDs.
roleIDLocks []*locksutil.LockEntry
// Locks to make changes to the storage entries of SecretIDs generated.
// These will be initialized to a predefined number of locks when the
// backend is created, and will be indexed based on the HMAC-ed SecretIDs.
secretIDLocks []*locksutil.LockEntry
// Locks to make changes to the storage entries of SecretIDAccessors
// generated. These will be initialized to a predefined number of locks
// when the backend is created, and will be indexed based on the
// SecretIDAccessors itself.
secretIDAccessorLocks []*locksutil.LockEntry
// secretIDListingLock is a dedicated lock for listing SecretIDAccessors
// for all the SecretIDs issued against an approle
@ -64,49 +61,19 @@ func Backend(conf *logical.BackendConfig) (*backend, error) {
b := &backend{
view: conf.StorageView,
// Create the map of locks to modify the registered roles
roleLocksMap: make(map[string]*sync.RWMutex, 257),
// Create locks to modify the registered roles
roleLocks: locksutil.CreateLocks(),
// Create the map of locks to modify the generated RoleIDs
roleIDLocksMap: make(map[string]*sync.RWMutex, 257),
// Create locks to modify the generated RoleIDs
roleIDLocks: locksutil.CreateLocks(),
// Create the map of locks to modify the generated SecretIDs
secretIDLocksMap: make(map[string]*sync.RWMutex, 257),
// Create locks to modify the generated SecretIDs
secretIDLocks: locksutil.CreateLocks(),
// Create the map of locks to modify the generated SecretIDAccessors
secretIDAccessorLocksMap: make(map[string]*sync.RWMutex, 257),
// Create locks to modify the generated SecretIDAccessors
secretIDAccessorLocks: locksutil.CreateLocks(),
}
var err error
// Create 256 locks each for managing RoleID and SecretIDs. This will avoid
// a superfluous number of locks directly proportional to the number of RoleID
// and SecretIDs. These locks can be accessed by indexing based on the first two
// characters of a randomly generated UUID.
if err = locksutil.CreateLocks(b.roleLocksMap, 256); err != nil {
return nil, fmt.Errorf("failed to create role locks: %v", err)
}
if err = locksutil.CreateLocks(b.roleIDLocksMap, 256); err != nil {
return nil, fmt.Errorf("failed to create role ID locks: %v", err)
}
if err = locksutil.CreateLocks(b.secretIDLocksMap, 256); err != nil {
return nil, fmt.Errorf("failed to create secret ID locks: %v", err)
}
if err = locksutil.CreateLocks(b.secretIDAccessorLocksMap, 256); err != nil {
return nil, fmt.Errorf("failed to create secret ID accessor locks: %v", err)
}
// Have an extra lock to use in case the indexing does not result in a lock.
// This happens if the indexing value is not beginning with hex characters.
// These locks can be used for listing purposes as well.
b.roleLocksMap["custom"] = &sync.RWMutex{}
b.roleIDLocksMap["custom"] = &sync.RWMutex{}
b.secretIDLocksMap["custom"] = &sync.RWMutex{}
b.secretIDAccessorLocksMap["custom"] = &sync.RWMutex{}
// Attach the paths and secrets that are to be handled by the backend
b.Backend = &framework.Backend{
// Register a periodic function that deletes the expired SecretID entries

View File

@ -3,12 +3,12 @@ package approle
import (
"fmt"
"strings"
"sync"
"time"
"github.com/fatih/structs"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/helper/cidrutil"
"github.com/hashicorp/vault/helper/locksutil"
"github.com/hashicorp/vault/helper/policyutil"
"github.com/hashicorp/vault/helper/strutil"
"github.com/hashicorp/vault/logical"
@ -518,7 +518,6 @@ func (b *backend) pathRoleExistenceCheck(req *logical.Request, data *framework.F
// pathRoleList is used to list all the Roles registered with the backend.
func (b *backend) pathRoleList(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
// This will return the "custom" lock
lock := b.roleLock("")
lock.RLock()
@ -1926,39 +1925,12 @@ func (b *backend) handleRoleSecretIDCommon(req *logical.Request, data *framework
}, nil
}
// roleIDLock is used to get a lock from the pre-initialized map
// of locks. Map is indexed based on the first 2 characters of the
// RoleID, which is a random UUID. If the input is not hex encoded
// or if it is empty a "custom" lock will be returned.
func (b *backend) roleIDLock(roleID string) *sync.RWMutex {
var lock *sync.RWMutex
var ok bool
if len(roleID) >= 2 {
lock, ok = b.roleIDLocksMap[roleID[0:2]]
}
if !ok || lock == nil {
lock = b.roleIDLocksMap["custom"]
}
return lock
func (b *backend) roleIDLock(roleID string) *locksutil.LockEntry {
return locksutil.LockForKey(b.roleIDLocks, roleID)
}
// roleLock is used to get a lock from the pre-initialized map of locks. Map is
// indexed based on the first 2 characters of the salted role name, which is a
// random UUID. If the input is empty, a "custom" lock will be returned.
func (b *backend) roleLock(roleName string) *sync.RWMutex {
var lock *sync.RWMutex
var ok bool
// Salting is used to induce randomness so that roles starting with
// similar characters will likely end up having different locks
index := b.salt.SaltID(roleName)
if len(index) >= 2 {
lock, ok = b.roleLocksMap[index[0:2]]
}
if !ok || lock == nil {
lock = b.roleLocksMap["custom"]
}
return lock
func (b *backend) roleLock(roleName string) *locksutil.LockEntry {
return locksutil.LockForKey(b.roleLocks, roleName)
}
// setRoleIDEntry creates a storage entry that maps RoleID to Role

View File

@ -6,11 +6,11 @@ import (
"encoding/hex"
"fmt"
"strings"
"sync"
"time"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/helper/cidrutil"
"github.com/hashicorp/vault/helper/locksutil"
"github.com/hashicorp/vault/helper/strutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
@ -299,39 +299,12 @@ func createHMAC(key, value string) (string, error) {
return hex.EncodeToString(hm.Sum(nil)), nil
}
// secretIDLock is used to get a lock from the pre-initialized map of locks.
// Map is indexed based on the first 2 characters of the secretIDHMAC. If the
// input is not hex encoded or if empty, a "custom" lock will be returned.
func (b *backend) secretIDLock(secretIDHMAC string) *sync.RWMutex {
var lock *sync.RWMutex
var ok bool
if len(secretIDHMAC) >= 2 {
lock, ok = b.secretIDLocksMap[secretIDHMAC[0:2]]
}
if !ok || lock == nil {
// Fall back for custom lock to make sure that this method
// never returns nil
lock = b.secretIDLocksMap["custom"]
}
return lock
func (b *backend) secretIDLock(secretIDHMAC string) *locksutil.LockEntry {
return locksutil.LockForKey(b.secretIDLocks, secretIDHMAC)
}
// secretIDAccessorLock is used to get a lock from the pre-initialized map
// of locks. Map is indexed based on the first 2 characters of the
// secretIDAccessor. If the input is not hex encoded or if empty, a "custom"
// lock will be returned.
func (b *backend) secretIDAccessorLock(secretIDAccessor string) *sync.RWMutex {
var lock *sync.RWMutex
var ok bool
if len(secretIDAccessor) >= 2 {
lock, ok = b.secretIDAccessorLocksMap[secretIDAccessor[0:2]]
}
if !ok || lock == nil {
// Fall back for custom lock to make sure that this method
// never returns nil
lock = b.secretIDAccessorLocksMap["custom"]
}
return lock
func (b *backend) secretIDAccessorLock(secretIDAccessor string) *locksutil.LockEntry {
return locksutil.LockForKey(b.secretIDAccessorLocks, secretIDAccessor)
}
// nonLockedSecretIDStorageEntry fetches the secret ID properties from physical

View File

@ -66,8 +66,10 @@ func (b *caInfoBundle) GetCAChain() []*certutil.CertBlock {
chain := []*certutil.CertBlock{}
// Include issuing CA in Chain, not including Root Authority
if len(b.Certificate.AuthorityKeyId) > 0 &&
!bytes.Equal(b.Certificate.AuthorityKeyId, b.Certificate.SubjectKeyId) {
if (len(b.Certificate.AuthorityKeyId) > 0 &&
!bytes.Equal(b.Certificate.AuthorityKeyId, b.Certificate.SubjectKeyId)) ||
(len(b.Certificate.AuthorityKeyId) == 0 &&
!bytes.Equal(b.Certificate.RawIssuer, b.Certificate.RawSubject)) {
chain = append(chain, &certutil.CertBlock{
Certificate: b.Certificate,
@ -215,7 +217,7 @@ func fetchCertBySerial(req *logical.Request, prefix, serial string) (*logical.St
// Given a set of requested names for a certificate, verifies that all of them
// match the various toggles set in the role for controlling issuance.
// If one does not pass, it is returned in the string argument.
func validateNames(req *logical.Request, names []string, role *roleEntry) (string, error) {
func validateNames(req *logical.Request, names []string, role *roleEntry) string {
for _, name := range names {
sanitizedName := name
emailDomain := name
@ -231,7 +233,7 @@ func validateNames(req *logical.Request, names []string, role *roleEntry) (strin
if strings.Contains(name, "@") {
splitEmail := strings.Split(name, "@")
if len(splitEmail) != 2 {
return name, nil
return name
}
sanitizedName = splitEmail[1]
emailDomain = splitEmail[1]
@ -248,7 +250,7 @@ func validateNames(req *logical.Request, names []string, role *roleEntry) (strin
// Email addresses using wildcard domain names do not make sense
if isEmail && isWildcard {
return name, nil
return name
}
// AllowAnyName is checked after this because EnforceHostnames still
@ -257,7 +259,7 @@ func validateNames(req *logical.Request, names []string, role *roleEntry) (strin
// wildcard prefix.
if role.EnforceHostnames {
if !hostnameRegex.MatchString(sanitizedName) {
return name, nil
return name
}
}
@ -366,10 +368,10 @@ func validateNames(req *logical.Request, names []string, role *roleEntry) (strin
}
//panic(fmt.Sprintf("\nName is %s\nRole is\n%#v\n", name, role))
return name, nil
return name
}
return "", nil
return ""
}
func generateCert(b *backend,
@ -558,13 +560,13 @@ func generateCreationBundle(b *backend,
var err error
var ok bool
// Get the common name
// Read in names -- CN, DNS and email addresses
var cn string
dnsNames := []string{}
emailAddresses := []string{}
{
if csr != nil {
if role.UseCSRCommonName {
cn = csr.Subject.CommonName
}
if csr != nil && role.UseCSRCommonName {
cn = csr.Subject.CommonName
}
if cn == "" {
cn = data.Get("common_name").(string)
@ -572,6 +574,91 @@ func generateCreationBundle(b *backend,
return nil, errutil.UserError{Err: `the common_name field is required, or must be provided in a CSR with "use_csr_common_name" set to true`}
}
}
if csr != nil && role.UseCSRSANs {
dnsNames = csr.DNSNames
emailAddresses = csr.EmailAddresses
}
if !data.Get("exclude_cn_from_sans").(bool) {
if strings.Contains(cn, "@") {
// Note: emails are not disallowed if the role's email protection
// flag is false, because they may well be included for
// informational purposes; it is up to the verifying party to
// ensure that email addresses in a subject alternate name can be
// used for the purpose for which they are presented
emailAddresses = append(emailAddresses, cn)
} else {
dnsNames = append(dnsNames, cn)
}
}
if csr == nil || !role.UseCSRSANs {
cnAltRaw, ok := data.GetOk("alt_names")
if ok {
cnAlt := strutil.ParseDedupAndSortStrings(cnAltRaw.(string), ",")
for _, v := range cnAlt {
if strings.Contains(v, "@") {
emailAddresses = append(emailAddresses, v)
} else {
dnsNames = append(dnsNames, v)
}
}
}
}
// Check the CN. This ensures that the CN is checked even if it's
// excluded from SANs.
badName := validateNames(req, []string{cn}, role)
if len(badName) != 0 {
return nil, errutil.UserError{Err: fmt.Sprintf(
"common name %s not allowed by this role", badName)}
}
// Check for bad email and/or DNS names
badName = validateNames(req, dnsNames, role)
if len(badName) != 0 {
return nil, errutil.UserError{Err: fmt.Sprintf(
"subject alternate name %s not allowed by this role", badName)}
}
badName = validateNames(req, emailAddresses, role)
if len(badName) != 0 {
return nil, errutil.UserError{Err: fmt.Sprintf(
"email address %s not allowed by this role", badName)}
}
}
// Get and verify any IP SANs
ipAddresses := []net.IP{}
var ipAltInt interface{}
{
if csr != nil && role.UseCSRSANs {
if !role.AllowIPSANs {
return nil, errutil.UserError{Err: fmt.Sprintf(
"IP Subject Alternative Names are not allowed in this role, but was provided some via CSR")}
}
ipAddresses = csr.IPAddresses
} else {
ipAltInt, ok = data.GetOk("ip_sans")
if ok {
ipAlt := ipAltInt.(string)
if len(ipAlt) != 0 {
if !role.AllowIPSANs {
return nil, errutil.UserError{Err: fmt.Sprintf(
"IP Subject Alternative Names are not allowed in this role, but was provided %s", ipAlt)}
}
for _, v := range strings.Split(ipAlt, ",") {
parsedIP := net.ParseIP(v)
if parsedIP == nil {
return nil, errutil.UserError{Err: fmt.Sprintf(
"the value '%s' is not a valid IP address", v)}
}
ipAddresses = append(ipAddresses, parsedIP)
}
}
}
}
}
// Set OU (organizationalUnit) values if specified in the role
@ -590,80 +677,6 @@ func generateCreationBundle(b *backend,
}
}
// Read in alternate names -- DNS and email addresses
dnsNames := []string{}
emailAddresses := []string{}
{
if !data.Get("exclude_cn_from_sans").(bool) {
if strings.Contains(cn, "@") {
// Note: emails are not disallowed if the role's email protection
// flag is false, because they may well be included for
// informational purposes; it is up to the verifying party to
// ensure that email addresses in a subject alternate name can be
// used for the purpose for which they are presented
emailAddresses = append(emailAddresses, cn)
} else {
dnsNames = append(dnsNames, cn)
}
}
cnAltInt, ok := data.GetOk("alt_names")
if ok {
cnAlt := cnAltInt.(string)
if len(cnAlt) != 0 {
for _, v := range strings.Split(cnAlt, ",") {
if strings.Contains(v, "@") {
emailAddresses = append(emailAddresses, v)
} else {
dnsNames = append(dnsNames, v)
}
}
}
}
// Check for bad email and/or DNS names
badName, err := validateNames(req, dnsNames, role)
if len(badName) != 0 {
return nil, errutil.UserError{Err: fmt.Sprintf(
"name %s not allowed by this role", badName)}
} else if err != nil {
return nil, errutil.InternalError{Err: fmt.Sprintf(
"error validating name %s: %s", badName, err)}
}
badName, err = validateNames(req, emailAddresses, role)
if len(badName) != 0 {
return nil, errutil.UserError{Err: fmt.Sprintf(
"email %s not allowed by this role", badName)}
} else if err != nil {
return nil, errutil.InternalError{Err: fmt.Sprintf(
"error validating name %s: %s", badName, err)}
}
}
// Get and verify any IP SANs
ipAddresses := []net.IP{}
var ipAltInt interface{}
{
ipAltInt, ok = data.GetOk("ip_sans")
if ok {
ipAlt := ipAltInt.(string)
if len(ipAlt) != 0 {
if !role.AllowIPSANs {
return nil, errutil.UserError{Err: fmt.Sprintf(
"IP Subject Alternative Names are not allowed in this role, but was provided %s", ipAlt)}
}
for _, v := range strings.Split(ipAlt, ",") {
parsedIP := net.ParseIP(v)
if parsedIP == nil {
return nil, errutil.UserError{Err: fmt.Sprintf(
"the value '%s' is not a valid IP address", v)}
}
ipAddresses = append(ipAddresses, parsedIP)
}
}
}
}
// Get the TTL and very it against the max allowed
var ttlField string
var ttl time.Duration

View File

@ -125,6 +125,7 @@ func (b *backend) pathSignVerbatim(
EnforceHostnames: false,
KeyType: "any",
UseCSRCommonName: true,
UseCSRSANs: true,
}
return b.pathIssueSignCert(req, data, role, true, true)

View File

@ -7,7 +7,7 @@ import (
"time"
"github.com/fatih/structs"
"github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/helper/parseutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
@ -169,6 +169,14 @@ does *not* include any requested Subject Alternative
Names. Defaults to true.`,
},
"use_csr_sans": &framework.FieldSchema{
Type: framework.TypeBool,
Default: true,
Description: `If set, when used with a signing profile,
the SANs in the CSR will be used. This does *not*
include the Common Name (cn). Defaults to true.`,
},
"ou": &framework.FieldSchema{
Type: framework.TypeString,
Default: "",
@ -371,6 +379,7 @@ func (b *backend) pathRoleCreate(
KeyType: data.Get("key_type").(string),
KeyBits: data.Get("key_bits").(int),
UseCSRCommonName: data.Get("use_csr_common_name").(bool),
UseCSRSANs: data.Get("use_csr_sans").(bool),
KeyUsage: data.Get("key_usage").(string),
OU: data.Get("ou").(string),
Organization: data.Get("organization").(string),
@ -388,7 +397,7 @@ func (b *backend) pathRoleCreate(
if len(entry.MaxTTL) == 0 {
maxTTL = maxSystemTTL
} else {
maxTTL, err = duration.ParseDurationSecond(entry.MaxTTL)
maxTTL, err = parseutil.ParseDurationSecond(entry.MaxTTL)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(
"Invalid max ttl: %s", err)), nil
@ -400,7 +409,7 @@ func (b *backend) pathRoleCreate(
ttl := b.System().DefaultLeaseTTL()
if len(entry.TTL) != 0 {
ttl, err = duration.ParseDurationSecond(entry.TTL)
ttl, err = parseutil.ParseDurationSecond(entry.TTL)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(
"Invalid ttl: %s", err)), nil
@ -487,6 +496,7 @@ type roleEntry struct {
CodeSigningFlag bool `json:"code_signing_flag" structs:"code_signing_flag" mapstructure:"code_signing_flag"`
EmailProtectionFlag bool `json:"email_protection_flag" structs:"email_protection_flag" mapstructure:"email_protection_flag"`
UseCSRCommonName bool `json:"use_csr_common_name" structs:"use_csr_common_name" mapstructure:"use_csr_common_name"`
UseCSRSANs bool `json:"use_csr_sans" structs:"use_csr_sans" mapstructure:"use_csr_sans"`
KeyType string `json:"key_type" structs:"key_type" mapstructure:"key_type"`
KeyBits int `json:"key_bits" structs:"key_bits" mapstructure:"key_bits"`
MaxPathLength *int `json:",omitempty" structs:"max_path_length,omitempty" mapstructure:"max_path_length"`

View File

@ -587,7 +587,7 @@ func TestBackend_ValidPrincipalsValidatedForHostCertificates(t *testing.T) {
},
}),
signCertificateStep("testing", "root", ssh.HostCert, []string{"dummy.example.org", "second.example.com"}, map[string]string{
signCertificateStep("testing", "vault-root-22608f5ef173aabf700797cb95c5641e792698ec6380e8e1eb55523e39aa5e51", ssh.HostCert, []string{"dummy.example.org", "second.example.com"}, map[string]string{
"option": "value",
}, map[string]string{
"extension": "extended",
@ -632,7 +632,7 @@ func TestBackend_OptionsOverrideDefaults(t *testing.T) {
},
}),
signCertificateStep("testing", "root", ssh.UserCert, []string{"tuber"}, map[string]string{
signCertificateStep("testing", "vault-root-22608f5ef173aabf700797cb95c5641e792698ec6380e8e1eb55523e39aa5e51", ssh.UserCert, []string{"tuber"}, map[string]string{
"secondary": "value",
}, map[string]string{
"additional": "value",
@ -709,7 +709,7 @@ func validateSSHCertificate(cert *ssh.Certificate, keyId string, certType int, v
ttl time.Duration) error {
if cert.KeyId != keyId {
return fmt.Errorf("Incorrect KeyId: %v", cert.KeyId)
return fmt.Errorf("Incorrect KeyId: %v, wanted %v", cert.KeyId, keyId)
}
if cert.CertType != uint32(certType) {

View File

@ -7,11 +7,25 @@ import (
"encoding/pem"
"fmt"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
"golang.org/x/crypto/ssh"
)
const (
caPublicKey = "ca_public_key"
caPrivateKey = "ca_private_key"
caPublicKeyStoragePath = "config/ca_public_key"
caPublicKeyStoragePathDeprecated = "public_key"
caPrivateKeyStoragePath = "config/ca_private_key"
caPrivateKeyStoragePathDeprecated = "config/ca_bundle"
)
type keyStorageEntry struct {
Key string `json:"key" structs:"key" mapstructure:"key"`
}
func pathConfigCA(b *backend) *framework.Path {
return &framework.Path{
Pattern: "config/ca",
@ -34,27 +48,102 @@ func pathConfigCA(b *backend) *framework.Path {
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.UpdateOperation: b.pathConfigCAUpdate,
logical.DeleteOperation: b.pathConfigCADelete,
logical.ReadOperation: b.pathConfigCARead,
},
HelpSynopsis: `Set the SSH private key used for signing certificates.`,
HelpDescription: `This sets the CA information used for certificates generated by this
by this mount. The fields must be in the standard private and public SSH format.
For security reasons, the private key cannot be retrieved later.`,
For security reasons, the private key cannot be retrieved later.
Read operations will return the public key, if already stored/generated.`,
}
}
func (b *backend) pathConfigCARead(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
publicKeyEntry, err := caKey(req.Storage, caPublicKey)
if err != nil {
return nil, fmt.Errorf("failed to read CA public key: %v", err)
}
if publicKeyEntry == nil {
return logical.ErrorResponse("keys haven't been configured yet"), nil
}
response := &logical.Response{
Data: map[string]interface{}{
"public_key": publicKeyEntry.Key,
},
}
return response, nil
}
func (b *backend) pathConfigCADelete(
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
if err := req.Storage.Delete("config/ca_bundle"); err != nil {
if err := req.Storage.Delete(caPrivateKeyStoragePath); err != nil {
return nil, err
}
if err := req.Storage.Delete("config/ca_public_key"); err != nil {
if err := req.Storage.Delete(caPublicKeyStoragePath); err != nil {
return nil, err
}
return nil, nil
}
func caKey(storage logical.Storage, keyType string) (*keyStorageEntry, error) {
var path, deprecatedPath string
switch keyType {
case caPrivateKey:
path = caPrivateKeyStoragePath
deprecatedPath = caPrivateKeyStoragePathDeprecated
case caPublicKey:
path = caPublicKeyStoragePath
deprecatedPath = caPublicKeyStoragePathDeprecated
default:
return nil, fmt.Errorf("unrecognized key type %q", keyType)
}
entry, err := storage.Get(path)
if err != nil {
return nil, fmt.Errorf("failed to read CA key of type %q: %v", keyType, err)
}
if entry == nil {
// If the entry is not found, look at an older path. If found, upgrade
// it.
entry, err = storage.Get(deprecatedPath)
if err != nil {
return nil, err
}
if entry != nil {
entry, err = logical.StorageEntryJSON(path, keyStorageEntry{
Key: string(entry.Value),
})
if err != nil {
return nil, err
}
if err := storage.Put(entry); err != nil {
return nil, err
}
if err = storage.Delete(deprecatedPath); err != nil {
return nil, err
}
}
}
if entry == nil {
return nil, nil
}
var keyEntry keyStorageEntry
if err := entry.DecodeJSON(&keyEntry); err != nil {
return nil, err
}
return &keyEntry, nil
}
func (b *backend) pathConfigCAUpdate(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
var err error
publicKey := data.Get("public_key").(string)
@ -112,39 +201,68 @@ func (b *backend) pathConfigCAUpdate(req *logical.Request, data *framework.Field
return nil, fmt.Errorf("failed to generate or parse the keys")
}
publicKeyEntry, err := req.Storage.Get("config/ca_public_key")
publicKeyEntry, err := caKey(req.Storage, caPublicKey)
if err != nil {
return nil, fmt.Errorf("failed while reading ca_public_key: %v", err)
return nil, fmt.Errorf("failed to read CA public key: %v", err)
}
privateKeyEntry, err := req.Storage.Get("config/ca_bundle")
privateKeyEntry, err := caKey(req.Storage, caPrivateKey)
if err != nil {
return nil, fmt.Errorf("failed while reading ca_bundle: %v", err)
return nil, fmt.Errorf("failed to read CA private key: %v", err)
}
if publicKeyEntry != nil || privateKeyEntry != nil {
if (publicKeyEntry != nil && publicKeyEntry.Key != "") || (privateKeyEntry != nil && privateKeyEntry.Key != "") {
return nil, fmt.Errorf("keys are already configured; delete them before reconfiguring")
}
err = req.Storage.Put(&logical.StorageEntry{
Key: "config/ca_public_key",
Value: []byte(publicKey),
entry, err := logical.StorageEntryJSON(caPublicKeyStoragePath, &keyStorageEntry{
Key: publicKey,
})
if err != nil {
return nil, err
}
bundle := signingBundle{
Certificate: privateKey,
}
entry, err := logical.StorageEntryJSON("config/ca_bundle", bundle)
// Save the public key
err = req.Storage.Put(entry)
if err != nil {
return nil, err
}
entry, err = logical.StorageEntryJSON(caPrivateKeyStoragePath, &keyStorageEntry{
Key: privateKey,
})
if err != nil {
return nil, err
}
// Save the private key
err = req.Storage.Put(entry)
return nil, err
if err != nil {
var mErr *multierror.Error
mErr = multierror.Append(mErr, fmt.Errorf("failed to store CA private key: %v", err))
// If storing private key fails, the corresponding public key should be
// removed
if delErr := req.Storage.Delete(caPublicKeyStoragePath); delErr != nil {
mErr = multierror.Append(mErr, fmt.Errorf("failed to cleanup CA public key: %v", delErr))
return nil, mErr
}
return nil, err
}
if generateSigningKey {
response := &logical.Response{
Data: map[string]interface{}{
"public_key": publicKey,
},
}
return response, nil
}
return nil, nil
}
func generateSSHKeyPair() (string, string, error) {

View File

@ -6,6 +6,91 @@ import (
"github.com/hashicorp/vault/logical"
)
func TestSSH_ConfigCAStorageUpgrade(t *testing.T) {
var err error
config := logical.TestBackendConfig()
config.StorageView = &logical.InmemStorage{}
b, err := Backend(config)
if err != nil {
t.Fatal(err)
}
_, err = b.Setup(config)
if err != nil {
t.Fatal(err)
}
// Store at an older path
err = config.StorageView.Put(&logical.StorageEntry{
Key: caPrivateKeyStoragePathDeprecated,
Value: []byte(privateKey),
})
if err != nil {
t.Fatal(err)
}
// Reading it should return the key as well as upgrade the storage path
privateKeyEntry, err := caKey(config.StorageView, caPrivateKey)
if err != nil {
t.Fatal(err)
}
if privateKeyEntry == nil || privateKeyEntry.Key == "" {
t.Fatalf("failed to read the stored private key")
}
entry, err := config.StorageView.Get(caPrivateKeyStoragePathDeprecated)
if err != nil {
t.Fatal(err)
}
if entry != nil {
t.Fatalf("bad: expected a nil entry after upgrade")
}
entry, err = config.StorageView.Get(caPrivateKeyStoragePath)
if err != nil {
t.Fatal(err)
}
if entry == nil {
t.Fatalf("bad: expected a non-nil entry after upgrade")
}
// Store at an older path
err = config.StorageView.Put(&logical.StorageEntry{
Key: caPublicKeyStoragePathDeprecated,
Value: []byte(publicKey),
})
if err != nil {
t.Fatal(err)
}
// Reading it should return the key as well as upgrade the storage path
publicKeyEntry, err := caKey(config.StorageView, caPublicKey)
if err != nil {
t.Fatal(err)
}
if publicKeyEntry == nil || publicKeyEntry.Key == "" {
t.Fatalf("failed to read the stored public key")
}
entry, err = config.StorageView.Get(caPublicKeyStoragePathDeprecated)
if err != nil {
t.Fatal(err)
}
if entry != nil {
t.Fatalf("bad: expected a nil entry after upgrade")
}
entry, err = config.StorageView.Get(caPublicKeyStoragePath)
if err != nil {
t.Fatal(err)
}
if entry == nil {
t.Fatalf("bad: expected a non-nil entry after upgrade")
}
}
func TestSSH_ConfigCAUpdateDelete(t *testing.T) {
var resp *logical.Response
var err error

View File

@ -19,19 +19,18 @@ func pathFetchPublicKey(b *backend) *framework.Path {
}
func (b *backend) pathFetchPublicKey(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
entry, err := req.Storage.Get("config/ca_public_key")
publicKeyEntry, err := caKey(req.Storage, caPublicKey)
if err != nil {
return nil, err
}
if entry == nil {
if publicKeyEntry == nil || publicKeyEntry.Key == "" {
return nil, nil
}
response := &logical.Response{
Data: map[string]interface{}{
logical.HTTPContentType: "text/plain",
logical.HTTPRawBody: entry.Value,
logical.HTTPRawBody: []byte(publicKeyEntry.Key),
logical.HTTPStatusCode: 200,
},
}

View File

@ -7,7 +7,7 @@ import (
"time"
"github.com/hashicorp/vault/helper/cidrutil"
"github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/helper/parseutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
@ -44,6 +44,7 @@ type sshRole struct {
AllowHostCertificates bool `mapstructure:"allow_host_certificates" json:"allow_host_certificates"`
AllowBareDomains bool `mapstructure:"allow_bare_domains" json:"allow_bare_domains"`
AllowSubdomains bool `mapstructure:"allow_subdomains" json:"allow_subdomains"`
AllowUserKeyIDs bool `mapstructure:"allow_user_key_ids" json:"allow_user_key_ids"`
}
func pathListRoles(b *backend) *framework.Path {
@ -143,11 +144,14 @@ func pathRoles(b *backend) *framework.Path {
Type: framework.TypeString,
Description: `
[Optional for all types]
If this option is not specified, client can request for a credential for
any valid user at the remote host, including the admin user. If only certain
usernames are to be allowed, then this list enforces it. If this field is
set, then credentials can only be created for default_user and usernames
present in this list.
If this option is not specified, client can request for a
credential for any valid user at the remote host, including the
admin user. If only certain usernames are to be allowed, then
this list enforces it. If this field is set, then credentials
can only be created for default_user and usernames present in
this list. Setting this option will enable all the users with
access this role to fetch credentials for all other usernames
in this list. Use with caution.
`,
},
"allowed_domains": &framework.FieldSchema{
@ -251,6 +255,15 @@ func pathRoles(b *backend) *framework.Path {
If set, host certificates that are requested are allowed to use subdomains of those listed in "allowed_domains".
`,
},
"allow_user_key_ids": &framework.FieldSchema{
Type: framework.TypeBool,
Description: `
[Not applicable for Dynamic type] [Not applicable for OTP type] [Optional for CA type]
If true, users can override the key ID for a signed certificate with the "key_id" field.
When false, the key ID will always be the token display name.
The key ID is logged by the SSH server and can be useful for auditing.
`,
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
@ -407,7 +420,6 @@ func (b *backend) pathRoleWrite(req *logical.Request, d *framework.FieldData) (*
}
func (b *backend) createCARole(allowedUsers, defaultUser string, data *framework.FieldData) (*sshRole, *logical.Response) {
role := &sshRole{
MaxTTL: data.Get("max_ttl").(string),
TTL: data.Get("ttl").(string),
@ -420,9 +432,14 @@ func (b *backend) createCARole(allowedUsers, defaultUser string, data *framework
DefaultUser: defaultUser,
AllowBareDomains: data.Get("allow_bare_domains").(bool),
AllowSubdomains: data.Get("allow_subdomains").(bool),
AllowUserKeyIDs: data.Get("allow_user_key_ids").(bool),
KeyType: KeyTypeCA,
}
if !role.AllowUserCertificates && !role.AllowHostCertificates {
return nil, logical.ErrorResponse("Either 'allow_user_certificates' or 'allow_host_certificates' must be set to 'true'")
}
defaultCriticalOptions := convertMapToStringValue(data.Get("default_critical_options").(map[string]interface{}))
defaultExtensions := convertMapToStringValue(data.Get("default_extensions").(map[string]interface{}))
@ -432,7 +449,7 @@ func (b *backend) createCARole(allowedUsers, defaultUser string, data *framework
maxTTL = maxSystemTTL
} else {
var err error
maxTTL, err = duration.ParseDurationSecond(role.MaxTTL)
maxTTL, err = parseutil.ParseDurationSecond(role.MaxTTL)
if err != nil {
return nil, logical.ErrorResponse(fmt.Sprintf(
"Invalid max ttl: %s", err))
@ -445,7 +462,7 @@ func (b *backend) createCARole(allowedUsers, defaultUser string, data *framework
ttl := b.System().DefaultLeaseTTL()
if len(role.TTL) != 0 {
var err error
ttl, err = duration.ParseDurationSecond(role.TTL)
ttl, err = parseutil.ParseDurationSecond(role.TTL)
if err != nil {
return nil, logical.ErrorResponse(fmt.Sprintf(
"Invalid ttl: %s", err))
@ -533,6 +550,7 @@ func (b *backend) pathRoleRead(req *logical.Request, d *framework.FieldData) (*l
"allow_host_certificates": role.AllowHostCertificates,
"allow_bare_domains": role.AllowBareDomains,
"allow_subdomains": role.AllowSubdomains,
"allow_user_key_ids": role.AllowUserKeyIDs,
"key_type": role.KeyType,
"default_critical_options": role.DefaultCriticalOptions,
"default_extensions": role.DefaultExtensions,

View File

@ -2,6 +2,8 @@ package ssh
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"strconv"
@ -9,28 +11,23 @@ import (
"time"
"github.com/hashicorp/vault/helper/certutil"
"github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/helper/errutil"
"github.com/hashicorp/vault/helper/parseutil"
"github.com/hashicorp/vault/helper/strutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
"golang.org/x/crypto/ssh"
)
type signingBundle struct {
Certificate string `json:"certificate" structs:"certificate" mapstructure:"certificate"`
}
type creationBundle struct {
KeyId string
ValidPrincipals []string
PublicKey ssh.PublicKey
CertificateType uint32
TTL time.Duration
SigningBundle signingBundle
Signer ssh.Signer
Role *sshRole
criticalOptions map[string]string
extensions map[string]string
CriticalOptions map[string]string
Extensions map[string]string
}
func pathSign(b *backend) *framework.Path {
@ -109,16 +106,16 @@ func (b *backend) pathSignCertificate(req *logical.Request, data *framework.Fiel
userPublicKey, err := parsePublicSSHKey(publicKey)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf("unable to decode \"public_key\" as SSH key: %s", err)), nil
}
keyId := data.Get("key_id").(string)
if keyId == "" {
keyId = req.DisplayName
return logical.ErrorResponse(fmt.Sprintf("failed to parse public_key as SSH key: %s", err)), nil
}
// Note that these various functions always return "user errors" so we pass
// them as 4xx values
keyId, err := b.calculateKeyId(data, req, role, userPublicKey)
if err != nil {
return logical.ErrorResponse(err.Error()), nil
}
certificateType, err := b.calculateCertificateType(data, role)
if err != nil {
return logical.ErrorResponse(err.Error()), nil
@ -152,32 +149,32 @@ func (b *backend) pathSignCertificate(req *logical.Request, data *framework.Fiel
return logical.ErrorResponse(err.Error()), nil
}
storedBundle, err := req.Storage.Get("config/ca_bundle")
privateKeyEntry, err := caKey(req.Storage, caPrivateKey)
if err != nil {
return nil, fmt.Errorf("unable to fetch local CA certificate/key: %v", err)
return nil, fmt.Errorf("failed to read CA private key: %v", err)
}
if storedBundle == nil {
return logical.ErrorResponse("backend must be configured with a CA certificate/key"), nil
if privateKeyEntry == nil || privateKeyEntry.Key == "" {
return nil, fmt.Errorf("failed to read CA private key")
}
var bundle signingBundle
if err := storedBundle.DecodeJSON(&bundle); err != nil {
return nil, fmt.Errorf("unable to decode local CA certificate/key: %v", err)
signer, err := ssh.ParsePrivateKey([]byte(privateKeyEntry.Key))
if err != nil {
return nil, fmt.Errorf("failed to parse stored CA private key: %v", err)
}
signingBundle := creationBundle{
cBundle := creationBundle{
KeyId: keyId,
PublicKey: userPublicKey,
SigningBundle: bundle,
Signer: signer,
ValidPrincipals: parsedPrincipals,
TTL: ttl,
CertificateType: certificateType,
Role: role,
criticalOptions: criticalOptions,
extensions: extensions,
CriticalOptions: criticalOptions,
Extensions: extensions,
}
certificate, err := signingBundle.sign()
certificate, err := cBundle.sign()
if err != nil {
return nil, err
}
@ -198,34 +195,37 @@ func (b *backend) pathSignCertificate(req *logical.Request, data *framework.Fiel
}
func (b *backend) calculateValidPrincipals(data *framework.FieldData, defaultPrincipal, principalsAllowedByRole string, validatePrincipal func([]string, string) bool) ([]string, error) {
if principalsAllowedByRole == "" {
return nil, fmt.Errorf(`"role is not configured to allow any principles`)
validPrincipals := ""
validPrincipalsRaw, ok := data.GetOk("valid_principals")
if ok {
validPrincipals = validPrincipalsRaw.(string)
} else {
validPrincipals = defaultPrincipal
}
validPrincipals := data.Get("valid_principals").(string)
if validPrincipals == "" {
if defaultPrincipal != "" {
return []string{defaultPrincipal}, nil
parsedPrincipals := strutil.ParseDedupAndSortStrings(validPrincipals, ",")
allowedPrincipals := strutil.ParseDedupAndSortStrings(principalsAllowedByRole, ",")
switch {
case len(parsedPrincipals) == 0:
// There is nothing to process
return nil, nil
case len(allowedPrincipals) == 0:
// User has requested principals to be set, but role is not configured
// with any principals
return nil, fmt.Errorf("role is not configured to allow any principles")
default:
// Role was explicitly configured to allow any principal.
if principalsAllowedByRole == "*" {
return parsedPrincipals, nil
}
return nil, fmt.Errorf(`"valid_principals" not supplied and no default set in the role`)
}
parsedPrincipals := strings.Split(validPrincipals, ",")
// Role was explicitly configured to allow any principal.
if principalsAllowedByRole == "*" {
for _, principal := range parsedPrincipals {
if !validatePrincipal(allowedPrincipals, principal) {
return nil, fmt.Errorf("%v is not a valid value for valid_principals", principal)
}
}
return parsedPrincipals, nil
}
allowedPrincipals := strings.Split(principalsAllowedByRole, ",")
for _, principal := range parsedPrincipals {
if !validatePrincipal(allowedPrincipals, principal) {
return nil, fmt.Errorf(`%v is not a valid value for "valid_principals"`, principal)
}
}
return parsedPrincipals, nil
}
func validateValidPrincipalForHosts(role *sshRole) func([]string, string) bool {
@ -250,21 +250,43 @@ func (b *backend) calculateCertificateType(data *framework.FieldData, role *sshR
switch requestedCertificateType {
case "user":
if !role.AllowUserCertificates {
return 0, errors.New(`"cert_type" 'user' is not allowed by role`)
return 0, errors.New("cert_type 'user' is not allowed by role")
}
certificateType = ssh.UserCert
case "host":
if !role.AllowHostCertificates {
return 0, errors.New(`"cert_type" 'host' is not allowed by role`)
return 0, errors.New("cert_type 'host' is not allowed by role")
}
certificateType = ssh.HostCert
default:
return 0, errors.New(`"cert_type" must be either 'user' or 'host'`)
return 0, errors.New("cert_type must be either 'user' or 'host'")
}
return certificateType, nil
}
func (b *backend) calculateKeyId(data *framework.FieldData, req *logical.Request, role *sshRole, pubKey ssh.PublicKey) (string, error) {
reqId := data.Get("key_id").(string)
if reqId != "" {
if !role.AllowUserKeyIDs {
return "", fmt.Errorf("setting key_id is not allowed by role")
}
return reqId, nil
}
keyHash := sha256.Sum256(pubKey.Marshal())
keyId := hex.EncodeToString(keyHash[:])
if req.DisplayName != "" {
keyId = fmt.Sprintf("%s-%s", req.DisplayName, keyId)
}
keyId = fmt.Sprintf("vault-%s", keyId)
return keyId, nil
}
func (b *backend) calculateCriticalOptions(data *framework.FieldData, role *sshRole) (map[string]string, error) {
unparsedCriticalOptions := data.Get("critical_options").(map[string]interface{})
if len(unparsedCriticalOptions) == 0 {
@ -310,7 +332,7 @@ func (b *backend) calculateExtensions(data *framework.FieldData, role *sshRole)
}
if len(notAllowed) != 0 {
return nil, fmt.Errorf("Extensions not on allowed list: %v", notAllowed)
return nil, fmt.Errorf("extensions %v are not on allowed list", notAllowed)
}
}
@ -332,7 +354,7 @@ func (b *backend) calculateTTL(data *framework.FieldData, role *sshRole) (time.D
ttl = b.System().DefaultLeaseTTL()
} else {
var err error
ttl, err = duration.ParseDurationSecond(ttlField)
ttl, err = parseutil.ParseDurationSecond(ttlField)
if err != nil {
return 0, fmt.Errorf("invalid requested ttl: %s", err)
}
@ -342,7 +364,7 @@ func (b *backend) calculateTTL(data *framework.FieldData, role *sshRole) (time.D
maxTTL = b.System().MaxLeaseTTL()
} else {
var err error
maxTTL, err = duration.ParseDurationSecond(role.MaxTTL)
maxTTL, err = parseutil.ParseDurationSecond(role.MaxTTL)
if err != nil {
return 0, fmt.Errorf("invalid requested max ttl: %s", err)
}
@ -362,11 +384,6 @@ func (b *backend) calculateTTL(data *framework.FieldData, role *sshRole) (time.D
}
func (b *creationBundle) sign() (*ssh.Certificate, error) {
signingKey, err := ssh.ParsePrivateKey([]byte(b.SigningBundle.Certificate))
if err != nil {
return nil, errutil.InternalError{Err: fmt.Sprintf("stored SSH signing key cannot be parsed: %v", err)}
}
serialNumber, err := certutil.GenerateSerialNumber()
if err != nil {
return nil, err
@ -383,14 +400,14 @@ func (b *creationBundle) sign() (*ssh.Certificate, error) {
ValidBefore: uint64(now.Add(b.TTL).In(time.UTC).Unix()),
CertType: b.CertificateType,
Permissions: ssh.Permissions{
CriticalOptions: b.criticalOptions,
Extensions: b.extensions,
CriticalOptions: b.CriticalOptions,
Extensions: b.Extensions,
},
}
err = certificate.SignCert(rand.Reader, signingKey)
err = certificate.SignCert(rand.Reader, b.Signer)
if err != nil {
return nil, errutil.InternalError{Err: "Failed to generate signed SSH key"}
return nil, fmt.Errorf("failed to generate signed SSH key")
}
return certificate, nil

View File

@ -15,12 +15,13 @@ type MountCommand struct {
func (c *MountCommand) Run(args []string) int {
var description, path, defaultLeaseTTL, maxLeaseTTL string
var local bool
var local, forceNoCache bool
flags := c.Meta.FlagSet("mount", meta.FlagSetDefault)
flags.StringVar(&description, "description", "", "")
flags.StringVar(&path, "path", "", "")
flags.StringVar(&defaultLeaseTTL, "default-lease-ttl", "", "")
flags.StringVar(&maxLeaseTTL, "max-lease-ttl", "", "")
flags.BoolVar(&forceNoCache, "force-no-cache", false, "")
flags.BoolVar(&local, "local", false, "")
flags.Usage = func() { c.Ui.Error(c.Help()) }
if err := flags.Parse(args); err != nil {
@ -55,6 +56,7 @@ func (c *MountCommand) Run(args []string) int {
Config: api.MountConfigInput{
DefaultLeaseTTL: defaultLeaseTTL,
MaxLeaseTTL: maxLeaseTTL,
ForceNoCache: forceNoCache,
},
Local: local,
}
@ -105,6 +107,11 @@ Mount Options:
the previously set value. Set to '0' to
explicitly set it to use the global default.
-force-no-cache Forces the backend to disable caching. If not
specified, uses the global default. This does
not affect caching of the underlying encrypted
data storage.
-local Mark the mount as a local mount. Local mounts
are not replicated nor (if a secondary)
removed by replication.

View File

@ -42,7 +42,7 @@ func (c *MountsCommand) Run(args []string) int {
}
sort.Strings(paths)
columns := []string{"Path | Type | Default TTL | Max TTL | Replication Behavior | Description"}
columns := []string{"Path | Type | Default TTL | Max TTL | Force No Cache | Replication Behavior | Description"}
for _, path := range paths {
mount := mounts[path]
defTTL := "system"
@ -68,7 +68,8 @@ func (c *MountsCommand) Run(args []string) int {
replicatedBehavior = "local"
}
columns = append(columns, fmt.Sprintf(
"%s | %s | %s | %s | %s | %s", path, mount.Type, defTTL, maxTTL, replicatedBehavior, mount.Description))
"%s | %s | %s | %s | %v | %s | %s", path, mount.Type, defTTL, maxTTL,
mount.Config.ForceNoCache, replicatedBehavior, mount.Description))
}
c.Ui.Output(columnize.SimpleFormat(columns))

View File

@ -204,8 +204,8 @@ func (c *ServerCommand) Run(args []string) int {
}
// Ensure that a backend is provided
if config.Backend == nil {
c.Ui.Output("A physical backend must be specified")
if config.Storage == nil {
c.Ui.Output("A storage backend must be specified")
return 1
}
@ -225,11 +225,11 @@ func (c *ServerCommand) Run(args []string) int {
// Initialize the backend
backend, err := physical.NewBackend(
config.Backend.Type, c.logger, config.Backend.Config)
config.Storage.Type, c.logger, config.Storage.Config)
if err != nil {
c.Ui.Output(fmt.Sprintf(
"Error initializing backend of type %s: %s",
config.Backend.Type, err))
"Error initializing storage of type %s: %s",
config.Storage.Type, err))
return 1
}
@ -255,7 +255,7 @@ func (c *ServerCommand) Run(args []string) int {
coreConfig := &vault.CoreConfig{
Physical: backend,
RedirectAddr: config.Backend.RedirectAddr,
RedirectAddr: config.Storage.RedirectAddr,
HAPhysical: nil,
Seal: seal,
AuditBackends: c.AuditBackends,
@ -288,39 +288,39 @@ func (c *ServerCommand) Run(args []string) int {
var disableClustering bool
// Initialize the separate HA physical backend, if it exists
// Initialize the separate HA storage backend, if it exists
var ok bool
if config.HABackend != nil {
if config.HAStorage != nil {
habackend, err := physical.NewBackend(
config.HABackend.Type, c.logger, config.HABackend.Config)
config.HAStorage.Type, c.logger, config.HAStorage.Config)
if err != nil {
c.Ui.Output(fmt.Sprintf(
"Error initializing backend of type %s: %s",
config.HABackend.Type, err))
"Error initializing HA storage of type %s: %s",
config.HAStorage.Type, err))
return 1
}
if coreConfig.HAPhysical, ok = habackend.(physical.HABackend); !ok {
c.Ui.Output("Specified HA backend does not support HA")
c.Ui.Output("Specified HA storage does not support HA")
return 1
}
if !coreConfig.HAPhysical.HAEnabled() {
c.Ui.Output("Specified HA backend has HA support disabled; please consult documentation")
c.Ui.Output("Specified HA storage has HA support disabled; please consult documentation")
return 1
}
coreConfig.RedirectAddr = config.HABackend.RedirectAddr
disableClustering = config.HABackend.DisableClustering
coreConfig.RedirectAddr = config.HAStorage.RedirectAddr
disableClustering = config.HAStorage.DisableClustering
if !disableClustering {
coreConfig.ClusterAddr = config.HABackend.ClusterAddr
coreConfig.ClusterAddr = config.HAStorage.ClusterAddr
}
} else {
if coreConfig.HAPhysical, ok = backend.(physical.HABackend); ok {
coreConfig.RedirectAddr = config.Backend.RedirectAddr
disableClustering = config.Backend.DisableClustering
coreConfig.RedirectAddr = config.Storage.RedirectAddr
disableClustering = config.Storage.DisableClustering
if !disableClustering {
coreConfig.ClusterAddr = config.Backend.ClusterAddr
coreConfig.ClusterAddr = config.Storage.ClusterAddr
}
}
}
@ -422,12 +422,12 @@ CLUSTER_SYNTHESIS_COMPLETE:
c.reloadFuncsLock = coreConfig.ReloadFuncsLock
// Compile server information for output later
info["backend"] = config.Backend.Type
info["storage"] = config.Storage.Type
info["log level"] = logLevel
info["mlock"] = fmt.Sprintf(
"supported: %v, enabled: %v",
mlock.Supported(), !config.DisableMlock && mlock.Supported())
infoKeys = append(infoKeys, "log level", "mlock", "backend")
infoKeys = append(infoKeys, "log level", "mlock", "storage")
if coreConfig.ClusterAddr != "" {
info["cluster address"] = coreConfig.ClusterAddr
@ -438,16 +438,16 @@ CLUSTER_SYNTHESIS_COMPLETE:
infoKeys = append(infoKeys, "redirect address")
}
if config.HABackend != nil {
info["HA backend"] = config.HABackend.Type
infoKeys = append(infoKeys, "HA backend")
if config.HAStorage != nil {
info["HA storage"] = config.HAStorage.Type
infoKeys = append(infoKeys, "HA storage")
} else {
// If the backend supports HA, then note it
// If the storage supports HA, then note it
if coreConfig.HAPhysical != nil {
if coreConfig.HAPhysical.HAEnabled() {
info["backend"] += " (HA available)"
info["storage"] += " (HA available)"
} else {
info["backend"] += " (HA disabled)"
info["storage"] += " (HA disabled)"
}
}
}

View File

@ -15,28 +15,32 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/vault/helper/parseutil"
)
// Config is the configuration for the vault server.
type Config struct {
Listeners []*Listener `hcl:"-"`
Backend *Backend `hcl:"-"`
HABackend *Backend `hcl:"-"`
Storage *Storage `hcl:"-"`
HAStorage *Storage `hcl:"-"`
HSM *HSM `hcl:"-"`
CacheSize int `hcl:"cache_size"`
DisableCache bool `hcl:"disable_cache"`
DisableMlock bool `hcl:"disable_mlock"`
CacheSize int `hcl:"cache_size"`
DisableCache bool `hcl:"-"`
DisableCacheRaw interface{} `hcl:"disable_cache"`
DisableMlock bool `hcl:"-"`
DisableMlockRaw interface{} `hcl:"disable_mlock"`
EnableUI bool `hcl:"ui"`
EnableUI bool `hcl:"-"`
EnableUIRaw interface{} `hcl:"ui"`
Telemetry *Telemetry `hcl:"telemetry"`
MaxLeaseTTL time.Duration `hcl:"-"`
MaxLeaseTTLRaw string `hcl:"max_lease_ttl"`
MaxLeaseTTLRaw interface{} `hcl:"max_lease_ttl"`
DefaultLeaseTTL time.Duration `hcl:"-"`
DefaultLeaseTTLRaw string `hcl:"default_lease_ttl"`
DefaultLeaseTTLRaw interface{} `hcl:"default_lease_ttl"`
ClusterName string `hcl:"cluster_name"`
PluginDirectory string `hcl:"plugin_directory"`
@ -48,7 +52,7 @@ func DevConfig(ha, transactional bool) *Config {
DisableCache: false,
DisableMlock: true,
Backend: &Backend{
Storage: &Storage{
Type: "inmem",
},
@ -72,11 +76,11 @@ func DevConfig(ha, transactional bool) *Config {
switch {
case ha && transactional:
ret.Backend.Type = "inmem_transactional_ha"
ret.Storage.Type = "inmem_transactional_ha"
case !ha && transactional:
ret.Backend.Type = "inmem_transactional"
ret.Storage.Type = "inmem_transactional"
case ha && !transactional:
ret.Backend.Type = "inmem_ha"
ret.Storage.Type = "inmem_ha"
}
return ret
@ -92,8 +96,8 @@ func (l *Listener) GoString() string {
return fmt.Sprintf("*%#v", *l)
}
// Backend is the backend configuration for the server.
type Backend struct {
// Storage is the underlying storage configuration for the server.
type Storage struct {
Type string
RedirectAddr string
ClusterAddr string
@ -101,7 +105,7 @@ type Backend struct {
Config map[string]string
}
func (b *Backend) GoString() string {
func (b *Storage) GoString() string {
return fmt.Sprintf("*%#v", *b)
}
@ -212,14 +216,14 @@ func (c *Config) Merge(c2 *Config) *Config {
result.Listeners = append(result.Listeners, l)
}
result.Backend = c.Backend
if c2.Backend != nil {
result.Backend = c2.Backend
result.Storage = c.Storage
if c2.Storage != nil {
result.Storage = c2.Storage
}
result.HABackend = c.HABackend
if c2.HABackend != nil {
result.HABackend = c2.HABackend
result.HAStorage = c.HAStorage
if c2.HAStorage != nil {
result.HAStorage = c2.HAStorage
}
result.HSM = c.HSM
@ -310,13 +314,31 @@ func ParseConfig(d string, logger log.Logger) (*Config, error) {
return nil, err
}
if result.MaxLeaseTTLRaw != "" {
if result.MaxLeaseTTL, err = time.ParseDuration(result.MaxLeaseTTLRaw); err != nil {
if result.MaxLeaseTTLRaw != nil {
if result.MaxLeaseTTL, err = parseutil.ParseDurationSecond(result.MaxLeaseTTLRaw); err != nil {
return nil, err
}
}
if result.DefaultLeaseTTLRaw != "" {
if result.DefaultLeaseTTL, err = time.ParseDuration(result.DefaultLeaseTTLRaw); err != nil {
if result.DefaultLeaseTTLRaw != nil {
if result.DefaultLeaseTTL, err = parseutil.ParseDurationSecond(result.DefaultLeaseTTLRaw); err != nil {
return nil, err
}
}
if result.EnableUIRaw != nil {
if result.EnableUI, err = parseutil.ParseBool(result.EnableUIRaw); err != nil {
return nil, err
}
}
if result.DisableCacheRaw != nil {
if result.DisableCache, err = parseutil.ParseBool(result.DisableCacheRaw); err != nil {
return nil, err
}
}
if result.DisableMlockRaw != nil {
if result.DisableMlock, err = parseutil.ParseBool(result.DisableMlockRaw); err != nil {
return nil, err
}
}
@ -328,6 +350,8 @@ func ParseConfig(d string, logger log.Logger) (*Config, error) {
valid := []string{
"atlas",
"storage",
"ha_storage",
"backend",
"ha_backend",
"hsm",
@ -346,15 +370,28 @@ func ParseConfig(d string, logger log.Logger) (*Config, error) {
return nil, err
}
if o := list.Filter("backend"); len(o.Items) > 0 {
if err := parseBackends(&result, o); err != nil {
return nil, fmt.Errorf("error parsing 'backend': %s", err)
// Look for storage but still support old backend
if o := list.Filter("storage"); len(o.Items) > 0 {
if err := parseStorage(&result, o, "storage"); err != nil {
return nil, fmt.Errorf("error parsing 'storage': %s", err)
}
} else {
if o := list.Filter("backend"); len(o.Items) > 0 {
if err := parseStorage(&result, o, "backend"); err != nil {
return nil, fmt.Errorf("error parsing 'backend': %s", err)
}
}
}
if o := list.Filter("ha_backend"); len(o.Items) > 0 {
if err := parseHABackends(&result, o); err != nil {
return nil, fmt.Errorf("error parsing 'ha_backend': %s", err)
if o := list.Filter("ha_storage"); len(o.Items) > 0 {
if err := parseHAStorage(&result, o, "ha_storage"); err != nil {
return nil, fmt.Errorf("error parsing 'ha_storage': %s", err)
}
} else {
if o := list.Filter("ha_backend"); len(o.Items) > 0 {
if err := parseHAStorage(&result, o, "ha_backend"); err != nil {
return nil, fmt.Errorf("error parsing 'ha_backend': %s", err)
}
}
}
@ -456,22 +493,22 @@ func isTemporaryFile(name string) bool {
(strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#")) // emacs
}
func parseBackends(result *Config, list *ast.ObjectList) error {
func parseStorage(result *Config, list *ast.ObjectList, name string) error {
if len(list.Items) > 1 {
return fmt.Errorf("only one 'backend' block is permitted")
return fmt.Errorf("only one %q block is permitted", name)
}
// Get our item
item := list.Items[0]
key := "backend"
key := name
if len(item.Keys) > 0 {
key = item.Keys[0].Token.Value().(string)
}
var m map[string]string
if err := hcl.DecodeObject(&m, item.Val); err != nil {
return multierror.Prefix(err, fmt.Sprintf("backend.%s:", key))
return multierror.Prefix(err, fmt.Sprintf("%s.%s:", name, key))
}
// Pull out the redirect address since it's common to all backends
@ -496,12 +533,12 @@ func parseBackends(result *Config, list *ast.ObjectList) error {
if v, ok := m["disable_clustering"]; ok {
disableClustering, err = strconv.ParseBool(v)
if err != nil {
return multierror.Prefix(err, fmt.Sprintf("backend.%s:", key))
return multierror.Prefix(err, fmt.Sprintf("%s.%s:", name, key))
}
delete(m, "disable_clustering")
}
result.Backend = &Backend{
result.Storage = &Storage{
RedirectAddr: redirectAddr,
ClusterAddr: clusterAddr,
DisableClustering: disableClustering,
@ -511,22 +548,22 @@ func parseBackends(result *Config, list *ast.ObjectList) error {
return nil
}
func parseHABackends(result *Config, list *ast.ObjectList) error {
func parseHAStorage(result *Config, list *ast.ObjectList, name string) error {
if len(list.Items) > 1 {
return fmt.Errorf("only one 'ha_backend' block is permitted")
return fmt.Errorf("only one %q block is permitted", name)
}
// Get our item
item := list.Items[0]
key := "backend"
key := name
if len(item.Keys) > 0 {
key = item.Keys[0].Token.Value().(string)
}
var m map[string]string
if err := hcl.DecodeObject(&m, item.Val); err != nil {
return multierror.Prefix(err, fmt.Sprintf("ha_backend.%s:", key))
return multierror.Prefix(err, fmt.Sprintf("%s.%s:", name, key))
}
// Pull out the redirect address since it's common to all backends
@ -551,12 +588,12 @@ func parseHABackends(result *Config, list *ast.ObjectList) error {
if v, ok := m["disable_clustering"]; ok {
disableClustering, err = strconv.ParseBool(v)
if err != nil {
return multierror.Prefix(err, fmt.Sprintf("backend.%s:", key))
return multierror.Prefix(err, fmt.Sprintf("%s.%s:", name, key))
}
delete(m, "disable_clustering")
}
result.HABackend = &Backend{
result.HAStorage = &Storage{
RedirectAddr: redirectAddr,
ClusterAddr: clusterAddr,
DisableClustering: disableClustering,
@ -627,6 +664,7 @@ func parseListeners(result *Config, list *ast.ObjectList) error {
"tls_min_version",
"tls_cipher_suites",
"tls_prefer_server_cipher_suites",
"tls_require_and_verify_client_cert",
"token",
}
if err := checkHCLKeys(item.Val, valid); err != nil {

View File

@ -37,7 +37,7 @@ func TestLoadConfigFile(t *testing.T) {
},
},
Backend: &Backend{
Storage: &Storage{
Type: "consul",
RedirectAddr: "foo",
Config: map[string]string{
@ -45,7 +45,7 @@ func TestLoadConfigFile(t *testing.T) {
},
},
HABackend: &Backend{
HAStorage: &Storage{
Type: "consul",
RedirectAddr: "snafu",
Config: map[string]string{
@ -60,9 +60,12 @@ func TestLoadConfigFile(t *testing.T) {
DisableHostname: false,
},
DisableCache: true,
DisableMlock: true,
EnableUI: true,
DisableCache: true,
DisableCacheRaw: true,
DisableMlock: true,
DisableMlockRaw: true,
EnableUI: true,
EnableUIRaw: true,
MaxLeaseTTL: 10 * time.Hour,
MaxLeaseTTLRaw: "10h",
@ -102,7 +105,7 @@ func TestLoadConfigFile_json(t *testing.T) {
},
},
Backend: &Backend{
Storage: &Storage{
Type: "consul",
Config: map[string]string{
"foo": "bar",
@ -134,7 +137,10 @@ func TestLoadConfigFile_json(t *testing.T) {
DefaultLeaseTTL: 10 * time.Hour,
DefaultLeaseTTLRaw: "10h",
ClusterName: "testcluster",
DisableCacheRaw: interface{}(nil),
DisableMlockRaw: interface{}(nil),
EnableUI: true,
EnableUIRaw: true,
}
if !reflect.DeepEqual(config, expected) {
t.Fatalf("expected \n\n%#v\n\n to be \n\n%#v\n\n", config, expected)
@ -165,7 +171,7 @@ func TestLoadConfigFile_json2(t *testing.T) {
},
},
Backend: &Backend{
Storage: &Storage{
Type: "consul",
Config: map[string]string{
"foo": "bar",
@ -173,7 +179,7 @@ func TestLoadConfigFile_json2(t *testing.T) {
DisableClustering: true,
},
HABackend: &Backend{
HAStorage: &Storage{
Type: "consul",
Config: map[string]string{
"bar": "baz",
@ -228,7 +234,7 @@ func TestLoadConfigDir(t *testing.T) {
},
},
Backend: &Backend{
Storage: &Storage{
Type: "consul",
Config: map[string]string{
"foo": "bar",

View File

@ -97,6 +97,15 @@ func listenerWrapTLS(
}
tlsConf.PreferServerCipherSuites = preferServer
}
if v, ok := config["tls_require_and_verify_client_cert"]; ok {
requireClient, err := strconv.ParseBool(v)
if err != nil {
return nil, nil, nil, fmt.Errorf("invalid value for 'tls_require_and_verify_client_cert': %v", err)
}
if requireClient {
tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
}
}
ln = tls.NewListener(ln, tlsConf)
props["tls"] = "enabled"

View File

@ -11,7 +11,7 @@
"node_id": "foo_node"
}
}],
"backend": {
"storage": {
"consul": {
"foo": "bar",
"disable_clustering": "true"

View File

@ -12,12 +12,12 @@
}
}
],
"backend":{
"storage":{
"consul":{
"foo":"bar"
}
},
"ha_backend":{
"ha_storage":{
"consul":{
"bar":"baz",
"disable_clustering": "true"

View File

@ -64,8 +64,8 @@ func TestServer_GoodSeparateHA(t *testing.T) {
t.Fatalf("bad: %d\n\n%s\n\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
}
if !strings.Contains(ui.OutputWriter.String(), "HA Backend:") {
t.Fatalf("did not find HA Backend: %s", ui.OutputWriter.String())
if !strings.Contains(ui.OutputWriter.String(), "HA Storage:") {
t.Fatalf("did not find HA Storage: %s", ui.OutputWriter.String())
}
}

View File

@ -6,7 +6,7 @@ import (
"time"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/helper/parseutil"
"github.com/hashicorp/vault/meta"
)
@ -44,7 +44,7 @@ func (c *TokenRenewCommand) Run(args []string) int {
increment = args[1]
}
if increment != "" {
dur, err := duration.ParseDurationSecond(increment)
dur, err := parseutil.ParseDurationSecond(increment)
if err != nil {
c.Ui.Error(fmt.Sprintf("Invalid increment: %s", err))
return 1

View File

@ -1,28 +1,32 @@
package locksutil
import (
"fmt"
"crypto/md5"
"sync"
)
// Takes in a map, indexed by string and creates new 'sync.RWMutex' items.
// This utility creates 'count' number of mutexes (with a cap of 256) and
// places them in the map. The indices will be 2 character hexadecimal
// string values from 0 to count.
func CreateLocks(p map[string]*sync.RWMutex, count int64) error {
// Since the indices of the map entries are based on 2 character
// hex values, this utility can only create upto 256 locks.
if count <= 0 || count > 256 {
return fmt.Errorf("invalid count: %d", count)
}
const (
LockCount = 256
)
if p == nil {
return fmt.Errorf("map of locks is not initialized")
}
for i := int64(0); i < count; i++ {
p[fmt.Sprintf("%02x", i)] = &sync.RWMutex{}
}
return nil
type LockEntry struct {
sync.RWMutex
}
func CreateLocks() []*LockEntry {
ret := make([]*LockEntry, LockCount)
for i := range ret {
ret[i] = new(LockEntry)
}
return ret
}
func LockIndexForKey(key string) uint8 {
hf := md5.New()
hf.Write([]byte(key))
return uint8(hf.Sum(nil)[0])
}
func LockForKey(locks []*LockEntry, key string) *LockEntry {
return locks[LockIndexForKey(key)]
}

View File

@ -1,47 +1,10 @@
package locksutil
import (
"sync"
"testing"
)
import "testing"
func Test_CreateLocks(t *testing.T) {
locks := map[string]*sync.RWMutex{}
// Invalid argument
if err := CreateLocks(locks, -1); err == nil {
t.Fatal("expected an error")
}
// Invalid argument
if err := CreateLocks(locks, 0); err == nil {
t.Fatal("expected an error")
}
// Invalid argument
if err := CreateLocks(locks, 300); err == nil {
t.Fatal("expected an error")
}
// Maximum number of locks
if err := CreateLocks(locks, 256); err != nil {
t.Fatalf("err: %v", err)
}
locks := CreateLocks()
if len(locks) != 256 {
t.Fatalf("bad: len(locks): expected:256 actual:%d", len(locks))
}
// Clear out the locks for testing the next case
for k, _ := range locks {
delete(locks, k)
}
// General case
if err := CreateLocks(locks, 10); err != nil {
t.Fatalf("err: %v", err)
}
if len(locks) != 10 {
t.Fatalf("bad: len(locks): expected:10 actual:%d", len(locks))
}
}

View File

@ -1,4 +1,4 @@
package duration
package parseutil
import (
"encoding/json"
@ -6,6 +6,8 @@ import (
"strconv"
"strings"
"time"
"github.com/mitchellh/mapstructure"
)
func ParseDurationSecond(in interface{}) (time.Duration, error) {
@ -50,3 +52,11 @@ func ParseDurationSecond(in interface{}) (time.Duration, error) {
return dur, nil
}
func ParseBool(in interface{}) (bool, error) {
var result bool
if err := mapstructure.WeakDecode(in, &result); err != nil {
return false, err
}
return result, nil
}

View File

@ -1,4 +1,4 @@
package duration
package parseutil
import (
"encoding/json"
@ -29,3 +29,27 @@ func Test_ParseDurationSecond(t *testing.T) {
t.Fatal("not equivalent")
}
}
func Test_ParseBool(t *testing.T) {
outp, err := ParseBool("true")
if err != nil {
t.Fatal(err)
}
if !outp {
t.Fatal("wrong output")
}
outp, err = ParseBool(1)
if err != nil {
t.Fatal(err)
}
if !outp {
t.Fatal("wrong output")
}
outp, err = ParseBool(true)
if err != nil {
t.Fatal(err)
}
if !outp {
t.Fatal("wrong output")
}
}

View File

@ -255,3 +255,22 @@ func StrListDelete(s []string, d string) []string {
return s
}
func GlobbedStringsMatch(item, val string) bool {
if len(item) < 2 {
return val == item
}
hasPrefix := strings.HasPrefix(item, "*")
hasSuffix := strings.HasSuffix(item, "*")
if hasPrefix && hasSuffix {
return strings.Contains(val, item[1:len(item)-1])
} else if hasPrefix {
return strings.HasSuffix(val, item[1:])
} else if hasSuffix {
return strings.HasPrefix(val, item[:len(item)-1])
}
return val == item
}

View File

@ -279,3 +279,39 @@ $$`,
t.Fatalf("bad: expected:\n%#v\nactual:\n%#v", jsonExpected, actual)
}
}
func TestGlobbedStringsMatch(t *testing.T) {
type tCase struct {
item string
val string
expect bool
}
tCases := []tCase{
tCase{"", "", true},
tCase{"*", "*", true},
tCase{"**", "**", true},
tCase{"*t", "t", true},
tCase{"*t", "test", true},
tCase{"t*", "test", true},
tCase{"*test", "test", true},
tCase{"*test", "a test", true},
tCase{"test", "a test", false},
tCase{"*test", "tests", false},
tCase{"test*", "test", true},
tCase{"test*", "testsss", true},
tCase{"test**", "testsss", false},
tCase{"test**", "test*", true},
tCase{"**test", "*test", true},
tCase{"TEST", "test", false},
tCase{"test", "test", true},
}
for _, tc := range tCases {
actual := GlobbedStringsMatch(tc.item, tc.val)
if actual != tc.expect {
t.Fatalf("Bad testcase %#v, expected %b, got %b", tc, tc.expect, actual)
}
}
}

View File

@ -10,7 +10,7 @@ import (
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/helper/parseutil"
"github.com/hashicorp/vault/helper/jsonutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/vault"
@ -274,6 +274,7 @@ func requestAuth(core *vault.Core, r *http.Request, req *logical.Request) *logic
te, err := core.LookupToken(v)
if err == nil && te != nil {
req.ClientTokenAccessor = te.Accessor
req.ClientTokenRemainingUses = te.NumUses
}
}
@ -289,7 +290,7 @@ func requestWrapInfo(r *http.Request, req *logical.Request) (*logical.Request, e
}
// If it has an allowed suffix parse as a duration string
dur, err := duration.ParseDurationSecond(wrapTTL)
dur, err := parseutil.ParseDurationSecond(wrapTTL)
if err != nil {
return req, err
}

View File

@ -80,6 +80,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -89,6 +90,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -98,6 +100,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},
@ -108,6 +111,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -117,6 +121,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -126,6 +131,7 @@ func TestSysMounts_headerAuth(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},

View File

@ -120,7 +120,7 @@ func handleSysGenerateRootUpdate(core *vault.Core) http.Handler {
if req.Key == "" {
respondError(
w, http.StatusBadRequest,
errors.New("'key' must specified in request body as JSON"))
errors.New("'key' must be specified in request body as JSON"))
return
}

View File

@ -32,6 +32,7 @@ func TestSysMounts(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -41,6 +42,7 @@ func TestSysMounts(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -50,6 +52,7 @@ func TestSysMounts(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},
@ -60,6 +63,7 @@ func TestSysMounts(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -69,6 +73,7 @@ func TestSysMounts(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -78,6 +83,7 @@ func TestSysMounts(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},
@ -119,6 +125,7 @@ func TestSysMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -128,6 +135,7 @@ func TestSysMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -137,6 +145,7 @@ func TestSysMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -146,6 +155,7 @@ func TestSysMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},
@ -156,6 +166,7 @@ func TestSysMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -165,6 +176,7 @@ func TestSysMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -174,6 +186,7 @@ func TestSysMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -183,6 +196,7 @@ func TestSysMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},
@ -246,6 +260,7 @@ func TestSysRemount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -255,6 +270,7 @@ func TestSysRemount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -264,6 +280,7 @@ func TestSysRemount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -273,6 +290,7 @@ func TestSysRemount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},
@ -283,6 +301,7 @@ func TestSysRemount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -292,6 +311,7 @@ func TestSysRemount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -301,6 +321,7 @@ func TestSysRemount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -310,6 +331,7 @@ func TestSysRemount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},
@ -354,6 +376,7 @@ func TestSysUnmount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -363,6 +386,7 @@ func TestSysUnmount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -372,6 +396,7 @@ func TestSysUnmount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},
@ -382,6 +407,7 @@ func TestSysUnmount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -391,6 +417,7 @@ func TestSysUnmount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -400,6 +427,7 @@ func TestSysUnmount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},
@ -441,6 +469,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -450,6 +479,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -459,6 +489,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -468,6 +499,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},
@ -478,6 +510,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -487,6 +520,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -496,6 +530,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -505,6 +540,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},
@ -567,6 +603,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("259196400"),
"max_lease_ttl": json.Number("259200000"),
"force_no_cache": false,
},
"local": false,
},
@ -576,6 +613,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -585,6 +623,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -594,6 +633,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},
@ -604,6 +644,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("259196400"),
"max_lease_ttl": json.Number("259200000"),
"force_no_cache": false,
},
"local": false,
},
@ -613,6 +654,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -622,6 +664,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": false,
},
@ -631,6 +674,7 @@ func TestSysTuneMount(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"force_no_cache": false,
},
"local": true,
},
@ -656,9 +700,11 @@ func TestSysTuneMount(t *testing.T) {
"data": map[string]interface{}{
"default_lease_ttl": json.Number("259196400"),
"max_lease_ttl": json.Number("259200000"),
"force_no_cache": false,
},
"default_lease_ttl": json.Number("259196400"),
"max_lease_ttl": json.Number("259200000"),
"force_no_cache": false,
}
testResponseStatus(t, resp, 200)
@ -687,9 +733,11 @@ func TestSysTuneMount(t *testing.T) {
"data": map[string]interface{}{
"default_lease_ttl": json.Number("40"),
"max_lease_ttl": json.Number("80"),
"force_no_cache": false,
},
"default_lease_ttl": json.Number("40"),
"max_lease_ttl": json.Number("80"),
"force_no_cache": false,
}
testResponseStatus(t, resp, 200)

View File

@ -48,6 +48,10 @@ func TestSysMountConfig(t *testing.T) {
t.Fatalf("Expected default lease TTL: %d, got %d",
expectedMaxTTL, mountConfig.MaxLeaseTTL)
}
if mountConfig.ForceNoCache == true {
t.Fatalf("did not expect force cache")
}
}
// testMount sets up a test mount of a generic backend w/ a random path; caller

View File

@ -168,7 +168,7 @@ func handleSysRekeyUpdate(core *vault.Core, recovery bool) http.Handler {
if req.Key == "" {
respondError(
w, http.StatusBadRequest,
errors.New("'key' must specified in request body as JSON"))
errors.New("'key' must be specified in request body as JSON"))
return
}

View File

@ -88,7 +88,7 @@ func handleSysUnseal(core *vault.Core) http.Handler {
if !req.Reset && req.Key == "" {
respondError(
w, http.StatusBadRequest,
errors.New("'key' must specified in request body as JSON, or 'reset' set to true"))
errors.New("'key' must be specified in request body as JSON, or 'reset' set to true"))
return
}

View File

@ -13,7 +13,7 @@ import (
log "github.com/mgutz/logxi/v1"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/helper/parseutil"
"github.com/hashicorp/vault/helper/errutil"
"github.com/hashicorp/vault/helper/logformat"
"github.com/hashicorp/vault/logical"
@ -551,7 +551,7 @@ func (s *FieldSchema) DefaultOrZero() interface{} {
case float64:
result = int(inp)
case string:
dur, err := duration.ParseDurationSecond(inp)
dur, err := parseutil.ParseDurationSecond(inp)
if err != nil {
return s.Type.Zero()
}

View File

@ -4,7 +4,7 @@ import (
"encoding/json"
"fmt"
"github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/helper/parseutil"
"github.com/mitchellh/mapstructure"
)
@ -161,7 +161,7 @@ func (d *FieldData) getPrimitive(
case float64:
result = int(inp)
case string:
dur, err := duration.ParseDurationSecond(inp)
dur, err := parseutil.ParseDurationSecond(inp)
if err != nil {
return nil, true, err
}

View File

@ -85,6 +85,10 @@ type Request struct {
// WrapInfo contains requested response wrapping parameters
WrapInfo *RequestWrapInfo `json:"wrap_info" structs:"wrap_info" mapstructure:"wrap_info"`
// ClientTokenRemainingUses represents the allowed number of uses left on the
// token supplied
ClientTokenRemainingUses int `json:"client_token_remaining_uses" structs:"client_token_remaining_uses" mapstructure:"client_token_remaining_uses"`
// For replication, contains the last WAL on the remote side after handling
// the request, used for best-effort avoidance of stale read-after-write
lastRemoteWAL uint64

View File

@ -1,15 +1,11 @@
package physical
import (
"crypto/sha1"
"encoding/hex"
"fmt"
"strings"
"sync"
"github.com/hashicorp/golang-lru"
"github.com/hashicorp/vault/helper/locksutil"
"github.com/hashicorp/vault/helper/strutil"
log "github.com/mgutz/logxi/v1"
)
@ -26,7 +22,7 @@ type Cache struct {
backend Backend
transactional Transactional
lru *lru.TwoQueueCache
locks map[string]*sync.RWMutex
locks []*locksutil.LockEntry
logger log.Logger
}
@ -43,13 +39,9 @@ func NewCache(b Backend, size int, logger log.Logger) *Cache {
c := &Cache{
backend: b,
lru: cache,
locks: make(map[string]*sync.RWMutex, 256),
locks: locksutil.CreateLocks(),
logger: logger,
}
if err := locksutil.CreateLocks(c.locks, 256); err != nil {
logger.Error("physical/cache: error creating locks", "error", err)
return nil
}
if txnl, ok := c.backend.(Transactional); ok {
c.transactional = txnl
@ -58,31 +50,10 @@ func NewCache(b Backend, size int, logger log.Logger) *Cache {
return c
}
func (c *Cache) lockHashForKey(key string) string {
hf := sha1.New()
hf.Write([]byte(key))
return strings.ToLower(hex.EncodeToString(hf.Sum(nil))[:2])
}
func (c *Cache) lockForKey(key string) *sync.RWMutex {
return c.locks[c.lockHashForKey(key)]
}
// Purge is used to clear the cache
func (c *Cache) Purge() {
// Lock the world
lockHashes := make([]string, 0, len(c.locks))
for hash := range c.locks {
lockHashes = append(lockHashes, hash)
}
// Sort and deduplicate. This ensures we don't try to grab the same lock
// twice, and enforcing a sort means we'll not have multiple goroutines
// deadlock by acquiring in different orders.
lockHashes = strutil.RemoveDuplicates(lockHashes)
for _, lockHash := range lockHashes {
lock := c.locks[lockHash]
for _, lock := range c.locks {
lock.Lock()
defer lock.Unlock()
}
@ -91,7 +62,7 @@ func (c *Cache) Purge() {
}
func (c *Cache) Put(entry *Entry) error {
lock := c.lockForKey(entry.Key)
lock := locksutil.LockForKey(c.locks, entry.Key)
lock.Lock()
defer lock.Unlock()
@ -103,7 +74,7 @@ func (c *Cache) Put(entry *Entry) error {
}
func (c *Cache) Get(key string) (*Entry, error) {
lock := c.lockForKey(key)
lock := locksutil.LockForKey(c.locks, key)
lock.RLock()
defer lock.RUnlock()
@ -139,7 +110,7 @@ func (c *Cache) Get(key string) (*Entry, error) {
}
func (c *Cache) Delete(key string) error {
lock := c.lockForKey(key)
lock := locksutil.LockForKey(c.locks, key)
lock.Lock()
defer lock.Unlock()
@ -162,18 +133,8 @@ func (c *Cache) Transaction(txns []TxnEntry) error {
return fmt.Errorf("physical/cache: underlying backend does not support transactions")
}
var lockHashes []string
for _, txn := range txns {
lockHashes = append(lockHashes, c.lockHashForKey(txn.Entry.Key))
}
// Sort and deduplicate. This ensures we don't try to grab the same lock
// twice, and enforcing a sort means we'll not have multiple goroutines
// deadlock by acquiring in different orders.
lockHashes = strutil.RemoveDuplicates(lockHashes)
for _, lockHash := range lockHashes {
lock := c.locks[lockHash]
// Lock the world
for _, lock := range c.locks {
lock.Lock()
defer lock.Unlock()
}

View File

@ -321,6 +321,9 @@ func (c *ConsulBackend) Transaction(txns []TxnEntry) error {
ops = append(ops, cop)
}
c.permitPool.Acquire()
defer c.permitPool.Release()
ok, resp, _, err := c.kv.Txn(ops, nil)
if err != nil {
return err

View File

@ -198,13 +198,12 @@ func testDynamoDBLockTTL(t *testing.T, ha HABackend) {
// The first lock should have lost the leader channel
leaderChClosed := false
blocking := make(chan struct{})
time.AfterFunc(watchInterval*3, func() {
close(blocking)
})
// Attempt to read from the leader or the blocking channel, which ever one
// happens first.
go func() {
select {
case <-time.After(watchInterval * 3):
return
case <-leaderCh:
leaderChClosed = true
close(blocking)

View File

@ -32,6 +32,9 @@ type EtcdBackend struct {
etcd *clientv3.Client
}
// etcd default lease duration is 60s. set to 15s for faster recovery.
const etcd3LockTimeoutInSeconds = 15
// newEtcd3Backend constructs a etcd3 backend.
func newEtcd3Backend(conf map[string]string, logger log.Logger) (Backend, error) {
// Get the etcd path form the configuration.
@ -228,7 +231,7 @@ type EtcdLock struct {
// Lock is used for mutual exclusion based on the given key.
func (c *EtcdBackend) LockWith(key, value string) (Lock, error) {
session, err := concurrency.NewSession(c.etcd)
session, err := concurrency.NewSession(c.etcd, concurrency.WithTTL(etcd3LockTimeoutInSeconds))
if err != nil {
return nil, err
}
@ -262,7 +265,7 @@ func (c *EtcdLock) Lock(stopCh <-chan struct{}) (<-chan struct{}, error) {
}
return nil, err
}
if _, err := c.etcd.Put(ctx, c.etcdMu.Key(), c.value); err != nil {
if _, err := c.etcd.Put(ctx, c.etcdMu.Key(), c.value, clientv3.WithLease(c.etcdSession.Lease())); err != nil {
return nil, err
}

View File

@ -6,6 +6,7 @@ import (
"io"
"os"
"sort"
"strconv"
"strings"
"time"
@ -16,6 +17,7 @@ import (
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/vault/helper/awsutil"
)
@ -25,6 +27,7 @@ type S3Backend struct {
bucket string
client *s3.S3
logger log.Logger
permitPool *PermitPool
}
// newS3Backend constructs a S3 backend using a pre-existing
@ -85,10 +88,23 @@ func newS3Backend(conf map[string]string, logger log.Logger) (Backend, error) {
return nil, fmt.Errorf("unable to access bucket '%s': %v", bucket, err)
}
maxParStr, ok := conf["max_parallel"]
var maxParInt int
if ok {
maxParInt, err = strconv.Atoi(maxParStr)
if err != nil {
return nil, errwrap.Wrapf("failed parsing max_parallel parameter: {{err}}", err)
}
if logger.IsDebug() {
logger.Debug("s3: max_parallel set", "max_parallel", maxParInt)
}
}
s := &S3Backend{
client: s3conn,
bucket: bucket,
logger: logger,
permitPool: NewPermitPool(maxParInt),
}
return s, nil
}
@ -97,6 +113,9 @@ func newS3Backend(conf map[string]string, logger log.Logger) (Backend, error) {
func (s *S3Backend) Put(entry *Entry) error {
defer metrics.MeasureSince([]string{"s3", "put"}, time.Now())
s.permitPool.Acquire()
defer s.permitPool.Release()
_, err := s.client.PutObject(&s3.PutObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(entry.Key),
@ -114,6 +133,9 @@ func (s *S3Backend) Put(entry *Entry) error {
func (s *S3Backend) Get(key string) (*Entry, error) {
defer metrics.MeasureSince([]string{"s3", "get"}, time.Now())
s.permitPool.Acquire()
defer s.permitPool.Release()
resp, err := s.client.GetObject(&s3.GetObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(key),
@ -122,9 +144,8 @@ func (s *S3Backend) Get(key string) (*Entry, error) {
// Return nil on 404s, error on anything else
if awsErr.StatusCode() == 404 {
return nil, nil
} else {
return nil, err
}
return nil, err
}
if err != nil {
return nil, err
@ -151,6 +172,9 @@ func (s *S3Backend) Get(key string) (*Entry, error) {
func (s *S3Backend) Delete(key string) error {
defer metrics.MeasureSince([]string{"s3", "delete"}, time.Now())
s.permitPool.Acquire()
defer s.permitPool.Release()
_, err := s.client.DeleteObject(&s3.DeleteObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(key),
@ -168,6 +192,9 @@ func (s *S3Backend) Delete(key string) error {
func (s *S3Backend) List(prefix string) ([]string, error) {
defer metrics.MeasureSince([]string{"s3", "list"}, time.Now())
s.permitPool.Acquire()
defer s.permitPool.Release()
params := &s3.ListObjectsV2Input{
Bucket: aws.String(s.bucket),
Prefix: aws.String(prefix),

View File

@ -3,7 +3,7 @@
//-------------------------------------------------------------------
variable "download-url" {
default = "https://releases.hashicorp.com/vault/0.6.5/vault_0.6.5_linux_amd64.zip"
default = "https://releases.hashicorp.com/vault/0.7.0/vault_0.7.0_linux_amd64.zip"
description = "URL to download Vault"
}

View File

@ -5,6 +5,7 @@ import (
"strings"
"github.com/armon/go-radix"
"github.com/hashicorp/vault/helper/strutil"
"github.com/hashicorp/vault/logical"
)
@ -348,7 +349,14 @@ func valueInParameterList(v interface{}, list []interface{}) bool {
func valueInSlice(v interface{}, list []interface{}) bool {
for _, el := range list {
if reflect.DeepEqual(el, v) {
if reflect.TypeOf(el).String() == "string" && reflect.TypeOf(v).String() == "string" {
item := el.(string)
val := v.(string)
if strutil.GlobbedStringsMatch(item, val) {
return true
}
} else if reflect.DeepEqual(el, v) {
return true
}
}

View File

@ -366,6 +366,7 @@ func TestACL_ValuePermissions(t *testing.T) {
{"dev/ops", []string{"allow"}, []interface{}{"good"}, true},
{"dev/ops", []string{"allow"}, []interface{}{"bad"}, false},
{"foo/bar", []string{"deny"}, []interface{}{"bad"}, false},
{"foo/bar", []string{"deny"}, []interface{}{"bad glob"}, false},
{"foo/bar", []string{"deny"}, []interface{}{"good"}, true},
{"foo/bar", []string{"allow"}, []interface{}{"good"}, true},
{"foo/baz", []string{"aLLow"}, []interface{}{"good"}, true},
@ -379,6 +380,9 @@ func TestACL_ValuePermissions(t *testing.T) {
{"fizz/buzz", []string{"allow_multi"}, []interface{}{"good"}, true},
{"fizz/buzz", []string{"allow_multi"}, []interface{}{"good1"}, true},
{"fizz/buzz", []string{"allow_multi"}, []interface{}{"good2"}, true},
{"fizz/buzz", []string{"allow_multi"}, []interface{}{"glob good2"}, false},
{"fizz/buzz", []string{"allow_multi"}, []interface{}{"glob good3"}, true},
{"fizz/buzz", []string{"allow_multi"}, []interface{}{"bad"}, false},
{"fizz/buzz", []string{"allow_multi"}, []interface{}{"bad"}, false},
{"fizz/buzz", []string{"allow_multi", "allow"}, []interface{}{"good1", "good"}, true},
{"fizz/buzz", []string{"deny_multi"}, []interface{}{"bad2"}, false},
@ -686,7 +690,7 @@ path "dev/*" {
path "foo/bar" {
policy = "write"
denied_parameters = {
"deny" = ["bad"]
"deny" = ["bad*"]
}
}
path "foo/baz" {
@ -701,7 +705,7 @@ path "foo/baz" {
path "fizz/buzz" {
policy = "write"
allowed_parameters = {
"allow_multi" = ["good", "good1", "good2"]
"allow_multi" = ["good", "good1", "good2", "*good3"]
"allow" = ["good"]
}
denied_parameters = {

View File

@ -87,6 +87,9 @@ func (c *Core) enableAudit(entry *MountEntry) error {
if err != nil {
return err
}
if backend == nil {
return fmt.Errorf("nil audit backend of type %q returned from factory", entry.Type)
}
newTable := c.audit.shallowClone()
newTable.Entries = append(newTable.Entries, entry)
@ -300,14 +303,18 @@ func (c *Core) setupAudits() error {
view := NewBarrierView(c.barrier, viewPath)
// Initialize the backend
audit, err := c.newAuditBackend(entry, view, entry.Options)
backend, err := c.newAuditBackend(entry, view, entry.Options)
if err != nil {
c.logger.Error("core: failed to create audit entry", "path", entry.Path, "error", err)
continue
}
if backend == nil {
c.logger.Error("core: created audit entry was nil", "path", entry.Path, "type", entry.Type)
continue
}
// Mount the backend
broker.Register(entry.Path, audit, view)
broker.Register(entry.Path, backend, view)
successCount += 1
}
@ -376,6 +383,9 @@ func (c *Core) newAuditBackend(entry *MountEntry, view logical.Storage, conf map
if err != nil {
return nil, err
}
if be == nil {
return nil, fmt.Errorf("nil backend returned from %q factory function", entry.Type)
}
switch entry.Type {
case "file":

View File

@ -444,6 +444,7 @@ func TestAuditBroker_LogResponse(t *testing.T) {
b.Register("bar", a2, nil)
auth := &logical.Auth{
NumUses: 10,
ClientToken: "foo",
Policies: []string{"dev", "ops"},
Metadata: map[string]string{

View File

@ -91,6 +91,9 @@ func (c *Core) enableCredential(entry *MountEntry) error {
if err != nil {
return err
}
if backend == nil {
return fmt.Errorf("nil backend returned from %q factory", entry.Type)
}
if err := backend.Initialize(); err != nil {
return err
@ -151,6 +154,12 @@ func (c *Core) disableCredential(path string) (bool, error) {
return true, err
}
// Call cleanup function if it exists
backend := c.router.MatchingBackend(fullPath)
if backend != nil {
backend.Cleanup()
}
// Unmount the backend
if err := c.router.Unmount(fullPath); err != nil {
return true, err
@ -386,6 +395,9 @@ func (c *Core) setupCredentials() error {
c.logger.Error("core: failed to create credential entry", "path", entry.Path, "error", err)
return errLoadAuthFailed
}
if backend == nil {
return fmt.Errorf("nil backend returned from %q factory", entry.Type)
}
if err := backend.Initialize(); err != nil {
return err
@ -430,10 +442,9 @@ func (c *Core) teardownCredentials() error {
if c.auth != nil {
authTable := c.auth.shallowClone()
for _, e := range authTable.Entries {
prefix := e.Path
b, ok := c.router.root.Get(prefix)
if ok {
b.(*routeEntry).backend.Cleanup()
backend := c.router.MatchingBackend(credentialRoutePrefix + e.Path)
if backend != nil {
backend.Cleanup()
}
}
}

View File

@ -1565,6 +1565,16 @@ func (c *Core) periodicCheckKeyUpgrade(doneCh, stopCh chan struct{}) {
continue
}
// Check for a poison pill. If we can read it, it means we have stale
// keys (e.g. from replication being activated) and we need to seal to
// be unsealed again.
entry, _ := c.barrier.Get(poisonPillPath)
if entry != nil && len(entry.Value) > 0 {
c.logger.Warn("core: encryption keys have changed out from underneath us (possibly due to replication enabling), must be unsealed again")
go c.Shutdown()
continue
}
if err := c.checkKeyUpgrades(); err != nil {
c.logger.Error("core: key rotation periodic upgrade check failed", "error", err)
}
@ -1581,16 +1591,6 @@ func (c *Core) checkKeyUpgrades() error {
// Check for an upgrade
didUpgrade, newTerm, err := c.barrier.CheckUpgrade()
if err != nil {
// The problem might be that we can't decrypt the value, e.g. if
// replication has been turned on, so check to see if a poison pill
// was written. If we can read it, it means we have stale keys and
// we need to seal to be unsealed again.
entry, _ := c.barrier.Get(poisonPillPath)
if entry != nil && len(entry.Value) > 0 {
c.logger.Warn("core: encryption keys have changed out from underneath us (possibly due to replication enabling), must be unsealed again")
go c.Shutdown()
return nil
}
return err
}

View File

@ -77,7 +77,7 @@ func (d dynamicSystemView) Tainted() bool {
// CachingDisabled indicates whether to use caching behavior
func (d dynamicSystemView) CachingDisabled() bool {
return d.core.cachingDisabled
return d.core.cachingDisabled || (d.mountEntry != nil && d.mountEntry.Config.ForceNoCache)
}
// Checks if this is a primary Vault instance.

View File

@ -5,7 +5,7 @@ import (
"fmt"
"strings"
"github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/helper/parseutil"
"github.com/hashicorp/vault/helper/jsonutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
@ -133,7 +133,7 @@ func (b *PassthroughBackend) handleRead(
}
ttlDuration := b.System().DefaultLeaseTTL()
if len(ttl) != 0 {
dur, err := duration.ParseDurationSecond(ttl)
dur, err := parseutil.ParseDurationSecond(ttl)
if err == nil {
ttlDuration = dur
}

View File

@ -10,7 +10,7 @@ import (
"time"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/helper/parseutil"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
"github.com/mitchellh/mapstructure"
@ -1030,6 +1030,7 @@ func (b *SystemBackend) handleMountTable(
"config": map[string]interface{}{
"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()),
"max_lease_ttl": int64(entry.Config.MaxLeaseTTL.Seconds()),
"force_no_cache": entry.Config.ForceNoCache,
},
"local": entry.Local,
}
@ -1064,6 +1065,7 @@ func (b *SystemBackend) handleMount(
var apiConfig struct {
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
}
configMap := data.Get("config").(map[string]interface{})
if configMap != nil && len(configMap) != 0 {
@ -1079,7 +1081,7 @@ func (b *SystemBackend) handleMount(
case "":
case "system":
default:
tmpDef, err := duration.ParseDurationSecond(apiConfig.DefaultLeaseTTL)
tmpDef, err := parseutil.ParseDurationSecond(apiConfig.DefaultLeaseTTL)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(
"unable to parse default TTL of %s: %s", apiConfig.DefaultLeaseTTL, err)),
@ -1092,7 +1094,7 @@ func (b *SystemBackend) handleMount(
case "":
case "system":
default:
tmpMax, err := duration.ParseDurationSecond(apiConfig.MaxLeaseTTL)
tmpMax, err := parseutil.ParseDurationSecond(apiConfig.MaxLeaseTTL)
if err != nil {
return logical.ErrorResponse(fmt.Sprintf(
"unable to parse max TTL of %s: %s", apiConfig.MaxLeaseTTL, err)),
@ -1113,6 +1115,11 @@ func (b *SystemBackend) handleMount(
logical.ErrInvalidRequest
}
// Copy over the force no cache if set
if apiConfig.ForceNoCache {
config.ForceNoCache = true
}
if logicalType == "" {
return logical.ErrorResponse(
"backend type must be specified as a string"),
@ -1248,10 +1255,17 @@ func (b *SystemBackend) handleTuneReadCommon(path string) (*logical.Response, er
return handleError(fmt.Errorf("sys: cannot fetch sysview for path %s", path))
}
mountEntry := b.Core.router.MatchingMountEntry(path)
if mountEntry == nil {
b.Backend.Logger().Error("sys: cannot fetch mount entry", "path", path)
return handleError(fmt.Errorf("sys: cannot fetch mount entry for path %s", path))
}
resp := &logical.Response{
Data: map[string]interface{}{
"default_lease_ttl": int(sysView.DefaultLeaseTTL().Seconds()),
"max_lease_ttl": int(sysView.MaxLeaseTTL().Seconds()),
"force_no_cache": mountEntry.Config.ForceNoCache,
},
}
@ -1327,7 +1341,7 @@ func (b *SystemBackend) handleTuneWriteCommon(
tmpDef := time.Duration(0)
newDefault = &tmpDef
default:
tmpDef, err := duration.ParseDurationSecond(defTTL)
tmpDef, err := parseutil.ParseDurationSecond(defTTL)
if err != nil {
return handleError(err)
}
@ -1341,7 +1355,7 @@ func (b *SystemBackend) handleTuneWriteCommon(
tmpMax := time.Duration(0)
newMax = &tmpMax
default:
tmpMax, err := duration.ParseDurationSecond(maxTTL)
tmpMax, err := parseutil.ParseDurationSecond(maxTTL)
if err != nil {
return handleError(err)
}

View File

@ -59,6 +59,7 @@ func TestSystemBackend_mounts(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": resp.Data["secret/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(int64),
"max_lease_ttl": resp.Data["secret/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64),
"force_no_cache": false,
},
"local": false,
},
@ -68,6 +69,7 @@ func TestSystemBackend_mounts(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": resp.Data["sys/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(int64),
"max_lease_ttl": resp.Data["sys/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64),
"force_no_cache": false,
},
"local": false,
},
@ -77,6 +79,7 @@ func TestSystemBackend_mounts(t *testing.T) {
"config": map[string]interface{}{
"default_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(int64),
"max_lease_ttl": resp.Data["cubbyhole/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64),
"force_no_cache": false,
},
"local": true,
},
@ -101,6 +104,32 @@ func TestSystemBackend_mount(t *testing.T) {
}
}
func TestSystemBackend_mount_force_no_cache(t *testing.T) {
core, b, _ := testCoreSystemBackend(t)
req := logical.TestRequest(t, logical.UpdateOperation, "mounts/prod/secret/")
req.Data["type"] = "generic"
req.Data["config"] = map[string]interface{}{
"force_no_cache": true,
}
resp, err := b.HandleRequest(req)
if err != nil {
t.Fatalf("err: %v", err)
}
if resp != nil {
t.Fatalf("bad: %v", resp)
}
mountEntry := core.router.MatchingMountEntry("prod/secret/")
if mountEntry == nil {
t.Fatalf("missing mount entry")
}
if !mountEntry.Config.ForceNoCache {
t.Fatalf("bad config %#v", mountEntry)
}
}
func TestSystemBackend_mount_invalid(t *testing.T) {
b := testSystemBackend(t)

View File

@ -146,6 +146,7 @@ type MountEntry struct {
type MountConfig struct {
DefaultLeaseTTL time.Duration `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` // Override for global default
MaxLeaseTTL time.Duration `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` // Override for global default
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"` // Override for global default
}
// Returns a deep copy of the mount entry
@ -212,6 +213,9 @@ func (c *Core) mount(entry *MountEntry) error {
if err != nil {
return err
}
if backend == nil {
return fmt.Errorf("nil backend of type %q returned from creation function", entry.Type)
}
// Call initialize; this takes care of init tasks that must be run after
// the ignore paths are collected
@ -283,9 +287,9 @@ func (c *Core) unmount(path string) (bool, error) {
}
// Call cleanup function if it exists
b, ok := c.router.root.Get(path)
if ok {
b.(*routeEntry).backend.Cleanup()
backend := c.router.MatchingBackend(path)
if backend != nil {
backend.Cleanup()
}
// Unmount the backend entirely
@ -638,6 +642,9 @@ func (c *Core) setupMounts() error {
c.logger.Error("core: failed to create mount entry", "path", entry.Path, "error", err)
return errLoadMountsFailed
}
if backend == nil {
return fmt.Errorf("created mount entry of type %q is nil", entry.Type)
}
if err := backend.Initialize(); err != nil {
return err
@ -680,10 +687,9 @@ func (c *Core) unloadMounts() error {
if c.mounts != nil {
mountTable := c.mounts.shallowClone()
for _, e := range mountTable.Entries {
prefix := e.Path
b, ok := c.router.root.Get(prefix)
if ok {
b.(*routeEntry).backend.Cleanup()
backend := c.router.MatchingBackend(e.Path)
if backend != nil {
backend.Cleanup()
}
}
}
@ -712,6 +718,9 @@ func (c *Core) newLogicalBackend(t string, sysView logical.SystemView, view logi
if err != nil {
return nil, err
}
if b == nil {
return nil, fmt.Errorf("nil backend of type %q returned from factory", t)
}
return b, nil
}

View File

@ -10,7 +10,7 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/helper/parseutil"
)
const (
@ -211,14 +211,14 @@ func parsePaths(result *Policy, list *ast.ObjectList) error {
}
}
if pc.MinWrappingTTLHCL != nil {
dur, err := duration.ParseDurationSecond(pc.MinWrappingTTLHCL)
dur, err := parseutil.ParseDurationSecond(pc.MinWrappingTTLHCL)
if err != nil {
return errwrap.Wrapf("error parsing min_wrapping_ttl: {{err}}", err)
}
pc.Permissions.MinWrappingTTL = dur
}
if pc.MaxWrappingTTLHCL != nil {
dur, err := duration.ParseDurationSecond(pc.MaxWrappingTTLHCL)
dur, err := parseutil.ParseDurationSecond(pc.MaxWrappingTTLHCL)
if err != nil {
return errwrap.Wrapf("error parsing max_wrapping_ttl: {{err}}", err)
}

View File

@ -283,6 +283,10 @@ func (r *Router) routeCommon(req *logical.Request, existenceCheck bool) (*logica
// Cache the identifier of the request
originalReqID := req.ID
// Cache the client token's number of uses in the request
originalClientTokenRemainingUses := req.ClientTokenRemainingUses
req.ClientTokenRemainingUses = 0
// Cache the headers and hide them from backends
headers := req.Headers
req.Headers = nil
@ -304,6 +308,7 @@ func (r *Router) routeCommon(req *logical.Request, existenceCheck bool) (*logica
req.ID = originalReqID
req.Storage = nil
req.ClientToken = clientToken
req.ClientTokenRemainingUses = originalClientTokenRemainingUses
req.WrapInfo = wrapInfo
req.Headers = headers
// This is only set in one place, after routing, so should never be set

View File

@ -5,13 +5,12 @@ import (
"fmt"
"regexp"
"strings"
"sync"
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/helper/duration"
"github.com/hashicorp/vault/helper/parseutil"
"github.com/hashicorp/vault/helper/jsonutil"
"github.com/hashicorp/vault/helper/locksutil"
"github.com/hashicorp/vault/helper/policyutil"
@ -88,7 +87,7 @@ type TokenStore struct {
policyLookupFunc func(string) (*Policy, error)
tokenLocks map[string]*sync.RWMutex
tokenLocks []*locksutil.LockEntry
cubbyholeDestroyer func(*TokenStore, string) error
}
@ -109,14 +108,7 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error)
t.policyLookupFunc = c.policyStore.GetPolicy
}
t.tokenLocks = map[string]*sync.RWMutex{}
// Create 256 locks
if err := locksutil.CreateLocks(t.tokenLocks, 256); err != nil {
return nil, fmt.Errorf("failed to create locks: %v", err)
}
t.tokenLocks["custom"] = &sync.RWMutex{}
t.tokenLocks = locksutil.CreateLocks()
// Setup the framework endpoints
t.Backend = &framework.Backend{
@ -741,21 +733,6 @@ func (ts *TokenStore) storeCommon(entry *TokenEntry, writeSecondary bool) error
return nil
}
func (ts *TokenStore) getTokenLock(id string) *sync.RWMutex {
// Find our multilevel lock, or fall back to global
var lock *sync.RWMutex
var ok bool
if len(id) >= 2 {
lock, ok = ts.tokenLocks[id[0:2]]
}
if !ok || lock == nil {
// Fall back for custom token IDs
lock = ts.tokenLocks["custom"]
}
return lock
}
// UseToken is used to manage restricted use tokens and decrement their
// available uses. Returns two values: a potentially updated entry or, if the
// token has been revoked, nil; and whether an error was encountered. The
@ -774,8 +751,7 @@ func (ts *TokenStore) UseToken(te *TokenEntry) (*TokenEntry, error) {
return te, nil
}
lock := ts.getTokenLock(te.ID)
lock := locksutil.LockForKey(ts.tokenLocks, te.ID)
lock.Lock()
defer lock.Unlock()
@ -828,7 +804,7 @@ func (ts *TokenStore) Lookup(id string) (*TokenEntry, error) {
return nil, fmt.Errorf("cannot lookup blank token")
}
lock := ts.getTokenLock(id)
lock := locksutil.LockForKey(ts.tokenLocks, id)
lock.RLock()
defer lock.RUnlock()
@ -932,7 +908,7 @@ func (ts *TokenStore) revokeSalted(saltedId string) (ret error) {
return nil
}
lock := ts.getTokenLock(entry.ID)
lock := locksutil.LockForKey(ts.tokenLocks, entry.ID)
lock.Lock()
// Lookup the token first
@ -1582,7 +1558,7 @@ func (ts *TokenStore) handleCreateCommon(
}
if data.ExplicitMaxTTL != "" {
dur, err := duration.ParseDurationSecond(data.ExplicitMaxTTL)
dur, err := parseutil.ParseDurationSecond(data.ExplicitMaxTTL)
if err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
@ -1598,7 +1574,7 @@ func (ts *TokenStore) handleCreateCommon(
return logical.ErrorResponse("root or sudo privileges required to create periodic token"),
logical.ErrInvalidRequest
}
dur, err := duration.ParseDurationSecond(data.Period)
dur, err := parseutil.ParseDurationSecond(data.Period)
if err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
@ -1611,7 +1587,7 @@ func (ts *TokenStore) handleCreateCommon(
// Parse the TTL/lease if any
if data.TTL != "" {
dur, err := duration.ParseDurationSecond(data.TTL)
dur, err := parseutil.ParseDurationSecond(data.TTL)
if err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
}
@ -1713,6 +1689,7 @@ func (ts *TokenStore) handleCreateCommon(
// Generate the response
resp.Auth = &logical.Auth{
NumUses: te.NumUses,
DisplayName: te.DisplayName,
Policies: te.Policies,
Metadata: te.Meta,
@ -1851,7 +1828,7 @@ func (ts *TokenStore) handleLookup(
return logical.ErrorResponse("missing token ID"), logical.ErrInvalidRequest
}
lock := ts.getTokenLock(id)
lock := locksutil.LockForKey(ts.tokenLocks, id)
lock.RLock()
defer lock.RUnlock()

View File

@ -9,5 +9,5 @@ func init() {
// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
VersionPrerelease = "beta1"
VersionPrerelease = ""
}

View File

@ -1,3 +1,3 @@
source "https://rubygems.org"
gem "middleman-hashicorp", "0.3.6"
gem "middleman-hashicorp", "0.3.18"

View File

@ -1,18 +1,17 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (4.2.7.1)
activesupport (4.2.8)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
autoprefixer-rails (6.5.3)
autoprefixer-rails (6.7.7.1)
execjs
bootstrap-sass (3.3.7)
autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4)
builder (3.2.2)
builder (3.2.3)
capybara (2.4.4)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
@ -23,7 +22,7 @@ GEM
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.10.0)
coffee-script-source (1.12.2)
compass (1.0.3)
chunky_png (~> 1.2)
compass-core (~> 1.0.2)
@ -40,9 +39,9 @@ GEM
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
erubis (2.7.0)
eventmachine (1.2.1)
eventmachine (1.2.3)
execjs (2.7.0)
ffi (1.9.14)
ffi (1.9.18)
haml (4.0.7)
tilt
hike (1.2.3)
@ -50,8 +49,8 @@ GEM
uber (~> 0.0.14)
http_parser.rb (0.6.0)
i18n (0.7.0)
json (1.8.3)
kramdown (1.12.0)
json (2.0.3)
kramdown (1.13.2)
listen (3.0.8)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
@ -78,13 +77,14 @@ GEM
rack (>= 1.4.5, < 2.0)
thor (>= 0.15.2, < 2.0)
tilt (~> 1.4.1, < 2.0)
middleman-hashicorp (0.3.6)
middleman-hashicorp (0.3.18)
bootstrap-sass (~> 3.3)
builder (~> 3.2)
middleman (~> 3.4)
middleman-livereload (~> 3.4)
middleman-syntax (~> 3.0)
redcarpet (~> 3.3)
turbolinks (~> 5.0)
middleman-livereload (3.4.6)
em-websocket (~> 0.5.1)
middleman-core (>= 3.3)
@ -101,9 +101,9 @@ GEM
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mini_portile2 (2.1.0)
minitest (5.9.1)
minitest (5.10.1)
multi_json (1.12.1)
nokogiri (1.6.8.1)
nokogiri (1.7.1)
mini_portile2 (~> 2.1.0)
padrino-helpers (0.12.8.1)
i18n (~> 0.6, >= 0.6.7)
@ -117,11 +117,11 @@ GEM
rack-test (0.6.3)
rack (>= 1.0)
rb-fsevent (0.9.8)
rb-inotify (0.9.7)
rb-inotify (0.9.8)
ffi (>= 0.5.0)
redcarpet (3.3.4)
rouge (2.0.6)
sass (3.4.22)
redcarpet (3.4.0)
rouge (2.0.7)
sass (3.4.23)
sprockets (2.12.4)
hike (~> 1.2)
multi_json (~> 1.0)
@ -132,10 +132,13 @@ GEM
sprockets-sass (1.3.1)
sprockets (~> 2.0)
tilt (~> 1.1)
thor (0.19.1)
thread_safe (0.3.5)
thor (0.19.4)
thread_safe (0.3.6)
tilt (1.4.1)
tzinfo (1.2.2)
turbolinks (5.0.1)
turbolinks-source (~> 5)
turbolinks-source (5.0.0)
tzinfo (1.2.3)
thread_safe (~> 0.1)
uber (0.0.15)
uglifier (2.7.2)
@ -148,7 +151,7 @@ PLATFORMS
ruby
DEPENDENCIES
middleman-hashicorp (= 0.3.6)
middleman-hashicorp (= 0.3.18)
BUNDLED WITH
1.13.6
1.14.6

View File

@ -3,8 +3,8 @@
This license is temporary while a more official one is drafted. However,
this should make it clear:
* The text contents of this website are MPL 2.0 licensed.
The text contents of this website are MPL 2.0 licensed.
* The design contents of this website are proprietary and may not be reproduced
or reused in any way other than to run the Vault website locally. The license
for the design is owned solely by HashiCorp, Inc.
The design contents of this website are proprietary and may not be reproduced
or reused in any way other than to run the website locally. The license for
the design is owned solely by HashiCorp, Inc.

View File

@ -1,4 +1,4 @@
VERSION?="0.3.6"
VERSION?="0.3.18"
website:
@echo "==> Starting website in Docker..."

View File

@ -1,18 +1,21 @@
# Vault Website
This subdirectory contains the entire source for the [Vault Website](https://www.vaultproject.io/).
This is a [Middleman](http://middlemanapp.com) project, which builds a static
site from these source files.
This subdirectory contains the entire source for the [Vault Website][vault].
This is a [Middleman][middleman] project, which builds a static site from these
source files.
## Contributions Welcome!
If you find a typo or you feel like you can improve the HTML, CSS, or
JavaScript, we welcome contributions. Feel free to open issues or pull
requests like any normal GitHub project, and we'll merge it in.
JavaScript, we welcome contributions. Feel free to open issues or pull requests
like any normal GitHub project, and we'll merge it in.
## Running the Site Locally
Running the site locally is simple. Clone this repo and run `make dev`.
Running the site locally is simple. Clone this repo and run `make website`.
Then open up `http://localhost:4567`. Note that some URLs you may need to append
".html" to make them work (in the navigation).
[middleman]: https://www.middlemanapp.com
[vault]: https://www.vaultproject.io

30
website/Vagrantfile vendored
View File

@ -1,30 +0,0 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
$script = <<SCRIPT
sudo apt-get -y update
# RVM/Ruby
sudo apt-get -y install curl git
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
curl -sSL https://get.rvm.io | bash -s stable
. ~/.bashrc
. ~/.bash_profile
rvm install 2.0.0
rvm --default use 2.0.0
gem install bundler
# Middleman deps
cd /vagrant
bundle
SCRIPT
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "bento/ubuntu-16.04"
config.vm.network "private_network", ip: "33.33.30.10"
config.vm.provision "shell", inline: $script, privileged: false
config.vm.synced_folder ".", "/vagrant", type: "rsync"
end

View File

@ -2,7 +2,71 @@ set :base_url, "https://www.vaultproject.io/"
activate :hashicorp do |h|
h.name = "vault"
h.version = "0.6.5"
h.version = "0.7.0"
h.github_slug = "hashicorp/vault"
h.website_root = "website"
end
helpers do
# Get the title for the page.
#
# @param [Middleman::Page] page
#
# @return [String]
def title_for(page)
if page && page.data.page_title
return "#{page.data.page_title} - Vault by HashiCorp"
end
"Vault by HashiCorp"
end
# Get the description for the page
#
# @param [Middleman::Page] page
#
# @return [String]
def description_for(page)
description = (page.data.description || "")
.gsub('"', '')
.gsub("/\n+/", ' ')
.squeeze(' ')
return escape_html(description)
end
# This helps by setting the "active" class for sidebar nav elements
# if the YAML frontmatter matches the expected value.
def sidebar_current(expected)
current = current_page.data.sidebar_current || ""
if current.start_with?(expected)
return " class=\"active\""
else
return ""
end
end
# Returns the id for this page.
# @return [String]
def body_id_for(page)
if !(name = page.data.sidebar_current).blank?
return "page-#{name.strip}"
end
if page.url == "/" || page.url == "/index.html"
return "page-home"
end
return ""
end
# Returns the list of classes for this page.
# @return [String]
def body_classes_for(page)
classes = []
if page && page.data.layout
classes << "layout-#{page.data.layout}"
end
return classes.join(" ")
end
end

View File

@ -1,38 +0,0 @@
require "rack"
require "rack/contrib/not_found"
require "rack/contrib/response_headers"
require "rack/contrib/static_cache"
require "rack/contrib/try_static"
require "rack/protection"
# Protect against various bad things
use Rack::Protection::JsonCsrf
use Rack::Protection::RemoteReferrer
use Rack::Protection::HttpOrigin
use Rack::Protection::EscapedParams
use Rack::Protection::XSSHeader
use Rack::Protection::FrameOptions
use Rack::Protection::PathTraversal
use Rack::Protection::IPSpoofing
# Properly compress the output if the client can handle it.
use Rack::Deflater
# Set the "forever expire" cache headers for these static assets. Since
# we hash the contents of the assets to determine filenames, this is safe
# to do.
use Rack::StaticCache,
:root => "build",
:urls => ["/images", "/javascripts", "/stylesheets"],
:duration => 2,
:versioning => false
# Try to find a static file that matches our request, since Middleman
# statically generates everything.
use Rack::TryStatic,
:root => "build",
:urls => ["/"],
:try => [".html", "index.html", "/index.html"]
# 404 if we reached this point. Sad times.
run Rack::NotFound.new(File.expand_path("../build/404.html", __FILE__))

View File

@ -1,12 +0,0 @@
module SidebarHelpers
# This helps by setting the "active" class for sidebar nav elements
# if the YAML frontmatter matches the expected value.
def sidebar_current(expected)
current = current_page.data.sidebar_current || ""
if current.start_with?(expected)
return " class=\"active\""
else
return ""
end
end
end

View File

@ -8,7 +8,7 @@
"builders": [
{
"type": "docker",
"image": "hashicorp/middleman-hashicorp:0.3.6",
"image": "hashicorp/middleman-hashicorp:0.3.18",
"discard": "true",
"run_command": ["-d", "-i", "-t", "{{ .Image }}", "/bin/sh"]
}

View File

@ -16,7 +16,7 @@
</div>
</div>
<span class="close-terminal" {{action "close"}}>X</span>
<span class="close-terminal" {{action "close"}}>&times;</span>
<div {{bind-attr class=":demo-terminal fullscreen:fullscreen" }}>
<div class="log">{{renderedLogs}}</div>

View File

@ -0,0 +1,18 @@
{
"name": "Vault",
"icons": [
{
"src": "<%= image_path('favicons/android-chrome-192x192.png') %>",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "<%= image_path('favicons/android-chrome-512x512.png') %>",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View File

@ -1,5 +1,5 @@
---
layout: "http"
layout: "api"
page_title: "HTTP API"
sidebar_current: "docs-http-overview"
description: |-
@ -170,5 +170,5 @@ The following HTTP status codes are used throughout the API.
## Limits
A maximum request size of 32MB is imposed to prevent a denial
A maximum request size of 32MB is imposed to prevent a denial
of service attack with arbitrarily large requests.

View File

@ -1,5 +1,5 @@
---
layout: "http"
layout: "api"
page_title: "HTTP API: Libraries"
sidebar_current: "docs-http-libraries"
description: |-
@ -18,12 +18,18 @@ These libraries are officially maintained by HashiCorp.
### Go
* [Vault Go Client](https://github.com/hashicorp/vault/tree/master/api)
* `go get github.com/hashicorp/vault/api`
```shell
$ go get github.com/hashicorp/vault/api
```
### Ruby
* [Vault Ruby Client](https://github.com/hashicorp/vault-ruby)
* `gem install vault`
```shell
$ gem install vault
```
## Community
@ -32,14 +38,24 @@ These libraries are provided by the community.
### Ansible
* [Ansible Modules Hashivault](https://pypi.python.org/pypi/ansible-modules-hashivault)
* `pip install ansible-modules-hashivault`
```shell
$ pip install ansible-modules-hashivault
```
### C&#35;
* [VaultSharp](https://github.com/rajanadar/VaultSharp) (.NET Standard = 1.4 (.NET Core >= 1.0.0) and also .NET 4.5.x, .NET 4.6.x)
* `Install-Package VaultSharp`
```shell
$ Install-Package VaultSharp
```
* [Vault.NET](https://github.com/Chatham/Vault.NET)
* `Install-Package Vault`
```shell
$ Install-Package Vault
```
### Clojure
@ -53,10 +69,17 @@ These libraries are provided by the community.
* [vc](https://github.com/adfinis-sygroup/vault-client)
```shell
$ go get github.com/adfinis-sygroup/vault-client
```
### Haskell
* [vault-tool](https://hackage.haskell.org/package/vault-tool)
* `cabal install vault-tool`
```shell
$ cabal install vault-tool
```
### Java
@ -71,21 +94,38 @@ These libraries are provided by the community.
### Node.js
* [node-vault](https://github.com/kr1sp1n/node-vault)
* `npm install node-vault`
```shell
$ npm install node-vault
```
* [vaulted](https://github.com/chiefy/vaulted)
* `npm install vaulted`
```shell
$ npm install vaulted
```
### PHP
* [vault-php-sdk](https://github.com/jippi/vault-php-sdk)
* `composer require jippi/vault-php-sdk`
```shell
$ composer require jippi/vault-php-sdk
```
* [vault-php-sdk](https://github.com/violuke/vault-php-sdk) extended from jipppi
* `composer require violuke/vault-php-sdk`
```shell
$ composer require violuke/vault-php-sdk
```
### Python
* [HVAC](https://github.com/ianunruh/hvac)
* `pip install hvac`
```shell
$ pip install hvac
```
### Rust

View File

@ -0,0 +1,357 @@
---
layout: "api"
page_title: "AWS Secret Backend - HTTP API"
sidebar_current: "docs-http-secret-aws"
description: |-
This is the API documentation for the Vault AWS secret backend.
---
# AWS Secret Backend HTTP API
This is the API documentation for the Vault AWS secret backend. For general
information about the usage and operation of the AWS backend, please see the
[Vault AWS backend documentation](/docs/secrets/aws/index.html).
This documentation assumes the AWS backend is mounted at the `/aws` path in
Vault. Since it is possible to mount secret backends at any location, please
update your API calls accordingly.
## Configure Root IAM Credentials
This endpoint configures the root IAM credentials to communicate with AWS. There
are multiple ways to pass root IAM credentials to the Vault server, specified
below with the highest precedence first. If credentials already exist, this will
overwrite them.
- Static credentials provided to the API as a payload
- Credentials in the `AWS_ACCESS_KEY`, `AWS_SECRET_KEY`, and `AWS_REGION`
environment variables **on the server**
- Querying the EC2 metadata service if the **Vault server** is on EC2 and has
querying capabilities
At present, this endpoint does not confirm that the provided AWS credentials are
valid AWS credentials with proper permissions.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/aws/config/root` | `204 (empty body)` |
### Parameters
- `access_key` `(string: <required>)` Specifies the AWS access key ID.
- `secret_key` `(string: <required>)`  Specifies the AWS secret access key.
- `region` `(string: <required>)`  Specifies the AWS region.
### Sample Payload
```json
{
"access_key": "AKIA...",
"secret_key": "2J+...",
"region": "us-east-1"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/aws/config/root
```
## Configure Lease
This endpoint configures lease settings for the AWS secret backend. It is
optional, as there are default values for `lease` and `lease_max`.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/aws/config/lease` | `204 (empty body)` |
### Parameters
- `lease` `(string: <required>)` Specifies the lease value provided as a
string duration with time suffix. "h" (hour) is the largest suffix.
- `lease_max` `(string: <required>)`  Specifies the maximum lease value
provided as a string duration with time suffix. "h" (hour) is the largest
suffix.
### Sample Payload
```json
{
"lease": "30m",
"lease_max": "12h"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/aws/config/lease
```
## Read Lease
This endpoint returns the current lease settings for the AWS secret backend.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/aws/config/lease` | `200 application/json` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/aws/config/lease
```
### Sample Response
```json
{
"data": {
"lease": "30m0s",
"lease_max": "12h0m0s"
}
}
```
## Create/Update Role
This endpoint creates or updates the role with the given `name`. If a role with
the name does not exist, it will be created. If the role exists, it will be
updated with the new attributes.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/aws/roles/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to create. This
is part of the request URL.
- `policy` `(string: <required unless arn provided>)` Specifies the IAM policy
in JSON format.
- `arn` `(string: <required unless policy provided>)`  Specifies the full ARN
reference to the desired existing policy.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/aws/roles/example-role
```
### Sample Payloads
Using an inline IAM policy:
```json
{
"policy": "{\"Version\": \"...\"}",
}
```
Using an ARN:
```json
{
"arn": "arn:aws:iam::123456789012:user/David"
}
```
## Read Role
This endpoint queries an existing role by the given name. If the role does not
exist, a 404 is returned.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/aws/roles/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to read. This
is part of the request URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/aws/roles/example-role
```
### Sample Responses
For an inline IAM policy:
```json
{
"data": {
"policy": "{\"Version\": \"...\"}"
}
}
```
For an ARN:
```json
{
"data": {
"arn": "arn:aws:iam::123456789012:user/David"
}
}
```
## List Roles
This endpoint lists all existing roles in the backend.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `LIST` | `/aws/roles` | `200 application/json` |
### Sample Request
```
$ curl
--header "X-Vault-Token: ..." \
--request LIST \
https://vault.rocks/v1/aws/roles
```
### Sample Response
```json
{
"data": {
"keys": [
"example-role"
]
}
}
```
## Delete Role
This endpoint deletes an existing role by the given name. If the role does not
exist, a 404 is returned.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELET` | `/aws/roles/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to delete. This
is part of the request URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/aws/roles/example-role
```
## Generate IAM Credentials
This endpoint generates dynamic IAM credentials based on the named role. This
role must be created before queried.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/aws/creds/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to generate
credentials againts. This is part of the request URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/aws/creds/example-role
```
### Sample Response
```json
{
"data": {
"access_key": "AKIA...",
"secret_key": "xlCs...",
"security_token": null
}
}
```
## Generate IAM with STS
This generates a dynamic IAM credential with an STS token based on the named
role.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/aws/sts/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role against which
to create this STS credential. This is part of the request URL.
- `ttl` `(string: "3600s")` Specifies the TTL for the use of the STS token.
This is specified as a string with a duration suffix.
### Sample Payload
```json
{
"ttl": "5m"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/aws/sts/example-role
```
### Sample Response
```json
{
"data": {
"access_key": "AKIA...",
"secret_key": "xlCs...",
"security_token": "429255"
}
}
```

View File

@ -0,0 +1,239 @@
---
layout: "api"
page_title: "Cassandra Secret Backend - HTTP API"
sidebar_current: "docs-http-secret-cassandra"
description: |-
This is the API documentation for the Vault Cassandra secret backend.
---
# Cassandra Secret Backend HTTP API
This is the API documentation for the Vault Cassandra secret backend. For
general information about the usage and operation of the Cassandra backend,
please see the
[Vault Cassandra backend documentation](/docs/secrets/cassandra/index.html).
This documentation assumes the Cassandra backend is mounted at the `/cassandra`
path in Vault. Since it is possible to mount secret backends at any location,
please update your API calls accordingly.
## Configure Connection
This endpoint configures the connection information used to communicate with
Cassandra.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/cassandra/config/connection` | `204 (empty body)` |
### Parameters
- `hosts` `(string: <required>)` Specifies a set of comma-delineated Cassandra
hosts to connect to.
- `username` `(string: <required>)`  Specifies the username to use for
superuser access.
- `password` `(string: <required>)`  Specifies the password corresponding to
the given username.
- `tls` `(bool: true)` Specifies whether to use TLS when connecting to
Cassandra.
- `insecure_tls` `(bool: false)`  Specifies whether to skip verification of the
server certificate when using TLS.
- `pem_bundle` `(string: "")` Specifies concatenated PEM blocks containing a
certificate and private key; a certificate, private key, and issuing CA
certificate; or just a CA certificate.
- `pem_json` `(string: "")`  Specifies JSON containing a certificate and
private key; a certificate, private key, and issuing CA certificate; or just a
CA certificate. For convenience format is the same as the output of the
`issue` command from the `pki` backend; see
[the pki documentation](/docs/secrets/pki/index.html).
- `protocol_version` `(int: 2)`  Specifies the CQL protocol version to use.
- `connect_timeout` `(string: "5s")`  Specifies the connection timeout to use.
TLS works as follows:
- If `tls` is set to true, the connection will use TLS; this happens
automatically if `pem_bundle`, `pem_json`, or `insecure_tls` is set
- If `insecure_tls` is set to true, the connection will not perform verification
of the server certificate; this also sets `tls` to true
- If only `issuing_ca` is set in `pem_json`, or the only certificate in
`pem_bundle` is a CA certificate, the given CA certificate will be used for
server certificate verification; otherwise the system CA certificates will be
used
- If `certificate` and `private_key` are set in `pem_bundle` or `pem_json`,
client auth will be turned on for the connection
`pem_bundle` should be a PEM-concatenated bundle of a private key + client
certificate, an issuing CA certificate, or both. `pem_json` should contain the
same information; for convenience, the JSON format is the same as that output by
the issue command from the PKI backend.
### Sample Payload
```json
{
"hosts": "cassandra1.local",
"username": "user",
"password": "pass"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/cassandra/config/connection
```
## Create Role
This endpoint creates or updates the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/cassandra/roles/:name` | `204 (empty body)` |
### Parameters
- `creation_cql` `(string: "")`  Specifies the CQL statements executed to
create and configure the new user. Must be a semicolon-separated string, a
base64-encoded semicolon-separated string, a serialized JSON string array, or
a base64-encoded serialized JSON string array. The '{{username}}' and
'{{password}}' values will be substituted; it is required that these
parameters are in single quotes. The default creates a non-superuser user with
no authorization grants.
- `rollback_cql` `(string: "")` Specifies the CQL statements executed to
attempt a rollback if an error is encountered during user creation. The
default is to delete the user. Must be a semicolon-separated string, a
base64-encoded semicolon-separated string, a serialized JSON string array, or
a base64-encoded serialized JSON string array. The '{{username}}' and
'{{password}}' values will be substituted; it is required that these
parameters are in single quotes.
- `lease` `(string: "")`  Specifies the lease value provided as a string
duration with time suffix. "h" hour is the largest suffix.
- `consistency` `(string: "Quorum")`  Specifies the consistency level value
provided as a string. Determines the consistency level used for operations
performed on the Cassandra database.
### Sample Payload
```json
{
"creation_cql": "CREATE USER ..."
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/cassandra/roles/my-role
```
## Read Role
This endpoint queries the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/cassandra/roles/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to read. This
is part of the request URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/cassandra/roles/my-role
```
### Sample Response
```json
{
"data": {
"creation_cql": "CREATE USER...",
"rollback_cql": "DROP USER...",
"lease": "12h",
"consistency": "Quorum"
}
}
```
## Delete Role
This endpoint deletes the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/cassandra/roles/:name` | `204 (no body)` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to delete. This
is part of the request URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/cassandra/roles/my-role
```
## Generate Credentials
This endpoint generates a new set of dynamic credentials based on the named
role.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/cassandra/creds/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to create
credentials against. This is part of the request URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/cassandra/creds/my-role
```
### Sample Response
```json
{
"data": {
"username": "vault-root-1430158508-126",
"password": "132ae3ef-5a64-7499-351e-bfe59f3a2a21"
}
}
```

View File

@ -0,0 +1,201 @@
---
layout: "api"
page_title: "Consul Secret Backend - HTTP API"
sidebar_current: "docs-http-secret-consul"
description: |-
This is the API documentation for the Vault Consul secret backend.
---
# Consul Secret Backend HTTP API
This is the API documentation for the Vault Consul secret backend. For general
information about the usage and operation of the Consul backend, please see the
[Vault Consul backend documentation](/docs/secrets/consul/index.html).
This documentation assumes the Consul backend is mounted at the `/consul` path
in Vault. Since it is possible to mount secret backends at any location, please
update your API calls accordingly.
## Configure Access
This endpoint configures the access information for Consul. This access
information is used so that Vault can communicate with Consul and generate
Consul tokens.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/consul/config/access` | `204 (empty body)` |
### Parameters
- `address` `(string: <required>)`  Specifies the address of the Consul
instance, provided as `"host:port"` like `"127.0.0.1:8500"`.
- `scheme` `(string: "http")`  Specifies the URL scheme to use.
- `token` `(string: <required>)` Specifies the Consul ACL token to use. This
must be a management type token.
### Sample Payload
```json
{
"address": "127.0.0.1:8500",
"scheme": "https",
"token": "adha..."
}
```
### Sample Request
```
$ curl \
--request POST \
--header "X-Vault-Token: ..." \
--data @payload.json \
https://vault.rocks/v1/consul/config/access
```
## Create/Update Role
This endpoint creates or updates the Consul role definition. If the role does
not exist, it will be created. If the role already exists, it will receive
updated attributes.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/consul/roles/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of an existing role against
which to create this Consul credential. This is part of the request URL.
- `lease` `(string: "")`  Specifies the lease for this role. This is provided
as a string duration with a time suffix like `"30s"` or `"1h"`. If not
provided, the default Vault lease is used.
- `policy` `(string: <required>)` Specifies the base64 encoded ACL policy. The
ACL format can be found in the [Consul ACL
documentation](https://www.consul.io/docs/internals/acl.html). This is
required unless the `token_type` is `management`.
- `token_type` `(string: "client")` - Specifies the type of token to create when
using this role. Valid values are `"client"` or `"management"`.
### Sample Payload
To create management tokens:
```json
{
"token_type": "management"
}
```
To create a client token with a custom policy:
```json
{
"policy": "abd2...=="
}
```
### Sample Request
```
$ curl \
--request POST \
--header "X-Vault-Token: ..." \
--data @payload.json \
https://vault.rocks/v1/consul/roles/example-role
```
## Read Role
This endpoint queries for information about a Consul role with the given name.
If no role exists with that name, a 404 is returned.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/consul/roles/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to query. This
is part of the request URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/consul/roles/example-role
```
### Sample Response
```json
{
"data": {
"policy": "abd2...==",
"lease": "1h0m0s",
"token_type": "client"
}
}
```
## Delete Role
This endpoint deletes a Consul role with the given name. Even if the role does
not exist, this endpoint will still return a successful response.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/consul/roles/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to delete. This
is part of the request URL.
### Sample Request
```
$ curl \
--request DELETE \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/consul/roles/example-role
```
## Generate Credential
This endpoint generates a dynamic Consul token based on the given role
definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/consul/creds/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of an existing role against
which to create this Consul credential. This is part of the request URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/consul/creds/example-role
```
### Sample Response
```json
{
"data": {
"token": "973a31ea-1ec4-c2de-0f63-623f477c2510"
}
}
```

View File

@ -0,0 +1,155 @@
---
layout: "api"
page_title: "Cubbyhole Secret Backend - HTTP API"
sidebar_current: "docs-http-secret-cubbyhole"
description: |-
This is the API documentation for the Vault Cubbyhole secret backend.
---
# Cubbyhole Secret Backend HTTP API
This is the API documentation for the Vault Cubbyhole secret backend. For
general information about the usage and operation of the Cubbyhole backend,
please see the
[Vault Cubbyhole backend documentation](/docs/secrets/cubbyhole/index.html).
This documentation assumes the Cubbyhole backend is mounted at the `/cubbyhole`
path in Vault. Since it is possible to mount secret backends at any location,
please update your API calls accordingly.
## Read Secret
This endpoint retrieves the secret at the specified location.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/cubbyhole/:path` | `200 application/json` |
### Parameters
- `path` `(string: <required>)` Specifies the path of the secret to read.
This is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/cubbyhole/my-secret
```
### Sample Response
```json
{
"auth": null,
"data": {
"foo": "bar"
},
"lease_duration": 0,
"lease_id": "",
"renewable": false
}
```
## List Secrets
This endpoint returns a list of secret entries at the specified location.
Folders are suffixed with `/`. The input must be a folder; list on a file will
not return a value. The values themselves are not accessible via this command.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `List` | `/cubbyhole/:path` | `200 application/json` |
### Parameters
- `path` `(string: <required>)` Specifies the path of the secrets to list.
This is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request LIST \
https://vault.rocks/v1/cubbyhole/my-secret
```
### Sample Response
The example below shows output for a query path of `cubbyhole/` when there are
secrets at `cubbyhole/foo` and `cubbyhole/foo/bar`; note the difference in the
two entries.
```json
{
"auth": null,
"data": {
"keys": ["foo", "foo/"]
},
"lease_duration": 2764800,
"lease_id": "",
"renewable": false
}
```
## Create/Update Secret
This endpoint stores a secret at the specified location.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/cubbyhole/:path` | `204 (empty body)` |
| `PUT` | `/cubbyhole/:path` | `204 (empty body)` |
### Parameters
- `path` `(string: <required>)` Specifies the path of the secrets to
create/update. This is specified as part of the URL.
- `:key` `(string: "")`  Specifies a key, paired with an associated value, to
be held at the given location. Multiple key/value pairs can be specified, and
all will be returned on a read operation. A key called `ttl` will trigger some
special behavior; see above for details.
### Sample Payload
```json
{
"foo": "bar",
"zip": "zap"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/cubbyhole/my-secret
```
## Delete Secret
This endpoint deletes the secret at the specified location.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/cubbyhole/:path` | `204 (empty body)` |
### Parameters
- `path` `(string: <required>)` Specifies the path of the secret to delete.
This is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/cubbyhole/my-secret
```

View File

@ -0,0 +1,159 @@
---
layout: "api"
page_title: "Generic Secret Backend - HTTP API"
sidebar_current: "docs-http-secret-generic"
description: |-
This is the API documentation for the Vault Generic secret backend.
---
# Generic Secret Backend HTTP API
This is the API documentation for the Vault Generic secret backend. For general
information about the usage and operation of the Generic backend, please see
the [Vault Generic backend documentation](/docs/secrets/generic/index.html).
This documentation assumes the Generic backend is mounted at the `/secret`
path in Vault. Since it is possible to mount secret backends at any location,
please update your API calls accordingly.
## Read Secret
This endpoint retrieves the secret at the specified location.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/secret/:path` | `200 application/json` |
### Parameters
- `path` `(string: <required>)` Specifies the path of the secret to read.
This is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/secret/my-secret
```
### Sample Response
```json
{
"auth": null,
"data": {
"foo": "bar"
},
"lease_duration": 2764800,
"lease_id": "",
"renewable": false
}
```
## List Secrets
This endpoint returns a list of key names at the specified location. Folders are
suffixed with `/`. The input must be a folder; list on a file will not return a
value. Note that no policy-based filtering is performed on keys; do not encode
sensitive information in key names. The values themselves are not accessible via
this command.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `LIST` | `/secret/:path` | `200 application/json` |
### Parameters
- `path` `(string: <required>)` Specifies the path of the secrets to list.
This is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request LIST \
https://vault.rocks/v1/secret/my-secret
```
### Sample Response
The example below shows output for a query path of `secret/` when there are
secrets at `secret/foo` and `secret/foo/bar`; note the difference in the two
entries.
```json
{
"auth": null,
"data": {
"keys": ["foo", "foo/"]
},
"lease_duration": 2764800,
"lease_id": "",
"renewable": false
}
```
## Create/Update Secret
This endpoint stores a secret at the specified location. If the value does not
yet exist, the calling token must have an ACL policy granting the `create`
capability. If the value already exists, the calling token must have an ACL
policy granting the `update` capability.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/secret/:path` | `204 (empty body)` |
| `PUT` | `/secret/:path` | `204 (empty body)` |
### Parameters
- `path` `(string: <required>)` Specifies the path of the secrets to
create/update. This is specified as part of the URL.
- `:key` `(string: "")`  Specifies a key, paired with an associated value, to
be held at the given location. Multiple key/value pairs can be specified, and
all will be returned on a read operation. A key called `ttl` will trigger some
special behavior; see above for details.
### Sample Payload
```json
{
"foo": "bar",
"zip": "zap"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/secret/my-secret
```
## Delete Secret
This endpoint deletes the secret at the specified location.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/secret/:path` | `204 (empty body)` |
### Parameters
- `path` `(string: <required>)` Specifies the path of the secret to delete.
This is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/secret/my-secret
```

View File

@ -0,0 +1,19 @@
---
layout: "api"
page_title: "HTTP API"
sidebar_current: "docs-http-secret"
description: |-
Each secret backend publishes its own set of API paths and methods. These
endpoints are documented in this section.
---
# Secret Backends
Each secret backend publishes its own set of API paths and methods. These
endpoints are documented in this section. Secret backends are mounted at a path,
but the documentation will assume the default mount points for simplicity. If
you are mounting at a different path, you should adjust your API calls
accordingly.
For the API documentation for a specific secret backend, please choose a secret
backend from the navigation.

View File

@ -0,0 +1,344 @@
---
layout: "api"
page_title: "MongoDB Secret Backend - HTTP API"
sidebar_current: "docs-http-secret-mongodb"
description: |-
This is the API documentation for the Vault MongoDB secret backend.
---
# MongoDB Secret Backend HTTP API
This is the API documentation for the Vault MongoDB secret backend. For general
information about the usage and operation of the MongoDB backend, please see
the [Vault MongoDB backend documentation](/docs/secrets/mongodb/index.html).
This documentation assumes the MongoDB backend is mounted at the `/mongodb`
path in Vault. Since it is possible to mount secret backends at any location,
please update your API calls accordingly.
## Configure Connection
This endpoint configures the standard connection string (URI) used to
communicate with MongoDB.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/mongodb/config/connection` | `200 application/json` |
### Parameters
- `url` `(string: <required>)` Specifies the MongoDB standard connection
string (URI).
- `verify_connection` `(bool: true)`  Specifies if the connection is verified
during initial configuration.
### Sample Payload
```json
{
"url": "mongodb://db1.example.net,db2.example.net:2500/?replicaSet=test"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/mongodb/config/connection
```
### Sample Response
```json
{
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": null,
"wrap_info": null,
"warnings": [
"Read access to this endpoint should be controlled via ACLs as it will return the connection URI as it is, including passwords, if any."
],
"auth": null
}
```
## Read Connection
This endpoint queries the connection configuration. Access to this endpoint
should be controlled via ACLs as it will return the connection URI as it is,
including passwords, if any.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/mongodb/config/connection` | `200 application/json` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/mongodb/config/connection
```
### Sample Response
```json
{
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"uri": "mongodb://admin:Password!@mongodb.acme.com:27017/admin?ssl=true"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
```
## Configure Lease
This endpoint configures the default lease TTL settings for credentials
generated by the mongodb backend.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/mongodb/config/lease` | `204 (empty body)` |
### Parameters
- `lease` `(string: <required>)`  Specifies the lease value provided as a
string duration with time suffix. "h" (hour) is the largest suffix.
- `lease_max` `(string: <required>)`  Specifies the maximum lease value
provided as a string duration with time suffix. "h" (hour) is the largest
suffix.
### Sample Payload
```json
{
"lease": "12h",
"lease_max": "24h"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/mongodb/config/lease
```
## Read Lease
This endpoint queries the lease configuration.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/mongodb/config/lease` | `200 application/json` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/mongodb/config/lease
```
### Sample Response
```json
{
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"max_ttl": 60,
"ttl": 60
},
"wrap_info": null,
"warnings": null,
"auth": null
}
```
## Create Role
This endpoint creates or updates a role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/mongodb/roles/:name` | `204 (empty body)` |
### Parameters
- `db` `(string: <required>)` Specifies the name of the database users should
be created in for this role.
- `roles` `(string: "")`  Specifies the MongoDB roles to assign to the users
generated for this role.
### Sample Payload
```json
{
"db": "my-db",
"roles": "[\"readWrite\",{\"db\":\"bar\",\"role\":\"read\"}]"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/mongodb/roles/my-role
```
## Read Role
This endpoint queries the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/mongodb/roles/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to read. This
is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/mongodb/roles/my-role
```
### Sample Response
```json
{
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"db": "foo",
"roles": "[\"readWrite\",{\"db\":\"bar\",\"role\":\"read\"}]"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
```
## List Roles
This endpoint returns a list of available roles. Only the role names are
returned, not any values.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `LIST` | `/mongodb/roles` | `200 application/json` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request LIST \
https://vault.rocks/v1/mongodb/roles
```
### Sample Response
```json
{
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"keys": [
"dev",
"prod"
]
},
"wrap_info": null,
"warnings": null,
"auth": null
}
```
## Delete Role
This endpoint deletes the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/mongodb/roles/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to delete. This
is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/mongodb/roles/my-role
```
## Generate Credentials
This endpoint generates a new set of dynamic credentials based on the named
role.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/mongodb/creds/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to create
credentials against. This is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/mongodb/creds/my-role
```
### Sample Response
```json
{
"lease_id": "mongodb/creds/readonly/e64e79d8-9f56-e379-a7c5-373f9b4ee3d8",
"renewable": true,
"lease_duration": 3600,
"data": {
"db": "foo",
"password": "de0f7b50-d700-54e5-4e81-5c3724283999",
"username": "vault-token-b32098cb-7ff2-dcf5-83cd-d5887cedf81b"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
```

View File

@ -0,0 +1,244 @@
---
layout: "api"
page_title: "MSSQL Secret Backend - HTTP API"
sidebar_current: "docs-http-secret-mssql"
description: |-
This is the API documentation for the Vault MSSQL secret backend.
---
# MSSQL Secret Backend HTTP API
This is the API documentation for the Vault MSSQL secret backend. For general
information about the usage and operation of the MSSQL backend, please see
the [Vault MSSQL backend documentation](/docs/secrets/mssql/index.html).
This documentation assumes the MSSQL backend is mounted at the `/mssql`
path in Vault. Since it is possible to mount secret backends at any location,
please update your API calls accordingly.
## Configure Connection
This endpoint configures the connection DSN used to communicate with Microsoft
SQL Server.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/mssql/config/connection` | `204 (empty body)` |
### Parameters
- `connection_string` `(string: <required>)`  Specifies the MSSQL DSN.
- `max_open_connections` `(int: 2)`  Specifies the maximum number of open
connections to the database.
- `max_idle_connections` `(int: 0)`  Specifies the maximum number of idle
connections to the database. A zero uses the value of `max_open_connections`
and a negative value disables idle connections. If larger than
`max_open_connections` it will be reduced to be equal.
### Sample Payload
```json
{
"connection_string": "Server=myServerAddress;Database=myDataBase;User Id=myUsername; Password=myPassword;"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/mssql/config/connection
```
## Configure Lease
This endpoint configures the lease settings for generated credentials.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/mysql/config/lease` | `204 (empty body)` |
### Parameters
- `lease` `(string: <required>)`  Specifies the lease value provided as a
string duration with time suffix. "h" (hour) is the largest suffix.
- `lease_max` `(string: <required>)`  Specifies the maximum lease value
provided as a string duration with time suffix. "h" (hour) is the largest
suffix.
### Sample Payload
```json
{
"lease": "12h",
"lease_max": "24h"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/mssql/config/lease
```
## Create Role
This endpoint creates or updates the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/mssql/roles/:name` | `204 (empty body)` |
### Parameters
- `sql` `(string: <required>)`  Specifies the SQL statements executed to create
and configure the role. The '{{name}}' and '{{password}}' values will be
substituted. Must be a semicolon-separated string, a base64-encoded
semicolon-separated string, a serialized JSON string array, or a
base64-encoded serialized JSON string array.
### Sample Payload
```json
{
"sql": "CREATE LOGIN ..."
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/mssql/roles/my-role
```
## Read Role
This endpoint queries the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/mssql/roles/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to read. This
is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/mssql/roles/my-role
```
### Sample Response
```json
{
"data": {
"sql": "CREATE LOGIN..."
}
}
```
## List Roles
This endpoint returns a list of available roles. Only the role names are
returned, not any values.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `LIST` | `/mssql/roles` | `200 application/json` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request LIST \
https://vault.rocks/v1/mssql/roles
```
### Sample Response
```json
{
"auth": null,
"data": {
"keys": ["dev", "prod"]
},
"lease_duration": 2764800,
"lease_id": "",
"renewable": false
}
```
## Delete Role
This endpoint deletes the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/mssql/roles/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to delete. This
is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/mssql/roles/my-role
```
## Generate Credentials
This endpoint generates a new set of dynamic credentials based on the named
role.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/mssql/creds/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to create
credentials against. This is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/mssql/creds/my-role
```
### Sample Response
```json
{
"data": {
"username": "root-a147d529-e7d6-4a16-8930-4c3e72170b19",
"password": "ee202d0d-e4fd-4410-8d14-2a78c5c8cb76"
}
}
```

View File

@ -0,0 +1,265 @@
---
layout: "api"
page_title: "MySQL Secret Backend - HTTP API"
sidebar_current: "docs-http-secret-mysql"
description: |-
This is the API documentation for the Vault MySQL secret backend.
---
# MySQL Secret Backend HTTP API
This is the API documentation for the Vault MySQL secret backend. For general
information about the usage and operation of the MySQL backend, please see
the [Vault MySQL backend documentation](/docs/secrets/mysql/index.html).
This documentation assumes the MySQL backend is mounted at the `/mysql`
path in Vault. Since it is possible to mount secret backends at any location,
please update your API calls accordingly.
## Configure Connection
This endpoint configures the connection DSN used to communicate with MySQL.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/mysql/config/connection` | `204 (empty body)` |
### Parameters
- `connection_url` `(string: <required>)`  Specifies the MySQL DSN.
- `max_open_connections` `(int: 2)`  Specifies the maximum number of open
connections to the database.
- `max_idle_connections` `(int: 0)`  Specifies the maximum number of idle
connections to the database. A zero uses the value of `max_open_connections`
and a negative value disables idle connections. If larger than
`max_open_connections` it will be reduced to be equal.
- `verify_connection` `(bool: true)`  Specifies if the connection is verified
during initial configuration.
### Sample Payload
```json
{
"connection_url": "mysql:host=localhost;dbname=testdb"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/mysql/config/connection
```
## Configure Lease
This endpoint configures the lease settings for generated credentials. If not
configured, leases default to 1 hour. This is a root protected endpoint.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/mysql/config/lease` | `204 (empty body)` |
### Parameters
- `lease` `(string: <required>)`  Specifies the lease value provided as a
string duration with time suffix. "h" (hour) is the largest suffix.
- `lease_max` `(string: <required>)`  Specifies the maximum lease value
provided as a string duration with time suffix. "h" (hour) is the largest
suffix.
### Sample Payload
```json
{
"lease": "12h",
"lease_max": "24h"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/mysql/config/lease
```
## Create Role
This endpoint creates or updates the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/mysql/roles/:name` | `204 (empty body)` |
### Parameters
- `sql` `(string: <required>)`  Specifies the SQL statements executed to create
and configure a user. Must be a semicolon-separated string, a base64-encoded
semicolon-separated string, a serialized JSON string array, or a
base64-encoded serialized JSON string array. The '{{name}}' and
'{{password}}' values will be substituted.
- `revocation_sql` `(string: "")` Specifies the SQL statements executed to
revoke a user. Must be a semicolon-separated string, a base64-encoded
semicolon-separated string, a serialized JSON string array, or a
base64-encoded serialized JSON string array. The '{{name}}' value will be
substituted.
- `rolename_length` `(int: 4)`  Specifies how many characters from the role
name will be used to form the mysql username interpolated into the '{{name}}'
field of the sql parameter.
- `displayname_length` `(int: 4)`  Specifies how many characters from the token
display name will be used to form the mysql username interpolated into the
'{{name}}' field of the sql parameter.
- `username_length` `(int: 16)`  Specifies the maximum total length in
characters of the mysql username interpolated into the '{{name}}' field of the
sql parameter.
### Sample Payload
```json
{
"sql": "CREATE USER ..."
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/mysql/roles/my-role
```
## Read Role
This endpoint queries the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/mysql/roles/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to read. This
is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/mysql/roles/my-role
```
### Sample Response
```json
{
"data": {
"sql": "CREATE USER..."
}
}
```
## List Roles
This endpoint returns a list of available roles. Only the role names are
returned, not any values.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `LIST` | `/mysql/roles` | `200 application/json` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request LIST \
https://vault.rocks/v1/mysql/roles
```
### Sample Response
```json
{
"auth": null,
"data": {
"keys": ["dev", "prod"]
},
"lease_duration": 2764800,
"lease_id": "",
"renewable": false
}
```
## Delete Role
This endpoint deletes the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/mysql/roles/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to delete. This
is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/mysql/roles/my-role
```
## Generate Credentials
This endpoint generates a new set of dynamic credentials based on the named
role.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/mysql/creds/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to create
credentials against. This is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/mysql/creds/my-role
```
### Sample Response
```json
{
"data": {
"username": "user-role-aefa63",
"password": "132ae3ef-5a64-7499-351e-bfe59f3a2a21"
}
}
```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,259 @@
---
layout: "api"
page_title: "PostgreSQL Secret Backend - HTTP API"
sidebar_current: "docs-http-secret-postgresql"
description: |-
This is the API documentation for the Vault PostgreSQL secret backend.
---
# PostgreSQL Secret Backend HTTP API
This is the API documentation for the Vault PostgreSQL secret backend. For
general information about the usage and operation of the PostgreSQL backend,
please see the
[Vault PostgreSQL backend documentation](/docs/secrets/postgresql/index.html).
This documentation assumes the PostgreSQL backend is mounted at the
`/postgresql` path in Vault. Since it is possible to mount secret backends at
any location, please update your API calls accordingly.
## Configure Connection
This endpoint configures the connection string used to communicate with
PostgreSQL.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/postgresql/config/connection` | `204 (empty body)` |
### Parameters
- `connection_url` `(string: <required>)`  Specifies the PostgreSQL connection
URL or PG-style string, for example `"user=foo host=bar"`.
- `max_open_connections` `(int: 2)`  Specifies the maximum number of open
connections to the database. A negative value means unlimited.
- `max_idle_connections` `(int: 0)`  Specifies the maximum number of idle
connections to the database. A zero uses the value of `max_open_connections`
and a negative value disables idle connections. If this is larger than
`max_open_connections` it will be reduced to be equal.
- `verify_connection` `(bool: true)`  Specifies if the connection is verified
during initial configuration.
### Sample Payload
```json
{
"connection_url": "postgresql://user:pass@localhost/my-db"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/postgresql/config/connection
```
## Configure Lease
This configures the lease settings for generated credentials. If not configured,
leases default to 1 hour. This is a root protected endpoint.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/postgresql/config/lease` | `204 (empty body)` |
### Parameters
- `lease` `(string: <required>)`  Specifies the lease value provided as a
string duration with time suffix. "h" (hour) is the largest suffix.
- `lease_max` `(string: <required>)`  Specifies the maximum lease value
provided as a string duration with time suffix. "h" (hour) is the largest
suffix.
### Sample Payload
```json
{
"lease": "12h",
"lease_max": "24h"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/postgresql/config/lease
```
## Create Role
This endpoint creates or updates a role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/postgresql/roles/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to create. This
is specified as part of the URL.
- `sql` `(string: <required>)`  Specifies the SQL statements executed to create
and configure the role. Must be a semicolon-separated string, a base64-encoded
semicolon-separated string, a serialized JSON string array, or a
base64-encoded serialized JSON string array. The '{{name}}', '{{password}}'
and '{{expiration}}' values will be substituted.
- `revocation_sql` `(string: "")`  Specifies the SQL statements to be executed
to revoke a user. Must be a semicolon-separated string, a base64-encoded
semicolon-separated string, a serialized JSON string array, or a
base64-encoded serialized JSON string array. The '{{name}}' value will be
substituted.
### Sample Payload
```json
{
"sql": "CREATE USER WITH ROLE {{name}}"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/postgresql/roles/my-role
```
## Read Role
This endpoint queries the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/postgresql/roles/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to read. This
is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/postgresql/roles/my-role
```
### Sample Response
```json
{
"data": {
"sql": "CREATE USER..."
}
}
```
## List Roles
This endpoint returns a list of available roles. Only the role names are
returned, not any values.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `LIST` | `/postgresql/roles` | `200 application/json` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request LIST \
https://vault.rocks/v1/postgresql/roles
```
### Sample Response
```json
{
"auth": null,
"data": {
"keys": ["dev", "prod"]
},
"lease_duration": 2764800,
"lease_id": "",
"renewable": false
}
```
## Delete Role
This endpoint deletes the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/postgresql/roles/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to delete. This
is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/postgresql/roles/my-role
```
## Generate Credentials
This endpoint generates a new set of dynamic credentials based on the named
role.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/postgresql/creds/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to create
credentials against. This is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/postgresql/creds/my-role
```
### Sample Response
```json
{
"data": {
"username": "root-1430158508-126",
"password": "132ae3ef-5a64-7499-351e-bfe59f3a2a21"
}
}
```

View File

@ -0,0 +1,218 @@
---
layout: "api"
page_title: "RabbitMQ Secret Backend - HTTP API"
sidebar_current: "docs-http-secret-rabbitmq"
description: |-
This is the API documentation for the Vault RabbitMQ secret backend.
---
# RabbitMQ Secret Backend HTTP API
This is the API documentation for the Vault RabbitMQ secret backend. For general
information about the usage and operation of the RabbitMQ backend, please see
the [Vault RabbitMQ backend documentation](/docs/secrets/rabbitmq/index.html).
This documentation assumes the RabbitMQ backend is mounted at the `/rabbitmq`
path in Vault. Since it is possible to mount secret backends at any location,
please update your API calls accordingly.
## Configure Connection
This endpoint configures the connection string used to communicate with
RabbitMQ.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/rabbitmq/config/connection` | `204 (empty body)` |
### Parameters
- `connection_uri` `(string: <required>)`  Specifies the RabbitMQ connection
URI.
- `username` `(string: <required>)` Specifies the RabbitMQ management
administrator username.
- `password` `(string: <required>)`  Specifies the RabbitMQ management
administrator password.
- `verify_connection` `(bool: true)`  Specifies whether to verify connection
URI, username, and password.
### Sample Payload
```json
{
"connection_uri": "https://...",
"username": "user",
"password": "password"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/rabbitmq/config/connection
```
## Configure Lease
This endpoint configures the lease settings for generated credentials. This is
endpoint requires sudo privileges.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/rabbitmq/config/lease` | `204 (empty body)` |
### Parameters
- `ttl` `(int: 0)`  Specifies the lease ttl provided in seconds.
- `max_ttl` `(int: 0)` Specifies the maximum ttl provided in seconds.
### Sample Payload
```json
{
"ttl": 1800,
"max_ttl": 3600
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/rabbitmq/config/lease
```
## Create Role
This endpoint creates or updates the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/rabbitmq/roles/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to create. This
is specified as part of the URL.
- `tags` `(string: "")`  Specifies a comma-separated RabbitMQ management tags.
- `vhost` `(string: "")`  Specifies a map of virtual hosts to
permissions.
### Sample Payload
```json
{
"tags": "tag1,tag2",
"vhost": "{\"/\": {\"configure\":\".*\", \"write\":\".*\", \"read\": \".*\"}}"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/rabbitmq/roles/my-role
```
## Read Role
This endpoint queries the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/rabbitmq/roles/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to read. This
is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/rabbitmq/roles/my-role
```
### Sample Response
```json
{
"data": {
"tags": "",
"vhost": "{\"/\": {\"configure\":\".*\", \"write\":\".*\", \"read\": \".*\"}}"
}
}
```
## Delete Role
This endpoint deletes the role definition.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/rabbitmq/roles/:namer` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to delete. This
is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/rabbitmq/roles/my-role
```
## Generate Credentials
This endpoint generates a new set of dynamic credentials based on the named
role.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/rabbitmq/creds/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the role to create
credentials against. This is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/rabbitmq/creds/my-role
```
### Sample Response
```json
{
"data": {
"username": "root-4b95bf47-281d-dcb5-8a60-9594f8056092",
"password": "e1b6c159-ca63-4c6a-3886-6639eae06c30"
}
}
```

View File

@ -0,0 +1,749 @@
---
layout: "api"
page_title: "SSH Secret Backend - HTTP API"
sidebar_current: "docs-http-secret-ssh"
description: |-
This is the API documentation for the Vault SSH secret backend.
---
# SSH Secret Backend HTTP API
This is the API documentation for the Vault SSH secret backend. For general
information about the usage and operation of the SSH backend, please see the
[Vault SSH backend documentation](/docs/secrets/ssh/index.html).
This documentation assumes the SSH backend is mounted at the `/ssh` path in
Vault. Since it is possible to mount secret backends at any location, please
update your API calls accordingly.
## Create/Update Key
This endpoint creates or updates a named key.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/ssh/keys/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the key to create. This
is part of the request URL.
- `key` `(string: <required>)` Specifies an SSH private key with appropriate
privileges on remote hosts.
### Sample Payload
```json
{
"key": "..."
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/ssh/keys/my-key
```
## Delete Key
This endpoint deletes a named key.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/ssh/keys/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the key to delete. This
is part of the request URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/ssh/keys/my-key
```
## Create Role
This endpoint creates or updates a named role.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/ssh/roles/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to create. This
is part of the request URL.
- `key` `(string: "")`  Specifies the name of the registered key in Vault.
Before creating the role, use the `keys/` endpoint to create a named key. This
is required for "Dynamic Key" type.
- `admin_user` `(string: "")`  Specifies the admin user at remote host. The
shared key being registered should be for this user and should have root or
sudo privileges. Every time a dynamic credential is generated for a client,
Vault uses this admin username to login to remote host and install the
generated credential. This is required for Dynamic Key type.
- `default_user` `(string: "")`  Specifies the default username for which a
credential will be generated. When the endpoint `creds/` is used without a
username, this value will be used as default username. Its recommended to
create individual roles for each username to ensure absolute isolation between
usernames. This is required for Dynamic Key type and OTP type.
For the CA type, if you wish this to be a valid principal, it must also be
in `allowed_users`.
- `cidr_list` `(string: "")`  Specifies a comma separated list of CIDR blocks
for which the role is applicable for.CIDR blocks can belong to more than one
role.
- `exclude_cidr_list` `(string: "")`  Specifies a comma-separated list of CIDR
blocks. IP addresses belonging to these blocks are not accepted by the role.
This is particularly useful when big CIDR blocks are being used by the role
and certain parts need to be kept out.
- `port` `(int: 22)`  Specifies the port number for SSH connection. Port number
does not play any role in OTP generation. For the `otp` backend type, this is
just a way to inform the client about the port number to use. The port number
will be returned to the client by Vault along with the OTP.
- `key_type` `(string: <required>)`  Specifies the type of credentials
generated by this role. This can be either `otp`, `dynamic` or `ca`.
- `key_bits` `(int: 1024)`  Specifies the length of the RSA dynamic key in
bits. This can be either 1024 or 2048.
- `install_script` `(string: "")`  Specifies the script used to install and
uninstall public keys in the target machine. Defaults to the built-in script.
- `allowed_users` `(string: "")`  If this option is not specified, client can
request for a credential for any valid user at the remote host, including the
admin user. If only certain usernames are to be allowed, then this list
enforces it. If this field is set, then credentials can only be created for
`default_user` and usernames present in this list. Setting this option will
enable all the users with access this role to fetch credentials for all other
usernames in this list. Use with caution.
- `allowed_domains` `(string: "")`  If this option is not specified, client can
request for a signed certificate for any valid host. If only certain domains
are allowed, then this list enforces it. If this option is explicitly set to
`"*"`, then credentials can be created for any domain.
- `key_option_specs` `(string: "")`  Specifies a aomma separated option
specification which will be prefixed to RSA keys in the remote host's
authorized_keys file. N.B.: Vault does not check this string for validity.
- `ttl` `(string: "")`  Specifies the Time To Live value provided as a string
duration with time suffix. Hour is the largest suffix. If not set, uses the
system default value or the value of `max_ttl`, whichever is shorter.
- `max_ttl` `(string: "")`  Specifies the maximum Time To Live provided as a
string duration with time suffix. Hour is the largest suffix. If not set,
defaults to the system maximum lease TTL.
- `allowed_critical_options` `(string: "")`  Specifies a comma-separated list
of critical options that certificates can have when signed. To allow any
critical options, set this to an empty string. Will default to allowing any
critical options.
- `allowed_extensions` `(string: "")` Specifies a comma-separated list of
extensions that certificates can have when signed. To allow any critical
options, set this to an empty string. Will default to allowing any extensions.
- `default_critical_options` `(map<string|string>: "")`  Specifies a map of
critical options certificates should have if none are provided when signing.
This field takes in key value pairs in JSON format. Note that these are not
restricted by `allowed_critical_options`. Defaults to none.
- `default_extensions` `(map<string|string>: "")`  Specifies a map of
extensions certificates should have if none are provided when signing. This
field takes in key value pairs in JSON format. Note that these are not
restricted by `allowed_extensions`. Defaults to none.
- `allow_user_certificates` `(bool: false)`  Specifies if certificates are
allowed to be signed for use as a 'user'.
- `allow_host_certificates` `(bool: false)`  Specifies if certificates are
allowed to be signed for use as a 'host'.
- `allow_bare_domains` `(bool: false)`  Specifies if host certificates that are
requested are allowed to use the base domains listed in "allowed_users", e.g.
"example.com". This is a separate option as in some cases this can be
considered a security threat.
- `allow_subdomains` `(bool: false)` Specifies if host certificates that are
requested are allowed to use subdomains of those listed in "allowed_users".
- `allow_user_key_ids` `(bool: false)` Specifies if users can override the key
ID for a signed certificate with the "key_id" field. When false, the key ID
will always be the token display name. The key ID is logged by the SSH server
and can be useful for auditing.
### Sample Payload
```json
{
"key_type": "otp"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/ssh/roles/my-role
```
## Read Role
This endpoint queries a named role.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/ssh/roles/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to read. This
is part of the request URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/ssh/roles/my-role
```
### Sample Response
For a dynamic key role:
```json
{
"admin_user": "username",
"cidr_list": "x.x.x.x/y",
"default_user": "username",
"key": "<key name>",
"key_type": "dynamic",
"port": 22
}
```
For an OTP role:
```json
{
"cidr_list": "x.x.x.x/y",
"default_user": "username",
"key_type": "otp",
"port": 22
}
```
For a CA role:
```json
{
"allow_bare_domains": false,
"allow_host_certificates": true,
"allow_subdomains": false,
"allow_user_key_ids": false,
"allow_user_certificates": true,
"allowed_critical_options": "",
"allowed_extensions": "",
"default_critical_options": {},
"default_extensions": {},
"max_ttl": "768h",
"ttl": "4h"
}
```
## List Roles
This endpoint returns a list of available roles. Only the role names are
returned, not any values.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `LIST` | `/ssh/roles` | `200 application/json` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request LIST \
https://vault.rocks/v1/ssh/roles
```
### Sample Response
```json
{
"auth": null,
"data": {
"keys": ["dev", "prod"]
},
"lease_duration": 2764800,
"lease_id": "",
"renewable": false
}
```
## Delete Role
This endpoint deletes a named role.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/ssh/roles/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to delete. This
is part of the request URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
--data @payload.json \
https://vault.rocks/v1/ssh/roles/my-role
```
## List Zero-Address Roles
This endpoint returns the list of configured zero-address roles.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/ssh/config/zeroaddress` | `200 application/json` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/ssh/config/zeroaddress
```
### Sample Response
```json
{
"lease_id":"",
"renewable":false,
"lease_duration":0,
"data":{
"roles":[
"otp_key_role"
]
},
"warnings":null,
"auth":null
}
```
## Configure Zero-Address Roles
This endpoint configures zero-address roles.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/ssh/config/zeroaddress` | `204 (empty body)` |
### Parameters
- `roles` `(string: <required>)`  Specifies a string containing comma separated
list of role names which allows credentials to be requested for any IP
address. CIDR blocks previously registered under these roles will be ignored.
### Sample Payload
```json
{
"roles": ["otp_key_role"]
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/ssh/config/zeroaddress
```
## Delete Zero-Address Role
This endpoint deletes the zero-address roles configuration.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/ssh/config/zeroaddress` | `204 (empty body)` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/ssh/config/zeroaddress
```
## Generate SSH Credentials
This endpoint creates credentials for a specific username and IP with the
parameters defined in the given role.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/ssh/creds/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to create
credentials against. This is part of the request URL.
- `username` `(string: "")`  Specifies the username on the remote host.
- `ip` `(string: <required>)` Specifies the IP of the remote host.
### Sample Payload
```json
{
"ip": "1.2.3.4"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/ssh/creds/my-role
```
### Sample Response
For a dynamic key role:
```json
{
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"admin_user": "rajanadar",
"allowed_users": "",
"cidr_list": "x.x.x.x/y",
"default_user": "rajanadar",
"exclude_cidr_list": "x.x.x.x/y",
"install_script": "pretty_large_script",
"key": "5d9ee6a1-c787-47a9-9738-da243f4f69bf",
"key_bits": 1024,
"key_option_specs": "",
"key_type": "dynamic",
"port": 22
},
"warnings": null,
"auth": null
}
```
For an OTP role:
```json
{
"lease_id": "sshs/creds/c3c2e60c-5a48-415a-9d5a-a41e0e6cdec5/3ee6ad28-383f-d482-2427-70498eba4d96",
"renewable": false,
"lease_duration": 2764800,
"data": {
"ip": "127.0.0.1",
"key": "6d6411fd-f622-ea0a-7e2c-989a745cbbb2",
"key_type": "otp",
"port": 22,
"username": "rajanadar"
},
"warnings": null,
"auth": null
}
```
## List Roles by IP
This endpoint lists all of the roles with which the given IP is associated.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/ssh/lookup` | `200 application/json` |
### Parameters
- `ip` `(string: <required>)`  Specifies the IP of the remote host.
### Sample Payload
```json
{
"ip": "1.2.3.4"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/ssh/lookup
```
### Sample Response
An array of roles as a secret structure.
```json
{
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"roles": [
"fe6f61b7-7e4a-46a6-b2c8-0d530b8513df",
"6d6411fd-f622-ea0a-7e2c-989a745cbbb2"
]
},
"warnings": null,
"auth": null
}
```
## Verify SSH OTP
This endpoint verifies if the given OTP is valid. This is an unauthenticated
endpoint.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/ssh/verify` | `200 application/json` |
## Parameters
- `otp` `(string: <required>)`  Specifies the One-Time-Key that needs to be
validated.
### Sample Payload
```json
{
"otp": "bad2b3-..."
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/ssh/verify
### Sample Response
```json
{
"lease_id":"",
"renewable":false,
"lease_duration":0,
"data": {
"ip":"127.0.0.1",
"username":"rajanadar"
},
"warnings":null,
"auth":null
}
```
## Submit CA Information
This endpoint allows submitting the CA information for the backend via an SSH
key pair. _If you have already set a certificate and key, they will be
overridden._
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/ssh/config/ca` | `200/204 application/json` |
### Parameters
- `private_key` `(string: "")`  Specifies the private key part the SSH CA key
pair; required if `generate_signing_key` is false.
- `public_key` `(string: "")` Specifies the public key part of the SSH CA key
pair; required if `generate_signing_key` is false.
- `generate_signing_key` `(bool: false)`  Specifies if Vault should generate
the signing key pair internally. The generated public key will be returned so
you can add it to your configuration.
### Sample Payload
```json
{
"generate_signing_key": true
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/ssh/config/ca
```
### Sample Response
This will return a `204` response if `generate_signing_key` was unset or false.
This will return a `200` response if `generate_signing_key` was true:
```json
{
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"public_key": "ssh-rsa AAAAHHNzaC1y...\n"
},
"warnings": null
}
```
## Read Public Key
This endpoint reads the configured/generated public key.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/ssh/config/ca` | `200 application/json` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/ssh/config/ca
```
### Sample Response
```json
{
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"public_key": "ssh-rsa AAAAHHNzaC1y...\n"
},
"warnings": null
}
```
## Sigh SSH Key
This endpoint signs an SSH public key based on the supplied parameters, subject
to the restrictions contained in the role named in the endpoint.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/ssh/sign/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)`  Specifies the name of the role to sign. This
is part of the request URL.
- `public_key` `(string: <required>)` Specifies the SSH public key that should
be signed.
- `ttl` `(string: "")`  Specifies the Requested Time To Live. Cannot be greater
than the role's `max_ttl` value. If not provided, the role's `ttl` value will
be used. Note that the role values default to system values if not explicitly
set.
- `valid_principals` `(string: "")`  Specifies valid principals, either
usernames or hostnames, that the certificate should be signed for.
- `cert_type` `(string: "user")`  Specifies the type of certificate to be
created; either "user" or "host".
- `key_id` `(string: "")`  Specifies the key id that the created certificate
should have. If not specified, the display name of the token will be used.
- `critical_options` `(map<string|string>: "")`  Specifies a map of the
critical options that the certificate should be signed for. Defaults to none.
- `extension` `(map<string|string>: "")`  Specifies a map of the extensions
that the certificate should be signed for. Defaults to none.
### Sample Payload
```json
{
"public_key": "ssh-rsa ..."
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/ssh/sign/my-key
```
### Sample Response
```json
{
"lease_id": "ssh/sign/example/097bf207-96dd-0041-0e83-b23bd1923993",
"renewable": false,
"lease_duration": 21600,
"data": {
"serial_number": "f65ed2fd21443d5c",
"signed_key": "ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1y...\n"
},
"auth": null
}
```

View File

@ -0,0 +1,859 @@
---
layout: "api"
page_title: "Transit Secret Backend - HTTP API"
sidebar_current: "docs-http-secret-transit"
description: |-
This is the API documentation for the Vault Transit secret backend.
---
# Transit Secret Backend HTTP API
This is the API documentation for the Vault Transit secret backend. For general
information about the usage and operation of the Transit backend, please see the
[Vault Transit backend documentation](/docs/secrets/transit/index.html).
This documentation assumes the Transit backend is mounted at the `/transit`
path in Vault. Since it is possible to mount secret backends at any location,
please update your API calls accordingly.
## Create Key
This endpoint creates a new named encryption key of the specified type. The
values set here cannot be changed after key creation.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/transit/keys/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the encryption key to
create. This is specified as part of the URL.
- `convergent_encryption` `(bool: false)` If enabled, the key will support
convergent encryption, where the same plaintext creates the same ciphertext.
This requires _derived_ to be set to `true`. When enabled, each
encryption(/decryption/rewrap/datakey) operation will derive a `nonce` value
rather than randomly generate it. Note that while this is useful for
particular situations, all nonce values used with a given context value **must
be unique** or it will compromise the security of your key, and the key space
for nonces is 96 bit -- not as large as the AES key itself.
- `derived` `(bool: false)`  Specifies if key derivation kist be used. If
enabled, all encrypt/decrypt requests to this named key must provide a context
which is used for key derivation.
- `exportable` `(bool: false)`  Specifies if the raw key is exportable.
- `type` `(string: "aes256-gcm96")`  Specifies the type of key to create. The
currently-supported types are:
- `aes256-gcm96` AES-256 wrapped with GCM using a 12-byte nonce size (symmetric)
- `ecdsa-p256` ECDSA using the P-256 elliptic curve (asymmetric)
### Sample Payload
```json
{
"type": "ecdsa-p256",
"derived": true
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/transit/keys/my-key
```
## Read Key
This endpoint returns information about a named encryption key. The `keys`
object shows the creation time of each key version; the values are not the keys
themselves. Depending on the type of key, different information may be returned,
e.g. an asymmetric key will return its public key in a standard format for the
type.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/transit/keys/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the encryption key to
read. This is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/transit/keys/my-key
```
### Sample Response
```json
{
"data": {
"type": "aes256-gcm96",
"deletion_allowed": false,
"derived": false,
"exportable": false,
"keys": {
"1": 1442851412
},
"min_decryption_version": 0,
"name": "foo",
"supports_encryption": true,
"supports_decryption": true,
"supports_derivation": true,
"supports_signing": false
}
}
```
## List Keys
This endpoint returns a list of keys. Only the key names are returned (not the
actual keys themselves).
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `LIST` | `/transit/keys` | `200 application/json` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request LIST \
https://vault.rocks/v1/transit/keys
```
### Sample Response
```json
{
"data": {
"keys": ["foo", "bar"]
},
"lease_duration": 0,
"lease_id": "",
"renewable": false
}
```
## Delete Key
This endpoint deletes a named encryption key. It will no longer be possible to
decrypt any data encrypted with the named key. Because this is a potentially
catastrophic operation, the `deletion_allowed` tunable must be set in the key's
`/config` endpoint.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/transit/keys/:name` | `204 (empty body)` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the encryption key to
delete. This is specified as part of the URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/transit/keys/my-key
```
#### Update Key Configuration
This endpoint allows tuning configuration values for a given key. (These values
are returned during a read operation on the named key.)
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/transit/keys/:name/config` | `204 (empty body)` |
### Parameters
- `min_decryption_version` `(int: 0)`  Specifies the minimum version of
ciphertext allowed to be decrypted. Adjusting this as part of a key rotation
policy can prevent old copies of ciphertext from being decrypted, should they
fall into the wrong hands. For signatures, this value controls the minimum
version of signature that can be verified against. For HMACs, this controls
the minimum version of a key allowed to be used as the key for the HMAC
function.
- `deletion_allowed` `(bool: false)`- Specifies if the key is allowed to be
deleted.
### Sample Payload
```json
{
"deletion_allowed": true
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/transit/keys/my-key/config
```
## Rotate Key
This endpoint rotates the version of the named key. After rotation, new
plaintext requests will be encrypted with the new version of the key. To upgrade
ciphertext to be encrypted with the latest version of the key, use the `rewrap`
endpoint. This is only supported with keys that support encryption and
decryption operations.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/transit/keys/:name/rotate` | `204 (empty body)` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
https://vault.rocks/v1/transit/keys/my-key/rotate
```
## Read Key
This endpoint returns the named key. The `keys` object shows the value of the
key for each version. If `version` is specified, the specific version will be
returned. If `latest` is provided as the version, the current key will be
provided. Depending on the type of key, different information may be returned.
The key must be exportable to support this operation and the version must still
be valid.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/transit/export/:key_type/:name(/:version)` | `200 application/json` |
### Parameters
- `key_type` `(string: <required>)`  Specifies the type of the key to export.
This is specified as part of the URL. Valid values are:
- `encryption-key`
- `signing-key`
- `hmac-key`
- `name` `(string: <required>)` Specifies the name of the key to read
information about. This is specified as part of the URL.
- `version` `(int: "")`  Specifies the version of the key to read. If omitted,
all versions of the key will be returned. This is specified as part of the
URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/transit/export/encryption-key/my-key/1
```
### Sample Response
```json
{
"data": {
"name": "foo",
"keys": {
"1": "eyXYGHbTmugUJn6EtYD/yVEoF6pCxm4R/cMEutUm3MY=",
"2": "Euzymqx6iXjS3/NuGKDCiM2Ev6wdhnU+rBiKnJ7YpHE="
}
}
}
```
## Encrypt Data
This endpoint encrypts the provided plaintext using the named key. Currently,
this only supports symmetric keys. This path supports the `create` and `update`
policy capabilities as follows: if the user has the `create` capability for this
endpoint in their policies, and the key does not exist, it will be upserted with
default values (whether the key requires derivation depends on whether the
context parameter is empty or not). If the user only has `update` capability and
the key does not exist, an error will be returned.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/transit/encrypt/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the encryption key to
encrypt against. This is specified as part of the URL.
- `plaintext` `(string: <required>)` Specifies **base64 encoded** plaintext to
be encoded.
- `context` `(string: "")`  Specifies the **base64 encoded** context for key
derivation. This is required if key derivation is enabled for this key.
- `nonce` `(string: "")` Specifies the **base64 encoded** nonce value. This
must be provided if convergent encryption is enabled for this key and the key
was generated with Vault 0.6.1. Not required for keys created in 0.6.2+. The
value must be exactly 96 bits (12 bytes) long and the user must ensure that
for any given context (and thus, any given encryption key) this nonce value is
**never reused**.
- `batch_input` `(array<object>: nil)`  Specifies a list of items to be
encrypted in a single batch. When this parameter is set, if the parameters
'plaintext', 'context' and 'nonce' are also set, they will be ignored. The
format for the input is:
```json
[
{
"context": "c2FtcGxlY29udGV4dA==",
"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="
},
{
"context": "YW5vdGhlcnNhbXBsZWNvbnRleHQ=",
"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="
},
]
```
- `type` `(string: "aes256-gcm96")` This parameter is required when encryption
key is expected to be created. When performing an upsert operation, the type
of key to create. Currently, "aes256-gcm96" (symmetric) is the only type
supported.
- `convergent_encryption` `(string: "")`  This parameter will only be used when
a key is expected to be created. Whether to support convergent encryption.
This is only supported when using a key with key derivation enabled and will
require all requests to carry both a context and 96-bit (12-byte) nonce. The
given nonce will be used in place of a randomly generated nonce. As a result,
when the same context and nonce are supplied, the same ciphertext is
generated. It is _very important_ when using this mode that you ensure that
all nonces are unique for a given context. Failing to do so will severely
impact the ciphertext's security.
### Sample Payload
```json
{
"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveA=="
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/transit/encrypt/my-key
```
### Sample Response
```json
{
"data": {
"ciphertext": "vault:v1:abcdefgh"
}
}
```
## Decrypt Data
This endpoint decrypts the provided ciphertext using the named key. Currently,
this only supports symmetric keys.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/transit/decrypt/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the encryption key to
decrypt against. This is specified as part of the URL.
- `ciphertext` `(string: <required>)`  Specifies the ciphertext to decrypt.
- `context` `(string: "")` Specifies the **base64 encoded** context for key
derivation. This is required if key derivation is enabled.
- `nonce` `(string: "")`  Specifies a base64 encoded nonce value used during
encryption. Must be provided if convergent encryption is enabled for this key
and the key was generated with Vault 0.6.1. Not required for keys created in
0.6.2+.
- `batch_input` `(array<object>: nil)`  Specifies a list of items to be
decrypted in a single batch. When this parameter is set, if the parameters
'ciphertext', 'context' and 'nonce' are also set, they will be ignored. Format
for the input goes like this:
```json
[
{
"context": "c2FtcGxlY29udGV4dA==",
"ciphertext": "vault:v1:/DupSiSbX/ATkGmKAmhqD0tvukByrx6gmps7dVI="
},
{
"context": "YW5vdGhlcnNhbXBsZWNvbnRleHQ=",
"ciphertext": "vault:v1:XjsPWPjqPrBi1N2Ms2s1QM798YyFWnO4TR4lsFA="
},
]
```
### Sample Payload
```json
{
"ciphertext": "vault:v1:XjsPWPjqPrBi1N2Ms2s1QM798YyFWnO4TR4lsFA="
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/transit/decrypt/my-key
```
### Sample Response
```json
{
"data": {
"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveAo="
}
}
```
## Rewrap Data
This endpoint rewrapw the provided ciphertext using the latest version of the
named key. Because this never returns plaintext, it is possible to delegate this
functionality to untrusted users or scripts.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/transit/rewrap/:name` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the encryption key to
re-encrypt against. This is specified as part of the URL.
- `ciphertext` `(string: <required>)`  Specifies the ciphertext to re-encrypt.
- `context` `(string: "")` Specifies the **base64 encoded** context for key
derivation. This is required if key derivation is enabled.
- `nonce` `(string: "")`  Specifies a base64 encoded nonce value used during
encryption. Must be provided if convergent encryption is enabled for this key
and the key was generated with Vault 0.6.1. Not required for keys created in
0.6.2+.
- `batch_input` `(array<object>: nil)`  Specifies a list of items to be
decrypted in a single batch. When this parameter is set, if the parameters
'ciphertext', 'context' and 'nonce' are also set, they will be ignored. Format
for the input goes like this:
```json
[
{
"context": "c2FtcGxlY29udGV4dA==",
"ciphertext": "vault:v1:/DupSiSbX/ATkGmKAmhqD0tvukByrx6gmps7dVI="
},
{
"context": "YW5vdGhlcnNhbXBsZWNvbnRleHQ=",
"ciphertext": "vault:v1:XjsPWPjqPrBi1N2Ms2s1QM798YyFWnO4TR4lsFA="
},
]
```
### Sample Payload
```json
{
"ciphertext": "vault:v1:XjsPWPjqPrBi1N2Ms2s1QM798YyFWnO4TR4lsFA="
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/transit/rewrap/my-key
```
### Sample Response
```json
{
"data": {
"ciphertext": "vault:v2:abcdefgh"
}
}
```
## Generate Data Key
This endpoint generates a new high-entropy key and the value encrypted with the
named key. Optionally return the plaintext of the key as well. Whether plaintext
is returned depends on the path; as a result, you can use Vault ACL policies to
control whether a user is allowed to retrieve the plaintext value of a key. This
is useful if you want an untrusted user or operation to generate keys that are
then made available to trusted users.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/transit/datakey/:type/:name` | `200 application/json` |
### Parameters
- `type` `(string: <required>)`  Specifies the type of key to generate. If
`plaintext`, the plaintext key will be returned along with the ciphertext. If
`wrapped`, only the ciphertext value will be returned. This is specified as
part of the URL.
- `name` `(string: <required>)` Specifies the name of the encryption key to
re-encrypt against. This is specified as part of the URL.
- `context` `(string: "")`  Specifies the key derivation context, provided as a
base64-encoded string. This must be provided if derivation is enabled.
- `nonce` `(string: "")`  Specifies a nonce value, provided as base64 encoded.
Must be provided if convergent encryption is enabled for this key and the key
was generated with Vault 0.6.1. Not required for keys created in 0.6.2+. The
value must be exactly 96 bits (12 bytes) long and the user must ensure that
for any given context (and thus, any given encryption key) this nonce value is
**never reused**.
- `bits` `(int: 256)`  Specifies the number of bits in the desired key. Can be
128, 256, or 512.
### Sample Payload
```json
{
"context": "Ab3=="
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/transit/datakey/plaintext/my-key
```
### Sample Response
```json
{
"data": {
"plaintext": "dGhlIHF1aWNrIGJyb3duIGZveAo=",
"ciphertext": "vault:v1:abcdefgh"
}
}
```
## Generate Random Bytes
This endpoint returns high-quality random bytes of the specified length.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/transit/random(/:bytes)` | `200 application/json` |
### Parameters
- `bytes` `(int: 32)`  Specifies the number of bytes to return. This value can
be specified either in the request body, or as a part of the URL.
- `format` `(string: "base64")` Specifies the output encoding. Valid options
are `hex` or `base64`.
### Sample Payload
```json
{
"format": "hex"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/transit/random/164
```
### Sample Response
```json
{
"data": {
"random_bytes": "dGhlIHF1aWNrIGJyb3duIGZveAo="
}
}
```
## Hash Data
This endpoint returns the cryptographic hash of given data using the specified
algorithm.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/transit/hash(/:algorithm)` | `200 application/json` |
### Parameters
- `algorithm` `(string: "sha2-256")` Specifies the hash algorithm to use. This
can also be specified as part of the URL. Currently-supported algorithms are:
- `sha2-224`
- `sha2-256`
- `sha2-384`
- `sha2-512`
- `input` `(string: <required>)`  Specifies the **base64 encoded** input data.
- `format` `(string: "hex")`  Specifies the output encoding. This can be either
`hex` or `base64`.
### Sample Payload
```json
{
"input": "adba32=="
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/transit/hash/sha2-512
```
### Sample Response
```json
{
"data": {
"sum": "dGhlIHF1aWNrIGJyb3duIGZveAo="
}
}
```
## Generate HMAC with Key
This endpoint returns the digest of given data using the specified hash
algorithm and the named key. The key can be of any type supported by `transit`;
the raw key will be marshaled into bytes to be used for the HMAC function. If
the key is of a type that supports rotation, the latest (current) version will
be used.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/transit/hmac/:name(/:algorithm)` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the encryption key to
generate hmac against. This is specified as part of the URL.
- `algorithm` `(string: "sha2-256")` Specifies the hash algorithm to use. This
can also be specified as part of the URL. Currently-supported algorithms are:
- `sha2-224`
- `sha2-256`
- `sha2-384`
- `sha2-512`
- `input` `(string: <required>)`  Specifies the **base64 encoded** input data.
- `format` `(string: "hex")`  Specifies the output encoding. This can be either
`hex` or `base64`.
### Sample Payload
```json
{
"input": "adba32=="
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/transit/hmac/my-key/sha2-512
```
### Sample Response
```json
{
"data": {
"hmac": "dGhlIHF1aWNrIGJyb3duIGZveAo="
}
}
```
## Sign Data with Key
This endpoint returns the cryptographic signature of the given data using the
named key and the specified hash algorithm. The key must be of a type that
supports signing.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/transit/sign/:name(/:algorithm)` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the encryption key to
generate hmac against. This is specified as part of the URL.
- `algorithm` `(string: "sha2-256")` Specifies the hash algorithm to use. This
can also be specified as part of the URL. Currently-supported algorithms are:
- `sha2-224`
- `sha2-256`
- `sha2-384`
- `sha2-512`
- `input` `(string: <required>)`  Specifies the **base64 encoded** input data.
- `format` `(string: "hex")`  Specifies the output encoding. This can be either
`hex` or `base64`.
### Sample Payload
```json
{
"input": "adba32=="
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/transit/sign/my-key/sha2-512
```
### Sample Response
```json
{
"data": {
"signature": "vault:v1:MEUCIQCyb869d7KWuA0hBM9b5NJrmWzMW3/pT+0XYCM9VmGR+QIgWWF6ufi4OS2xo1eS2V5IeJQfsi59qeMWtgX0LipxEHI="
}
}
```
## Verify Data with Key
This endpoint returns whether the provided signature is valid for the given
data.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/transit/verify/:name(/:algorithm)` | `200 application/json` |
### Parameters
- `name` `(string: <required>)` Specifies the name of the encryption key to
generate hmac against. This is specified as part of the URL.
- `algorithm` `(string: "sha2-256")` Specifies the hash algorithm to use. This
can also be specified as part of the URL. Currently-supported algorithms are:
- `sha2-224`
- `sha2-256`
- `sha2-384`
- `sha2-512`
- `input` `(string: <required>)`  Specifies the **base64 encoded** input data.
- `format` `(string: "hex")`  Specifies the output encoding. This can be either
`hex` or `base64`.
- `signature` `(string: "")`  Specifies the signature output from the
`/transit/sign` function. Either this must be supplied or `hmac` must be
supplied.
- `hmac` `(string: "")`  Specifies the signature output from the
`/transit/hmac` function. Either this must be supplied or `signature` must be
supplied.
### Sample Payload
```json
{
"input": "abcd13==",
"signature": "vault:v1:MEUCIQCyb869d7KWuA..."
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/transit/verify/my-key/sha2-512
```
### Sample Response
```json
{
"data": {
"valid": true
}
}
```

View File

@ -0,0 +1,63 @@
---
layout: "api"
page_title: /sys/audit-hash - HTTP API"
sidebar_current: "docs-http-system-audit-hash"
description: |-
The `/sys/audit-hash` endpoint is used to hash data using an audit backend's
hash function and salt.
---
# `/sys/audit-hash`
The `/sys/audit-hash` endpoint is used to calculate the hash of the data used by
an audit backend's hash function and salt. This can be used to search audit logs
for a hashed value when the original value is known.
## Calculate Hash
This endpoint hashes the given input data with the specified audit backend's
hash function and salt. This endpoint can be used to discover whether a given
plaintext string (the `input` parameter) appears in the audit log in obfuscated
form.
The audit log records requests and responses. Since the Vault API is JSON-based,
any binary data returned from an API call (such as a DER-format certificate) is
base64-encoded by the Vault server in the response. As a result such information
should also be base64-encoded to supply into the `input` parameter.
| Method | Path | Produces |
| :------- | :---------------------- | :--------------------- |
| `POST` | `/sys/audit-hash/:path` | `204 (empty body)` |
### Parameters
- `path` `(string: <required>)`  Specifies the path of the audit backend to
generate hashes for. This is part of the request URL.
- `input` `(string: <required>)`  Specifies the input string to hash.
### Sample Payload
```json
{
"input": "my-secret-vault"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/sys/audit-hash/example-audit
```
### Sample Response
```json
{
"hash": "hmac-sha256:08ba35..."
}
```

View File

@ -0,0 +1,118 @@
---
layout: "api"
page_title: "/sys/audit - HTTP API"
sidebar_current: "docs-http-system-audit/"
description: |-
The `/sys/audit` endpoint is used to enable and disable audit backends.
---
# `/sys/audit`
The `/sys/audit` endpoint is used to list, mount, and unmount audit backends.
Audit backends must be enabled before use, and more than one backend may be
enabled at a time.
## List Mounted Audit Backends
This endpoint lists only the mounted audit backends (it does not list all
available audit backends).
- **`sudo` required**  This endpoint requires `sudo` capability in addition to
any path-specific capabilities.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/sys/audit` | `200 application/json` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/sys/audit
```
### Sample Response
```javascript
{
"file": {
"type": "file",
"description": "Store logs in a file",
"options": {
"path": "/var/log/vault.log"
}
}
}
```
## Mount Audit Backend
This endpoint mounts a new audit backend at the supplied path. The path can be a
single word name or a more complex, nested path.
- **`sudo` required**  This endpoint requires `sudo` capability in addition to
any path-specific capabilities.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `PUT` | `/sys/audit/:path` | `204 (empty body)` |
### Parameters
- `path` `(string: <required>)`  Specifies the path in which to mount the audit
backend. This is part of the request URL.
- `description` `(string: "")`  Specifies a human-friendly description of the
audit backend.
- `options` `(map<string|string>: nil)`  Specifies configuration options to
pass to the audit backend itself. This is dependent on the audit backend type.
- `type` `(string: <required>)`  Specifies the type of the audit backend.
### Sample Payload
```json
{
"type": "file",
"options": {
"path": "/var/log/vault/log"
}
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request PUT \
--data @payload.json \
https://vault.rocks/v1/sys/audit/example-audit
```
## Unmount Audit Backend
This endpoint un-mounts the audit backend at the given path.
- **`sudo` required**  This endpoint requires `sudo` capability in addition to
any path-specific capabilities.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/sys/audit/:path` | `204 (empty body)` |
### Parameters
- `path` `(string: <required>)`  Specifies the path of the audit backend to
delete. This is part of the request URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/sys/audit/example-audit
```

View File

@ -0,0 +1,193 @@
---
layout: "api"
page_title: "/sys/auth - HTTP API"
sidebar_current: "docs-http-system-auth"
description: |-
The `/sys/auth` endpoint is used to manage auth backends in Vault.
---
# `/sys/auth`
The `/sys/auth` endpoint is used to list, create, update, and delete auth
backends. Auth backends convert user or machine-supplied information into a
token which can be used for all future requests.
## List Auth Backends
This endpoint lists all enabled auth backends.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/sys/auth` | `200 application/json` |
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/sys/auth
```
### Sample Response
```json
{
"github/": {
"type": "github",
"description": "GitHub auth"
},
"token/": {
"config": {
"default_lease_ttl": 0,
"max_lease_ttl": 0
},
"description": "token based credentials",
"type": "token"
}
}
```
## Mount Auth Backend
This endpoint enables a new auth backend. After mounting, the auth backend can
be accessed and configured via the auth path specified as part of the URL. This
auth path will be nested under the `auth` prefix.
For example, mounting the "foo" auth backend will make it accessible at
`/auth/foo`.
- **`sudo` required**  This endpoint requires `sudo` capability in addition to
any path-specific capabilities.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/sys/auth/:path` | `204 (empty body)` |
### Parameters
- `path` `(string: <required>)`  Specifies the path in which to mount the auth
backend. This is part of the request URL.
- `description` `(string: "")`  Specifies a human-friendly description of the
auth backend.
- `type` `(string: <required>)`  Specifies the name of the authentication
backend type, such as "github" or "token".
### Sample Payload
```json
{
"type": "github",
"description": "Login with GitHub"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/sys/auth/my-auth
```
## Unmount Auth Backend
This endpoint un-mounts the auth backend at the given auth path.
- **`sudo` required**  This endpoint requires `sudo` capability in addition to
any path-specific capabilities.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `DELETE` | `/sys/auth/:path` | `204 (empty body)` |
### Parameters
- `path` `(string: <required>)`  Specifies the path to unmount. This is part of
the request URL.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request DELETE \
https://vault.rocks/v1/sys/auth/my-auth
```
## Read Auth Backend Tuning
This endpoint reads the given auth path's configuration. _This endpoint requires
`sudo` capability on the final path, but the same functionality can be achieved
without `sudo` via `sys/mounts/auth/[auth-path]/tune`._
- **`sudo` required**  This endpoint requires `sudo` capability in addition to
any path-specific capabilities.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `GET` | `/sys/auth/:path/tune` | `200 application/json` |
### Parameters
- `path` `(string: <required>)`  Specifies the path in which to tune.
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
https://vault.rocks/v1/sys/auth/my-auth/tune
```
### Sample Response
```json
{
"default_lease_ttl": 3600,
"max_lease_ttl": 7200
}
```
## Tune Auth Backend
Tune configuration parameters for a given auth path. _This endpoint
requires `sudo` capability on the final path, but the same functionality
can be achieved without `sudo` via `sys/mounts/auth/[auth-path]/tune`._
- **`sudo` required**  This endpoint requires `sudo` capability in addition to
any path-specific capabilities.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/sys/auth/:path/tune` | `204 (empty body)` |
### Parameters
- `default_lease_ttl` `(int: 0)` Specifies the default time-to-live. If set on
a specific auth path, this overrides the global default.
- `max_lease_ttl` `(int: 0)`  Specifies the maximum time-to-live. If set on a
specific auth path, this overrides the global default.
### Sample Payload
```json
{
"default_lease_ttl": 1800,
"max_lease_ttl": 86400
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data @payload.json \
https://vault.rocks/v1/sys/auth/my-auth/tune
```

View File

@ -0,0 +1,57 @@
---
layout: "api"
page_title: "/sys/capabilities-accessor - HTTP API"
sidebar_current: "docs-http-system-capabilities-accessor"
description: |-
The `/sys/capabilities-accessor` endpoint is used to fetch the capabilities of
the token associated with an accessor, on the given path.
---
# `/sys/capabilities-accessor`
The `/sys/capabilities-accessor` endpoint is used to fetch the capabilities of a
token associated with an accessor.
## Query Token Accessor Capabilities
This endpoint returns the capabilities of the token associated with an accessor,
for the given path.
| Method | Path | Produces |
| :------- | :--------------------------- | :--------------------- |
| `POST` | `/sys/capabilities-accessor` | `200 application/json` |
### Parameters
- `accessor` `(string: <required>)` Specifies the accessor of the token to
check.
- `path` `(string: <required>)` Specifies the path on which the token's
capabilities will be checked.
### Sample Payload
```json
{
"accessor": "abcd1234",
"path": "secret/foo"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data payload.json \
https://vault.rocks/v1/sys/capabilities-accessor
```
### Sample Response
```json
{
"capabilities": ["read", "list"]
}
```

View File

@ -0,0 +1,54 @@
---
layout: "api"
page_title: "/sys/capabilities-self - HTTP API"
sidebar_current: "docs-http-system-capabilities-self"
description: |-
The `/sys/capabilities-self` endpoint is used to fetch the capabilities of
client token on a given path.
---
# `/sys/capabilities-self`
The `/sys/capabilities-self` endpoint is used to fetch the capabilities of a the
supplied token.
## Query Self Capabilities
This endpoint returns the capabilities of client token on the given path. The
client token is the Vault token with which this API call is made.
| Method | Path | Produces |
| :------- | :----------------------- | :--------------------- |
| `POST` | `/sys/capabilities-self` | `200 application/json` |
### Parameters
- `path` `(string: <required>)`  Specifies the path on which the client token's
capabilities will be checked.
### Sample Payload
```json
{
"path": "secret/foo"
}
```
### Sample Request
```
$ curl \
--header "X-Vault-Token: ..." \
--request POST \
--data payload.json \
https://vault.rocks/v1/sys/capabilities-self
```
### Sample Response
```json
{
"capabilities": ["read", "list"]
}
```

Some files were not shown because too many files have changed in this diff Show More