Remove JWT for the 0.3 release; it needs a lot of rework.
This commit is contained in:
parent
f10343921b
commit
af27a99bb7
|
@ -12,7 +12,6 @@ FEATURES:
|
|||
|
||||
* **SSH Backend**: Vault can now be used to delegate SSH access to machines, via a (recommended) One-Time Password approach or by issuing dynamic keys. [GH-385]
|
||||
* **Cubbyhole Backend**: This backend works similarly to the "generic" backend but provides a per-token workspace. This enables some additional authentication workflows (especially for containers) and can be useful to applications to e.g. store local credentials while being restarted or upgraded, rather than persisting to disk. [GH-612]
|
||||
* **JWT Backend**: Vault can now generate JSON Web Tokens. Vault's built-in authentication and authorization mechanisms provide verification functionality, removing the need to generate a private key and sign the token. [GH-460]
|
||||
* **Transit Backend Improvements**: The transit backend now allows key rotation and datakey generation. For rotation, data encrypted with previous versions of the keys can still be decrypted, down to a (configurable) minimum previous version; there is a rewrap function for manual upgrades of ciphertext to newer versions. Additionally, the backend now allows generating and returning high-entropy keys of a configurable bitsize suitable for AES and other functions; this is returned wrapped by a named key, or optionally both wrapped and plaintext for immediate use. [GH-626]
|
||||
* **Global and Per-Mount Default/Max TTL Support**: You can now set the default and maximum Time To Live for leases both globally and per-mount. Per-mount settings override global settings. Not all backends honor these settings yet, but the maximum is a hard limit enforced outside the backend. See the documentation for "/sys/mounts/" for details on configuring per-mount TTLs. [GH-469]
|
||||
* **PGP Encryption for Unseal Keys**: When initializing or rotating Vault's master key, PGP/GPG public keys can now be provided. The output keys will be encrypted with the given keys, in order. [GH-570]
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
package jwt
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
// Factory creates a new backend implementing the logical.Backend interface
|
||||
func Factory(conf *logical.BackendConfig) (logical.Backend, error) {
|
||||
return Backend().Setup(conf)
|
||||
}
|
||||
|
||||
// Backend returns a new Backend framework struct
|
||||
func Backend() *framework.Backend {
|
||||
var b backend
|
||||
b.Backend = &framework.Backend{
|
||||
|
||||
Paths: []*framework.Path{
|
||||
pathRoles(&b),
|
||||
pathIssue(&b),
|
||||
},
|
||||
}
|
||||
|
||||
return b.Backend
|
||||
}
|
||||
|
||||
type backend struct {
|
||||
*framework.Backend
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
package jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
logicaltest "github.com/hashicorp/vault/logical/testing"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
func TestBackend_basic(t *testing.T) {
|
||||
tokenClaims := map[string]interface{}{
|
||||
"iss": "Test Issuer",
|
||||
"sub": "Test Subject",
|
||||
"aud": "Test Audience",
|
||||
"iat": 1438898720,
|
||||
"nbf": 1438898720,
|
||||
"exp": 1538898720,
|
||||
"jti": "jti",
|
||||
"ran": "random",
|
||||
}
|
||||
|
||||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Backend: Backend(),
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepWriteRole(t, "test", "HS256", "test"),
|
||||
testAccStepReadRole(t, "test", "HS256", "test"),
|
||||
testAccStepSignToken(t, "test", tokenClaims, false),
|
||||
testAccStepDeleteRole(t, "test"),
|
||||
testAccStepReadRole(t, "test", "HS256", "test"),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestBackend_defaults(t *testing.T) {
|
||||
tokenClaims := map[string]interface{}{
|
||||
"iat": 1438898720,
|
||||
"nbf": 1438898720,
|
||||
"exp": 1538898720,
|
||||
"jti": "9fe94d93-7bb4-434c-b197-731b4b4c70d3",
|
||||
"ran": "random",
|
||||
}
|
||||
|
||||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Backend: Backend(),
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepWriteRole(t, "test", "HS256", "test"),
|
||||
testAccStepReadRole(t, "test", "HS256", "test"),
|
||||
testAccStepSignToken(t, "test", tokenClaims, true),
|
||||
testAccStepDeleteRole(t, "test"),
|
||||
testAccStepReadRole(t, "test", "HS256", "test"),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccStepWriteRole(t *testing.T, name string, algorithm string, key string) logicaltest.TestStep {
|
||||
return logicaltest.TestStep{
|
||||
Operation: logical.WriteOperation,
|
||||
Path: "roles/" + name,
|
||||
Data: map[string]interface{}{
|
||||
"algorithm": algorithm,
|
||||
"key": key,
|
||||
"default_issuer": "Test Default Issuer",
|
||||
"default_subject": "Test Default Subject",
|
||||
"default_audience": "Test Default Audience",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testAccStepDeleteRole(t *testing.T, name string) logicaltest.TestStep {
|
||||
return logicaltest.TestStep{
|
||||
Operation: logical.DeleteOperation,
|
||||
Path: "roles/" + name,
|
||||
}
|
||||
}
|
||||
|
||||
func testAccStepReadRole(t *testing.T, name string, algorithm string, key string) logicaltest.TestStep {
|
||||
return logicaltest.TestStep{
|
||||
Operation: logical.ReadOperation,
|
||||
Path: "roles/" + name,
|
||||
Check: func(resp *logical.Response) error {
|
||||
if resp == nil {
|
||||
return fmt.Errorf("missing response")
|
||||
}
|
||||
var d struct {
|
||||
Algorithm string `json:"algorithm" structs:"algorithm" mapstructure:"algorithm"`
|
||||
Key string `json:"key" structs:"key" mapstructure:"key"`
|
||||
Issuer string `json:"iss" structs:"iss" mapstructure:"iss"`
|
||||
Subject string `json:"sub" structs:"sub" mapstructure:"sub"`
|
||||
Audience string `json:"aud" structs:"aud" mapstructure:"aud"`
|
||||
}
|
||||
if err := mapstructure.Decode(resp.Data, &d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.Algorithm != algorithm {
|
||||
return fmt.Errorf("bad algorithm: expected %s, got %#v", algorithm, d)
|
||||
}
|
||||
if d.Key != "" {
|
||||
return fmt.Errorf("bad key: expected %s, got %#v", key, d)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testAccStepSignToken(t *testing.T, name string, tokenClaims map[string]interface{}, defaults bool) logicaltest.TestStep {
|
||||
return logicaltest.TestStep{
|
||||
Operation: logical.WriteOperation,
|
||||
Path: "issue/" + name,
|
||||
Data: tokenClaims,
|
||||
Check: func(resp *logical.Response) error {
|
||||
var d struct {
|
||||
JTI string `mapstructure:"jti"`
|
||||
Token string `mapstructure:"token"`
|
||||
}
|
||||
if err := mapstructure.Decode(resp.Data, &d); err != nil {
|
||||
return err
|
||||
}
|
||||
if d.Token == "" {
|
||||
return fmt.Errorf("missing token")
|
||||
}
|
||||
|
||||
token, err := jwt.Parse(d.Token, func(token *jwt.Token) (interface{}, error) {
|
||||
return token, nil
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing token")
|
||||
}
|
||||
|
||||
if d.JTI != token.Claims["jti"] {
|
||||
return fmt.Errorf("bad: %#v", d)
|
||||
}
|
||||
|
||||
if token.Claims["ran"] != "random" {
|
||||
return fmt.Errorf("bad: %#v", d)
|
||||
}
|
||||
|
||||
if defaults == true {
|
||||
if token.Claims["sub"] != "Test Default Subject" {
|
||||
return fmt.Errorf("bad: %#v", d)
|
||||
}
|
||||
if token.Claims["aud"] != "Test Default Audience" {
|
||||
return fmt.Errorf("bad: %#v", d)
|
||||
}
|
||||
if token.Claims["iss"] != "Test Default Issuer" {
|
||||
return fmt.Errorf("bad: %#v", d)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
package jwt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
|
||||
"github.com/hashicorp/vault/helper/uuid"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
func pathIssue(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: "issue/" + framework.GenericNameRegex("role"),
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"role": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "The desired role with configuration for this request",
|
||||
},
|
||||
"issuer": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "The issuer of the token",
|
||||
},
|
||||
"subject": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "The subject of the token",
|
||||
},
|
||||
"audience": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "The audience of the token",
|
||||
},
|
||||
"expiration": &framework.FieldSchema{
|
||||
Type: framework.TypeInt,
|
||||
Description: "This will define the expiration in NumericDate value",
|
||||
},
|
||||
"not_before": &framework.FieldSchema{
|
||||
Type: framework.TypeInt,
|
||||
Description: "Defines the time before which the JWT MUST NOT be accepted for processing",
|
||||
},
|
||||
"issued_at": &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Description: "Whether to include the issued_at claim",
|
||||
},
|
||||
"jti": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Unique identifier for the JWT",
|
||||
},
|
||||
"claims": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "JSON Object of Claims for the JWT Token",
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.WriteOperation: b.pathIssueWrite,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) pathIssueWrite(
|
||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
roleName := data.Get("role").(string)
|
||||
|
||||
// Get the role
|
||||
role, err := b.getRole(req.Storage, roleName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if role == nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf("Unknown role: %s", roleName)), nil
|
||||
}
|
||||
|
||||
claims := map[string]interface{}{
|
||||
"initial": "ok",
|
||||
}
|
||||
|
||||
if role.Issuer != "" {
|
||||
claims["iss"] = role.Issuer
|
||||
}
|
||||
if role.Subject != "" {
|
||||
claims["sub"] = role.Subject
|
||||
}
|
||||
if role.Audience != "" {
|
||||
claims["aud"] = role.Audience
|
||||
}
|
||||
|
||||
if data.Get("not_before") == 0 {
|
||||
claims["nbf"] = int(time.Now().Unix())
|
||||
}
|
||||
if data.Get("issued_at").(bool) {
|
||||
claims["iat"] = int(time.Now().Unix())
|
||||
}
|
||||
if data.Get("jti") == "" {
|
||||
claims["jti"] = uuid.GenerateUUID()
|
||||
}
|
||||
|
||||
if data.Get("issuer") != "" {
|
||||
claims["iss"] = data.Get("issuer").(string)
|
||||
}
|
||||
if data.Get("subject") != "" {
|
||||
claims["sub"] = data.Get("subject").(string)
|
||||
}
|
||||
if data.Get("audience") != "" {
|
||||
claims["aud"] = data.Get("audience").(string)
|
||||
}
|
||||
if data.Get("expiration").(int) > 0 {
|
||||
claims["exp"] = data.Get("expiration").(int)
|
||||
}
|
||||
if data.Get("not_before").(int) > 0 {
|
||||
claims["nbf"] = data.Get("not_before").(int)
|
||||
}
|
||||
if data.Get("jti") != "" {
|
||||
claims["jti"] = data.Get("jti").(string)
|
||||
}
|
||||
|
||||
if data.Get("claims").(string) != "" {
|
||||
// Parse JSON using unmarshal
|
||||
var uc map[string]interface{}
|
||||
err := json.Unmarshal([]byte(data.Get("claims").(string)), &uc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range uc {
|
||||
claims[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
delete(claims, "initial")
|
||||
|
||||
token := jwt.New(jwt.GetSigningMethod(role.Algorithm))
|
||||
token.Claims = claims
|
||||
|
||||
tokenString, err := token.SignedString([]byte(role.Key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"jti": claims["jti"].(string),
|
||||
"token": tokenString,
|
||||
},
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
|
@ -1,188 +0,0 @@
|
|||
package jwt
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
"github.com/fatih/structs"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
func pathRoles(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: "roles/" + framework.GenericNameRegex("name"),
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"name": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Name of the Role",
|
||||
},
|
||||
"algorithm": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Default: "RS256",
|
||||
Description: "Algorithm for JWT Signing",
|
||||
},
|
||||
"key": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Private Key (RSA or EC) or String for HMAC Algorithm",
|
||||
},
|
||||
"default_issuer": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Default Issuer for the Role for the JWT Tokens",
|
||||
},
|
||||
"default_subject": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Default Subject for the Role for the JWT Token",
|
||||
},
|
||||
"default_audience": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Default Audience for the Role for the JWT Token",
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.ReadOperation: b.pathRoleRead,
|
||||
logical.WriteOperation: b.pathRoleCreate,
|
||||
logical.DeleteOperation: b.pathRoleDelete,
|
||||
},
|
||||
|
||||
HelpSynopsis: pathRolesHelpSyn,
|
||||
HelpDescription: pathRolesHelpDesc,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) getRole(s logical.Storage, n string) (*roleEntry, error) {
|
||||
entry, err := s.Get("role/" + n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if entry == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var result roleEntry
|
||||
if err := entry.DecodeJSON(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (b *backend) pathRoleDelete(
|
||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
err := req.Storage.Delete("role/" + data.Get("name").(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *backend) pathRoleRead(
|
||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
role, err := b.getRole(req.Storage, data.Get("name").(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if role == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var r = structs.New(role).Map()
|
||||
|
||||
delete(r, "key")
|
||||
|
||||
resp := &logical.Response{
|
||||
Data: r,
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *backend) pathRoleCreate(
|
||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||
name := data.Get("name").(string)
|
||||
key := data.Get("key").(string)
|
||||
alg := data.Get("algorithm").(string)
|
||||
|
||||
signingMethod := jwt.GetSigningMethod(data.Get("algorithm").(string))
|
||||
if signingMethod == nil {
|
||||
return nil, fmt.Errorf("Invalid Signing Algorithm")
|
||||
}
|
||||
|
||||
if key == "" {
|
||||
return nil, fmt.Errorf("Key is Required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(alg, "RS") {
|
||||
// need RSA Private Key
|
||||
if strings.Contains(key, "RSA PRIVATE KEY") == false {
|
||||
return nil, fmt.Errorf("Key is not a PEM formatted RSA Private Key")
|
||||
}
|
||||
|
||||
block, _ := pem.Decode([]byte(key))
|
||||
_, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse the private key: %s", err)
|
||||
}
|
||||
} else if strings.HasPrefix(alg, "HS") {
|
||||
// need a string
|
||||
if key == "" {
|
||||
return nil, fmt.Errorf("Key must not be blank")
|
||||
}
|
||||
} else if strings.HasPrefix(alg, "EC") {
|
||||
// need EC Private Key
|
||||
if strings.Contains(key, "EC PRIVATE KEY") == false {
|
||||
return nil, fmt.Errorf("Key is not a PEM formatted EC Private Key")
|
||||
}
|
||||
|
||||
block, _ := pem.Decode([]byte(key))
|
||||
_, err := x509.ParseECPrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse the private key: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
entry := &roleEntry{
|
||||
Algorithm: alg,
|
||||
Key: key,
|
||||
Issuer: data.Get("default_issuer").(string),
|
||||
Subject: data.Get("default_subject").(string),
|
||||
Audience: data.Get("default_audience").(string),
|
||||
}
|
||||
|
||||
// Store it
|
||||
jsonEntry, err := logical.StorageEntryJSON("role/"+name, entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := req.Storage.Put(jsonEntry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type roleEntry struct {
|
||||
Algorithm string `json:"algorithm" structs:"algorithm" mapstructure:"algorithm"`
|
||||
Key string `json:"key" structs:"key" mapstructure:"key"`
|
||||
Issuer string `json:"iss" structs:"iss" mapstructure:"iss"`
|
||||
Subject string `json:"sub" structs:"sub" mapstructure:"sub"`
|
||||
Audience string `json:"aud" structs:"aud" mapstructure:"aud"`
|
||||
}
|
||||
|
||||
const pathRolesHelpSyn = `
|
||||
Read and write basic configuration for generating signed JWT Tokens.
|
||||
`
|
||||
|
||||
const pathRolesHelpDesc = `
|
||||
This path allows you to read and write roles that are used to
|
||||
create JWT tokens. These roles have a few settings that dictated
|
||||
what signing algorithm is used for the JWT token. For example,
|
||||
if the backend is mounted at "jwt" and you create a role at
|
||||
"jwt/roles/auth" then a user can request a JWT token at "jwt/issue/auth".
|
||||
`
|
|
@ -17,7 +17,6 @@ import (
|
|||
"github.com/hashicorp/vault/builtin/logical/aws"
|
||||
"github.com/hashicorp/vault/builtin/logical/cassandra"
|
||||
"github.com/hashicorp/vault/builtin/logical/consul"
|
||||
"github.com/hashicorp/vault/builtin/logical/jwt"
|
||||
"github.com/hashicorp/vault/builtin/logical/mysql"
|
||||
"github.com/hashicorp/vault/builtin/logical/pki"
|
||||
"github.com/hashicorp/vault/builtin/logical/postgresql"
|
||||
|
@ -76,7 +75,6 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
|
|||
"transit": transit.Factory,
|
||||
"mysql": mysql.Factory,
|
||||
"ssh": ssh.Factory,
|
||||
"jwt": jwt.Factory,
|
||||
},
|
||||
ShutdownCh: makeShutdownCh(),
|
||||
}, nil
|
||||
|
|
|
@ -1,267 +0,0 @@
|
|||
---
|
||||
layout: "docs"
|
||||
page_title: "Secret Backend: JWT"
|
||||
sidebar_current: "docs-secrets-jwt"
|
||||
description: |-
|
||||
The JWT secret backend for Vault generates JSON Web Tokens dynamically based on configured roles.
|
||||
---
|
||||
|
||||
# JWT Secret Backend
|
||||
|
||||
Name: `jwt`
|
||||
|
||||
The JWT secret backend for Vault generates JSON Web Tokens dynamically based on configured roles. This means services can get tokens needed for authentication without going through the usual manual process of generating a private key and signing the token and maintaining the private key's security. Vault's built-in authentication and authorization mechanisms provide the verification functionality.
|
||||
|
||||
This page will show a quick start for this backend. For detailed documentation on every path, use `vault path-help` after mounting the backend.
|
||||
|
||||
The JWT secret backend acts like the `transit` backend, it does not store any information.
|
||||
|
||||
## Algorithms
|
||||
|
||||
#### RSA
|
||||
* RS256
|
||||
* RS384
|
||||
* RS512
|
||||
|
||||
These require a RSA private/public keypair for signing and verification.
|
||||
|
||||
#### ECDSA
|
||||
* EC256
|
||||
* EC384
|
||||
* EC512
|
||||
|
||||
These require an ECDSA private/public keypair for signing and verification.
|
||||
|
||||
#### HMAC
|
||||
* HS256
|
||||
* HS384
|
||||
* HS512
|
||||
|
||||
These require a shared secret for signing and verification.
|
||||
|
||||
## Roles
|
||||
|
||||
Roles are defined with the signing algorithm, the secret key or private key to be used, as well as allowing for default but optional JWT Token claims. Once you write a private key or a secret to the role, it CANNOT be read back out.
|
||||
|
||||
## Quick Start
|
||||
|
||||
The first step to using the jwt backend is to mount it.
|
||||
Unlike the `generic` backend, the `jwt` backend is not mounted by default.
|
||||
|
||||
```text
|
||||
$ vault mount jwt
|
||||
Successfully mounted 'jwt' at 'jwt'!
|
||||
```
|
||||
|
||||
The next step is to configure a role. A role is a logical name that maps
|
||||
to a few settings used to generated the tokens. For example, lets create
|
||||
a "webauth" role:
|
||||
|
||||
```text
|
||||
$ vault write jwt/roles/webauth \
|
||||
algorithm=RS256 \
|
||||
key=@/path/to/private.key
|
||||
```
|
||||
|
||||
Each role requires a secret or a private key to be associated against it.
|
||||
|
||||
Generating a token requires passing of additional information so we use the
|
||||
"jwt/issue/ROLE" path.
|
||||
|
||||
```text
|
||||
$ vault write jwt/issue/webauth \
|
||||
issuer="Vault" \
|
||||
audience="Vault Client" \
|
||||
expiration="1538096292" \
|
||||
claims=@extra.json
|
||||
```
|
||||
|
||||
|
||||
## API
|
||||
|
||||
### /jwt/roles/
|
||||
#### POST
|
||||
|
||||
<dl class="api">
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
Creates or updates a named role.
|
||||
</dd>
|
||||
|
||||
<dt>Method</dt>
|
||||
<dd>POST</dd>
|
||||
|
||||
<dt>URL</dt>
|
||||
<dd>`/jwt/roles/<name>`</dd>
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">algorithm</span>
|
||||
<span class="param-flags">required</span>
|
||||
The algorithm used by JWT to sign the token.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">key</span>
|
||||
<span class="param-flags">required</span>
|
||||
The private key or string used to sign the token.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">default_issuer</span>
|
||||
<span class="param-flags">required</span>
|
||||
The default issuer claim for the role, can be overridden at issue time.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">default_subject</span>
|
||||
<span class="param-flags">required</span>
|
||||
The default subject claim for the role, can be overridden at issue time.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">default_audience</span>
|
||||
<span class="param-flags">required</span>
|
||||
The default audience claim for the role, can be overridden at issue time.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt>Returns</dt>
|
||||
<dd>
|
||||
A `204` response code.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
#### GET
|
||||
|
||||
<dl class="api">
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
Queries a named role.
|
||||
</dd>
|
||||
|
||||
<dt>Method</dt>
|
||||
<dd>GET</dd>
|
||||
|
||||
<dt>URL</dt>
|
||||
<dd>`/jwt/roles/<name>`</dd>
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
None
|
||||
</dd>
|
||||
|
||||
<dt>Returns</dt>
|
||||
<dd>
|
||||
|
||||
```javascript
|
||||
{
|
||||
"data": {
|
||||
"algorithm": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
#### DELETE
|
||||
|
||||
<dl class="api">
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
Deletes a named role.
|
||||
</dd>
|
||||
|
||||
<dt>Method</dt>
|
||||
<dd>DELETE</dd>
|
||||
|
||||
<dt>URL</dt>
|
||||
<dd>`/jwt/roles/<name>`</dd>
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
None
|
||||
</dd>
|
||||
|
||||
<dt>Returns</dt>
|
||||
<dd>
|
||||
A `204` response code.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
### /jwt/issue/
|
||||
#### POST
|
||||
|
||||
<dl class="api">
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
Generates a JWT token based on the named role.
|
||||
</dd>
|
||||
|
||||
<dt>Method</dt>
|
||||
<dd>GET</dd>
|
||||
|
||||
<dt>URL</dt>
|
||||
<dd>`/jwt/issue/<role>`</dd>
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">issuer</span>
|
||||
<span class="param-flags">optional</span>
|
||||
The Issuer of the token.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">audience</span>
|
||||
<span class="param-flags">optional</span>
|
||||
The Audience of the token.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">subject</span>
|
||||
<span class="param-flags">optional</span>
|
||||
The Subject of the token.
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">expiration</span>
|
||||
<span class="param-flags">optional</span>
|
||||
The expiration of the token, expressed in seconds (unix time).
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">issued_at</span>
|
||||
<span class="param-flags">optional</span>
|
||||
The issued at time of the token, expressed in seconds (unix time). (Default: current time)
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">not_before</span>
|
||||
<span class="param-flags">optional</span>
|
||||
Not Before: the time at which the token is not useful before. Expressed as seconds, unix time. (Default: current time)
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">jti</span>
|
||||
<span class="param-flags">optional</span>
|
||||
JSONWebToken Identifier. Unique ID useful for preventing replay attacks. (Default: Random UUID)
|
||||
</li>
|
||||
<li>
|
||||
<span class="param">claims</span>
|
||||
<span class="param-flags">optional</span>
|
||||
Should be a JSON Object of additional key/values you want in the token.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt>Returns</dt>
|
||||
<dd>
|
||||
|
||||
```javascript
|
||||
{
|
||||
"data": {
|
||||
"jti": "...",
|
||||
"token": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</dd>
|
||||
</dl>
|
|
@ -121,10 +121,6 @@
|
|||
<a href="/docs/secrets/generic/index.html">Generic</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-secrets-jwt") %>>
|
||||
<a href="/docs/secrets/jwt/index.html">JWT</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-secrets-mysql") %>>
|
||||
<a href="/docs/secrets/mysql/index.html">MySQL</a>
|
||||
</li>
|
||||
|
|
Loading…
Reference in New Issue