Add kms_library configuration stanza (#13352)
- Add the kms_library configuration stanza to Vault's command/server - Provide validation of keys and general configuration. - Add initial kms_library configuration documentation - Attempt at startup to verify we can read the configured HSM Library - Hook in KmsLibrary config into the Validate to detect typo/unused keys
This commit is contained in:
parent
7178e2c4be
commit
94e6a688ff
|
@ -8,10 +8,13 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
||||
"github.com/hashicorp/hcl"
|
||||
|
@ -23,6 +26,8 @@ var entConfigValidate = func(_ *Config, _ string) []configutil.ConfigError {
|
|||
return nil
|
||||
}
|
||||
|
||||
var kmsLibraryValidator = defaultKmsLibraryValidator
|
||||
|
||||
// Config is the configuration for the vault server.
|
||||
type Config struct {
|
||||
UnusedKeys configutil.UnusedKeyMap `hcl:",unusedKeyPositions"`
|
||||
|
@ -82,6 +87,8 @@ type Config struct {
|
|||
|
||||
License string `hcl:"-"`
|
||||
LicensePath string `hcl:"license_path"`
|
||||
|
||||
KmsLibraries map[string]*KMSLibrary `hcl:"-"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -99,6 +106,9 @@ func (c *Config) Validate(sourceFilePath string) []configutil.ConfigError {
|
|||
for _, l := range c.Listeners {
|
||||
results = append(results, l.Validate(sourceFilePath)...)
|
||||
}
|
||||
for _, kmslibrary := range c.KmsLibraries {
|
||||
results = append(results, kmslibrary.Validate(sourceFilePath)...)
|
||||
}
|
||||
results = append(results, c.validateEnt(sourceFilePath)...)
|
||||
return results
|
||||
}
|
||||
|
@ -168,6 +178,24 @@ func (b *ServiceRegistration) GoString() string {
|
|||
return fmt.Sprintf("*%#v", *b)
|
||||
}
|
||||
|
||||
// KMSLibrary is a per-server configuration that will be further augmented with managed key configuration to
|
||||
// build up a KMS wrapper type to delegate encryption operations to HSMs
|
||||
type KMSLibrary struct {
|
||||
UnusedKeys configutil.UnusedKeyMap `hcl:",unusedKeyPositions"`
|
||||
FoundKeys []string `hcl:",decodedFields"`
|
||||
Type string `hcl:"-"`
|
||||
Name string `hcl:"name"`
|
||||
Library string `hcl:"library"`
|
||||
}
|
||||
|
||||
func (k *KMSLibrary) Validate(source string) []configutil.ConfigError {
|
||||
return configutil.ValidateUnusedFields(k.UnusedKeys, source)
|
||||
}
|
||||
|
||||
func (k *KMSLibrary) GoString() string {
|
||||
return fmt.Sprintf("*%#v", *k)
|
||||
}
|
||||
|
||||
func NewConfig() *Config {
|
||||
return &Config{
|
||||
SharedConfig: new(configutil.SharedConfig),
|
||||
|
@ -528,6 +556,14 @@ func ParseConfig(d, source string) (*Config, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Parse KMSLibraries sections if any
|
||||
if o := list.Filter("kms_library"); len(o.Items) > 0 {
|
||||
delete(result.UnusedKeys, "kms_library")
|
||||
if err := parseKmsLibraries(result, o); err != nil {
|
||||
return nil, fmt.Errorf("error parsing 'kms_library': %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
entConfig := &(result.entConfig)
|
||||
if err := entConfig.parseConfig(list); err != nil {
|
||||
return nil, fmt.Errorf("error parsing enterprise config: %w", err)
|
||||
|
@ -794,6 +830,75 @@ func parseServiceRegistration(result *Config, list *ast.ObjectList, name string)
|
|||
return nil
|
||||
}
|
||||
|
||||
func parseKmsLibraries(result *Config, list *ast.ObjectList) error {
|
||||
result.KmsLibraries = make(map[string]*KMSLibrary, len(list.Items))
|
||||
|
||||
for _, item := range list.Items {
|
||||
library, err := decodeKmsLibrary(item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateKmsLibrary(library); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := result.KmsLibraries[library.Name]; ok {
|
||||
return fmt.Errorf("duplicated kms_library configuration sections with name %s", library.Name)
|
||||
}
|
||||
|
||||
result.KmsLibraries[library.Name] = library
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeKmsLibrary(item *ast.ObjectItem) (*KMSLibrary, error) {
|
||||
library := &KMSLibrary{}
|
||||
if err := hcl.DecodeObject(&library, item.Val); err != nil {
|
||||
return nil, multierror.Prefix(err, "kms_library")
|
||||
}
|
||||
|
||||
if len(item.Keys) != 1 {
|
||||
return nil, errors.New("kms_library section was missing a type")
|
||||
}
|
||||
|
||||
library.Type = strings.ToLower(item.Keys[0].Token.Value().(string))
|
||||
library.Name = strings.ToLower(library.Name)
|
||||
|
||||
return library, nil
|
||||
}
|
||||
|
||||
func defaultKmsLibraryValidator(kms *KMSLibrary) error {
|
||||
switch kms.Type {
|
||||
case wrapping.PKCS11:
|
||||
return fmt.Errorf("KMS type 'pkcs11' requires the Vault Enterprise HSM binary")
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown KMS type %q", kms.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func validateKmsLibrary(kmsConfig *KMSLibrary) error {
|
||||
if kmsConfig.Library == "" {
|
||||
return fmt.Errorf("library key can not be blank within kms_library type: %s", kmsConfig.Type)
|
||||
}
|
||||
|
||||
if kmsConfig.Name == "" {
|
||||
return fmt.Errorf("name key can not be blank within kms_library type: %s", kmsConfig.Type)
|
||||
}
|
||||
|
||||
nameRegex := regexp.MustCompile("^[\\w._-]+$")
|
||||
if !nameRegex.MatchString(kmsConfig.Name) {
|
||||
return fmt.Errorf("value ('%s') for name field contained invalid characters within kms_library type: %s", kmsConfig.Name, kmsConfig.Type)
|
||||
}
|
||||
|
||||
if err := kmsLibraryValidator(kmsConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sanitized returns a copy of the config with all values that are considered
|
||||
// sensitive stripped. It also strips all `*Raw` values that are mainly
|
||||
// used for parsing.
|
||||
|
|
|
@ -17,3 +17,7 @@ func TestLoadConfigFile_json2(t *testing.T) {
|
|||
func TestParseEntropy(t *testing.T) {
|
||||
testParseEntropy(t, true)
|
||||
}
|
||||
|
||||
func TestKmsLibraryFailsForNonHSMBinary(t *testing.T) {
|
||||
testKmsLibraryFailsForNonHSMBinary(t)
|
||||
}
|
||||
|
|
|
@ -1063,3 +1063,26 @@ func testConfigRaftAutopilot(t *testing.T) {
|
|||
t.Fatal(diff)
|
||||
}
|
||||
}
|
||||
|
||||
func testKmsLibraryFailsForNonHSMBinary(t *testing.T) {
|
||||
config := `
|
||||
ui = false
|
||||
storage "file" {
|
||||
path = "/tmp/test"
|
||||
}
|
||||
|
||||
listener "tcp" {
|
||||
address = "0.0.0.0:8200"
|
||||
tls_cert_file = "/opt/vault/tls/tls.crt"
|
||||
tls_key_file = "/opt/vault/tls/tls.key"
|
||||
}
|
||||
|
||||
kms_library "pkcs11" {
|
||||
name="Logical1"
|
||||
library="a library"
|
||||
}
|
||||
`
|
||||
_, err := ParseConfig(config, "")
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "requires the Vault Enterprise HSM binary")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
layout: docs
|
||||
page_title: Kms Library - Configuration
|
||||
description: >-
|
||||
The kms_library stanza allows node specific configuration for access to
|
||||
KMS access libraries
|
||||
---
|
||||
|
||||
# `kms_library` Stanza
|
||||
|
||||
The `kms_library` stanza isolates platform specific configuration for managed keys.
|
||||
It defines logical names that are referenced within an API configuration keeping cluster
|
||||
and node specific details separated along with deployment concerns for each.
|
||||
|
||||
At the moment managed keys are only available as a feature set within Vault Enterprise HSM edition.
|
||||
|
||||
## Requirements
|
||||
|
||||
The following software packages are required for Vault Enterprise HSM:
|
||||
|
||||
- PKCS#11 compatible HSM integration library. Vault targets version 2.2 or
|
||||
higher of PKCS#11. Depending on any given HSM, some functions (such as key
|
||||
generation) may have to be performed manually.
|
||||
- The [GNU libltdl
|
||||
library](https://www.gnu.org/software/libtool/manual/html_node/Using-libltdl.html)
|
||||
— ensure that it is installed for the correct architecture of your servers
|
||||
|
||||
## Configuration
|
||||
|
||||
Multiple kms_library stanza's can be defined with the only limitation that the value for the
|
||||
name key needs to be a unique value across all the stanza definitions in a case-insensitive
|
||||
manner.
|
||||
|
||||
The type argument only supports "pkcs11" at the moment.
|
||||
|
||||
Example `kms_library` stanza:
|
||||
|
||||
```hcl
|
||||
kms_library [TYPE] {
|
||||
name = "<logical name>"
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
## `pkcs11` Parameters
|
||||
|
||||
These parameters apply to the `kms_library` stanza of type `pkcs11` in the Vault configuration file:
|
||||
|
||||
- `name` `(string: <required>)`: The logical name to be referenced by a managed key
|
||||
- `library` `(string: <required>)`: The path to the PKCS#11 library shared object file.
|
||||
|
||||
For example:
|
||||
|
||||
```hcl
|
||||
kms_library "pkcs11" {
|
||||
name = "hsm1"
|
||||
library = "/usr/lib/Cryptoki.so"
|
||||
}
|
||||
```
|
|
@ -369,6 +369,10 @@
|
|||
{
|
||||
"title": "<code>Entropy Augmentation</code> <sup>ENT</sup>",
|
||||
"path": "configuration/entropy-augmentation"
|
||||
},
|
||||
{
|
||||
"title": "<code>kms_library</code> <sup>ENT</sup>",
|
||||
"path": "configuration/kms-library"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue