mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 11:08:10 +00:00
* Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License. Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at https://hashi.co/bsl-blog, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUS-1.1 * Fix test that expected exact offset on hcl file --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Co-authored-by: Sarah Thompson <sthompson@hashicorp.com> Co-authored-by: Brian Kassouf <bkassouf@hashicorp.com>
2339 lines
69 KiB
Go
2339 lines
69 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package cert
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"math/big"
|
|
mathrand "math/rand"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-test/deep"
|
|
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
|
rootcerts "github.com/hashicorp/go-rootcerts"
|
|
"github.com/hashicorp/go-sockaddr"
|
|
"github.com/hashicorp/vault/api"
|
|
"github.com/hashicorp/vault/builtin/logical/pki"
|
|
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
|
|
vaulthttp "github.com/hashicorp/vault/http"
|
|
"github.com/hashicorp/vault/sdk/framework"
|
|
"github.com/hashicorp/vault/sdk/helper/certutil"
|
|
"github.com/hashicorp/vault/sdk/helper/tokenutil"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
"github.com/hashicorp/vault/vault"
|
|
"github.com/mitchellh/mapstructure"
|
|
"golang.org/x/net/http2"
|
|
)
|
|
|
|
const (
|
|
serverCertPath = "test-fixtures/cacert.pem"
|
|
serverKeyPath = "test-fixtures/cakey.pem"
|
|
serverCAPath = serverCertPath
|
|
|
|
testRootCACertPath1 = "test-fixtures/testcacert1.pem"
|
|
testRootCAKeyPath1 = "test-fixtures/testcakey1.pem"
|
|
testCertPath1 = "test-fixtures/testissuedcert4.pem"
|
|
testKeyPath1 = "test-fixtures/testissuedkey4.pem"
|
|
testIssuedCertCRL = "test-fixtures/issuedcertcrl"
|
|
|
|
testRootCACertPath2 = "test-fixtures/testcacert2.pem"
|
|
testRootCAKeyPath2 = "test-fixtures/testcakey2.pem"
|
|
testRootCertCRL = "test-fixtures/cacert2crl"
|
|
)
|
|
|
|
func generateTestCertAndConnState(t *testing.T, template *x509.Certificate) (string, tls.ConnectionState, error) {
|
|
t.Helper()
|
|
tempDir, err := ioutil.TempDir("", "vault-cert-auth-test-")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Logf("test %s, temp dir %s", t.Name(), tempDir)
|
|
caCertTemplate := &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "localhost",
|
|
},
|
|
DNSNames: []string{"localhost"},
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
KeyUsage: x509.KeyUsage(x509.KeyUsageCertSign | x509.KeyUsageCRLSign),
|
|
SerialNumber: big.NewInt(mathrand.Int63()),
|
|
NotBefore: time.Now().Add(-30 * time.Second),
|
|
NotAfter: time.Now().Add(262980 * time.Hour),
|
|
BasicConstraintsValid: true,
|
|
IsCA: true,
|
|
}
|
|
caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
caBytes, err := x509.CreateCertificate(rand.Reader, caCertTemplate, caCertTemplate, caKey.Public(), caKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
caCert, err := x509.ParseCertificate(caBytes)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
caCertPEMBlock := &pem.Block{
|
|
Type: "CERTIFICATE",
|
|
Bytes: caBytes,
|
|
}
|
|
err = ioutil.WriteFile(filepath.Join(tempDir, "ca_cert.pem"), pem.EncodeToMemory(caCertPEMBlock), 0o755)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
marshaledCAKey, err := x509.MarshalECPrivateKey(caKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
caKeyPEMBlock := &pem.Block{
|
|
Type: "EC PRIVATE KEY",
|
|
Bytes: marshaledCAKey,
|
|
}
|
|
err = ioutil.WriteFile(filepath.Join(tempDir, "ca_key.pem"), pem.EncodeToMemory(caKeyPEMBlock), 0o755)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
certBytes, err := x509.CreateCertificate(rand.Reader, template, caCert, key.Public(), caKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
certPEMBlock := &pem.Block{
|
|
Type: "CERTIFICATE",
|
|
Bytes: certBytes,
|
|
}
|
|
err = ioutil.WriteFile(filepath.Join(tempDir, "cert.pem"), pem.EncodeToMemory(certPEMBlock), 0o755)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
marshaledKey, err := x509.MarshalECPrivateKey(key)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
keyPEMBlock := &pem.Block{
|
|
Type: "EC PRIVATE KEY",
|
|
Bytes: marshaledKey,
|
|
}
|
|
err = ioutil.WriteFile(filepath.Join(tempDir, "key.pem"), pem.EncodeToMemory(keyPEMBlock), 0o755)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
connInfo, err := testConnState(filepath.Join(tempDir, "cert.pem"), filepath.Join(tempDir, "key.pem"), filepath.Join(tempDir, "ca_cert.pem"))
|
|
return tempDir, connInfo, err
|
|
}
|
|
|
|
// Unlike testConnState, this method does not use the same 'tls.Config' objects for
|
|
// both dialing and listening. Instead, it runs the server without specifying its CA.
|
|
// But the client, presents the CA cert of the server to trust the server.
|
|
// The client can present a cert and key which is completely independent of server's CA.
|
|
// The connection state returned will contain the certificate presented by the client.
|
|
func connectionState(serverCAPath, serverCertPath, serverKeyPath, clientCertPath, clientKeyPath string) (tls.ConnectionState, error) {
|
|
serverKeyPair, err := tls.LoadX509KeyPair(serverCertPath, serverKeyPath)
|
|
if err != nil {
|
|
return tls.ConnectionState{}, err
|
|
}
|
|
// Prepare the listener configuration with server's key pair
|
|
listenConf := &tls.Config{
|
|
Certificates: []tls.Certificate{serverKeyPair},
|
|
ClientAuth: tls.RequestClientCert,
|
|
}
|
|
|
|
clientKeyPair, err := tls.LoadX509KeyPair(clientCertPath, clientKeyPath)
|
|
if err != nil {
|
|
return tls.ConnectionState{}, err
|
|
}
|
|
// Load the CA cert required by the client to authenticate the server.
|
|
rootConfig := &rootcerts.Config{
|
|
CAFile: serverCAPath,
|
|
}
|
|
serverCAs, err := rootcerts.LoadCACerts(rootConfig)
|
|
if err != nil {
|
|
return tls.ConnectionState{}, err
|
|
}
|
|
// Prepare the dial configuration that the client uses to establish the connection.
|
|
dialConf := &tls.Config{
|
|
Certificates: []tls.Certificate{clientKeyPair},
|
|
RootCAs: serverCAs,
|
|
}
|
|
|
|
// Start the server.
|
|
list, err := tls.Listen("tcp", "127.0.0.1:0", listenConf)
|
|
if err != nil {
|
|
return tls.ConnectionState{}, err
|
|
}
|
|
defer list.Close()
|
|
|
|
// Accept connections.
|
|
serverErrors := make(chan error, 1)
|
|
connState := make(chan tls.ConnectionState)
|
|
go func() {
|
|
defer close(connState)
|
|
serverConn, err := list.Accept()
|
|
if err != nil {
|
|
serverErrors <- err
|
|
close(serverErrors)
|
|
return
|
|
}
|
|
defer serverConn.Close()
|
|
|
|
// Read the ping
|
|
buf := make([]byte, 4)
|
|
_, err = serverConn.Read(buf)
|
|
if (err != nil) && (err != io.EOF) {
|
|
serverErrors <- err
|
|
close(serverErrors)
|
|
return
|
|
}
|
|
close(serverErrors)
|
|
connState <- serverConn.(*tls.Conn).ConnectionState()
|
|
}()
|
|
|
|
// Establish a connection from the client side and write a few bytes.
|
|
clientErrors := make(chan error, 1)
|
|
go func() {
|
|
addr := list.Addr().String()
|
|
conn, err := tls.Dial("tcp", addr, dialConf)
|
|
if err != nil {
|
|
clientErrors <- err
|
|
close(clientErrors)
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
// Write ping
|
|
_, err = conn.Write([]byte("ping"))
|
|
if err != nil {
|
|
clientErrors <- err
|
|
}
|
|
close(clientErrors)
|
|
}()
|
|
|
|
for err = range clientErrors {
|
|
if err != nil {
|
|
return tls.ConnectionState{}, fmt.Errorf("error in client goroutine:%v", err)
|
|
}
|
|
}
|
|
|
|
for err = range serverErrors {
|
|
if err != nil {
|
|
return tls.ConnectionState{}, fmt.Errorf("error in server goroutine:%v", err)
|
|
}
|
|
}
|
|
// Grab the current state
|
|
return <-connState, nil
|
|
}
|
|
|
|
func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) {
|
|
// Enable PKI secret engine and Cert auth method
|
|
coreConfig := &vault.CoreConfig{
|
|
CredentialBackends: map[string]logical.Factory{
|
|
"cert": Factory,
|
|
},
|
|
LogicalBackends: map[string]logical.Factory{
|
|
"pki": pki.Factory,
|
|
},
|
|
}
|
|
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
|
HandlerFunc: vaulthttp.Handler,
|
|
})
|
|
cluster.Start()
|
|
defer cluster.Cleanup()
|
|
cores := cluster.Cores
|
|
vault.TestWaitActive(t, cores[0].Core)
|
|
client := cores[0].Client
|
|
|
|
var err error
|
|
|
|
// Mount /pki as a root CA
|
|
err = client.Sys().Mount("pki", &api.MountInput{
|
|
Type: "pki",
|
|
Config: api.MountConfigInput{
|
|
DefaultLeaseTTL: "16h",
|
|
MaxLeaseTTL: "32h",
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Set the cluster's certificate as the root CA in /pki
|
|
pemBundleRootCA := string(cluster.CACertPEM) + string(cluster.CAKeyPEM)
|
|
_, err = client.Logical().Write("pki/config/ca", map[string]interface{}{
|
|
"pem_bundle": pemBundleRootCA,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Mount /pki2 to operate as an intermediate CA
|
|
err = client.Sys().Mount("pki2", &api.MountInput{
|
|
Type: "pki",
|
|
Config: api.MountConfigInput{
|
|
DefaultLeaseTTL: "16h",
|
|
MaxLeaseTTL: "32h",
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create a CSR for the intermediate CA
|
|
secret, err := client.Logical().Write("pki2/intermediate/generate/internal", nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
intermediateCSR := secret.Data["csr"].(string)
|
|
|
|
// Sign the intermediate CSR using /pki
|
|
secret, err = client.Logical().Write("pki/root/sign-intermediate", map[string]interface{}{
|
|
"permitted_dns_domains": ".myvault.com",
|
|
"csr": intermediateCSR,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
intermediateCertPEM := secret.Data["certificate"].(string)
|
|
|
|
// Configure the intermediate cert as the CA in /pki2
|
|
_, err = client.Logical().Write("pki2/intermediate/set-signed", map[string]interface{}{
|
|
"certificate": intermediateCertPEM,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create a role on the intermediate CA mount
|
|
_, err = client.Logical().Write("pki2/roles/myvault-dot-com", map[string]interface{}{
|
|
"allowed_domains": "myvault.com",
|
|
"allow_subdomains": "true",
|
|
"max_ttl": "5m",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Issue a leaf cert using the intermediate CA
|
|
secret, err = client.Logical().Write("pki2/issue/myvault-dot-com", map[string]interface{}{
|
|
"common_name": "cert.myvault.com",
|
|
"format": "pem",
|
|
"ip_sans": "127.0.0.1",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
leafCertPEM := secret.Data["certificate"].(string)
|
|
leafCertKeyPEM := secret.Data["private_key"].(string)
|
|
|
|
// Enable the cert auth method
|
|
err = client.Sys().EnableAuthWithOptions("cert", &api.EnableAuthOptions{
|
|
Type: "cert",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Set the intermediate CA cert as a trusted certificate in the backend
|
|
_, err = client.Logical().Write("auth/cert/certs/myvault-dot-com", map[string]interface{}{
|
|
"display_name": "myvault.com",
|
|
"policies": "default",
|
|
"certificate": intermediateCertPEM,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create temporary files for CA cert, client cert and client cert key.
|
|
// This is used to configure TLS in the api client.
|
|
caCertFile, err := ioutil.TempFile("", "caCert")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.Remove(caCertFile.Name())
|
|
if _, err := caCertFile.Write([]byte(cluster.CACertPEM)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := caCertFile.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
leafCertFile, err := ioutil.TempFile("", "leafCert")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.Remove(leafCertFile.Name())
|
|
if _, err := leafCertFile.Write([]byte(leafCertPEM)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := leafCertFile.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
leafCertKeyFile, err := ioutil.TempFile("", "leafCertKey")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.Remove(leafCertKeyFile.Name())
|
|
if _, err := leafCertKeyFile.Write([]byte(leafCertKeyPEM)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := leafCertKeyFile.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// This function is a copy-pasta from the NewTestCluster, with the
|
|
// modification to reconfigure the TLS on the api client with the leaf
|
|
// certificate generated above.
|
|
getAPIClient := func(port int, tlsConfig *tls.Config) *api.Client {
|
|
transport := cleanhttp.DefaultPooledTransport()
|
|
transport.TLSClientConfig = tlsConfig.Clone()
|
|
if err := http2.ConfigureTransport(transport); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
client := &http.Client{
|
|
Transport: transport,
|
|
CheckRedirect: func(*http.Request, []*http.Request) error {
|
|
// This can of course be overridden per-test by using its own client
|
|
return fmt.Errorf("redirects not allowed in these tests")
|
|
},
|
|
}
|
|
config := api.DefaultConfig()
|
|
if config.Error != nil {
|
|
t.Fatal(config.Error)
|
|
}
|
|
config.Address = fmt.Sprintf("https://127.0.0.1:%d", port)
|
|
config.HttpClient = client
|
|
|
|
// Set the above issued certificates as the client certificates
|
|
config.ConfigureTLS(&api.TLSConfig{
|
|
CACert: caCertFile.Name(),
|
|
ClientCert: leafCertFile.Name(),
|
|
ClientKey: leafCertKeyFile.Name(),
|
|
})
|
|
|
|
apiClient, err := api.NewClient(config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return apiClient
|
|
}
|
|
|
|
// Create a new api client with the desired TLS configuration
|
|
newClient := getAPIClient(cores[0].Listeners[0].Address.Port, cores[0].TLSConfig())
|
|
|
|
secret, err = newClient.Logical().Write("auth/cert/login", map[string]interface{}{
|
|
"name": "myvault-dot-com",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if secret.Auth == nil || secret.Auth.ClientToken == "" {
|
|
t.Fatalf("expected a successful authentication")
|
|
}
|
|
|
|
// testing pathLoginRenew for cert auth
|
|
oldAccessor := secret.Auth.Accessor
|
|
newClient.SetToken(client.Token())
|
|
secret, err = newClient.Logical().Write("auth/token/renew-accessor", map[string]interface{}{
|
|
"accessor": secret.Auth.Accessor,
|
|
"increment": 3600,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if secret.Auth == nil || secret.Auth.ClientToken != "" || secret.Auth.LeaseDuration != 3600 || secret.Auth.Accessor != oldAccessor {
|
|
t.Fatalf("unexpected accessor renewal")
|
|
}
|
|
}
|
|
|
|
func TestBackend_MetadataBasedACLPolicy(t *testing.T) {
|
|
// Start cluster with cert auth method enabled
|
|
coreConfig := &vault.CoreConfig{
|
|
CredentialBackends: map[string]logical.Factory{
|
|
"cert": Factory,
|
|
},
|
|
}
|
|
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
|
HandlerFunc: vaulthttp.Handler,
|
|
})
|
|
cluster.Start()
|
|
defer cluster.Cleanup()
|
|
cores := cluster.Cores
|
|
vault.TestWaitActive(t, cores[0].Core)
|
|
client := cores[0].Client
|
|
|
|
var err error
|
|
|
|
// Enable the cert auth method
|
|
err = client.Sys().EnableAuthWithOptions("cert", &api.EnableAuthOptions{
|
|
Type: "cert",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Enable metadata in aliases
|
|
_, err = client.Logical().Write("auth/cert/config", map[string]interface{}{
|
|
"enable_identity_alias_metadata": true,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Retrieve its accessor id
|
|
auths, err := client.Sys().ListAuth()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var accessor string
|
|
|
|
for _, auth := range auths {
|
|
if auth.Type == "cert" {
|
|
accessor = auth.Accessor
|
|
}
|
|
}
|
|
|
|
if accessor == "" {
|
|
t.Fatal("failed to find cert auth accessor")
|
|
}
|
|
|
|
// Write ACL policy
|
|
err = client.Sys().PutPolicy("metadata-based", fmt.Sprintf(`
|
|
path "kv/cn/{{identity.entity.aliases.%s.metadata.common_name}}" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "kv/ext/{{identity.entity.aliases.%s.metadata.2-1-1-1}}" {
|
|
capabilities = ["read"]
|
|
}
|
|
`, accessor, accessor))
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Set the trusted certificate in the backend
|
|
_, err = client.Logical().Write("auth/cert/certs/test", map[string]interface{}{
|
|
"display_name": "test",
|
|
"policies": "metadata-based",
|
|
"certificate": string(ca),
|
|
"allowed_metadata_extensions": "2.1.1.1,1.2.3.45",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// This function is a copy-paste from the NewTestCluster, with the
|
|
// modification to reconfigure the TLS on the api client with a
|
|
// specific client certificate.
|
|
getAPIClient := func(port int, tlsConfig *tls.Config) *api.Client {
|
|
transport := cleanhttp.DefaultPooledTransport()
|
|
transport.TLSClientConfig = tlsConfig.Clone()
|
|
if err := http2.ConfigureTransport(transport); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
client := &http.Client{
|
|
Transport: transport,
|
|
CheckRedirect: func(*http.Request, []*http.Request) error {
|
|
// This can of course be overridden per-test by using its own client
|
|
return fmt.Errorf("redirects not allowed in these tests")
|
|
},
|
|
}
|
|
config := api.DefaultConfig()
|
|
if config.Error != nil {
|
|
t.Fatal(config.Error)
|
|
}
|
|
config.Address = fmt.Sprintf("https://127.0.0.1:%d", port)
|
|
config.HttpClient = client
|
|
|
|
// Set the client certificates
|
|
config.ConfigureTLS(&api.TLSConfig{
|
|
CACertBytes: cluster.CACertPEM,
|
|
ClientCert: "test-fixtures/root/rootcawextcert.pem",
|
|
ClientKey: "test-fixtures/root/rootcawextkey.pem",
|
|
})
|
|
|
|
apiClient, err := api.NewClient(config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return apiClient
|
|
}
|
|
|
|
// Create a new api client with the desired TLS configuration
|
|
newClient := getAPIClient(cores[0].Listeners[0].Address.Port, cores[0].TLSConfig())
|
|
|
|
var secret *api.Secret
|
|
|
|
secret, err = newClient.Logical().Write("auth/cert/login", map[string]interface{}{
|
|
"name": "test",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if secret.Auth == nil || secret.Auth.ClientToken == "" {
|
|
t.Fatalf("expected a successful authentication")
|
|
}
|
|
|
|
// Check paths guarded by ACL policy
|
|
newClient.SetToken(secret.Auth.ClientToken)
|
|
|
|
_, err = newClient.Logical().Read("kv/cn/example.com")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = newClient.Logical().Read("kv/cn/not.example.com")
|
|
if err == nil {
|
|
t.Fatal("expected access denied")
|
|
}
|
|
|
|
_, err = newClient.Logical().Read("kv/ext/A UTF8String Extension")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = newClient.Logical().Read("kv/ext/bar")
|
|
if err == nil {
|
|
t.Fatal("expected access denied")
|
|
}
|
|
}
|
|
|
|
func TestBackend_NonCAExpiry(t *testing.T) {
|
|
var resp *logical.Response
|
|
var err error
|
|
|
|
// Create a self-signed certificate and issue a leaf certificate using the
|
|
// CA cert
|
|
template := &x509.Certificate{
|
|
SerialNumber: big.NewInt(1234),
|
|
Subject: pkix.Name{
|
|
CommonName: "localhost",
|
|
Organization: []string{"hashicorp"},
|
|
OrganizationalUnit: []string{"vault"},
|
|
},
|
|
BasicConstraintsValid: true,
|
|
NotBefore: time.Now().Add(-30 * time.Second),
|
|
NotAfter: time.Now().Add(50 * time.Second),
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
|
KeyUsage: x509.KeyUsage(x509.KeyUsageCertSign | x509.KeyUsageCRLSign),
|
|
}
|
|
|
|
// Set IP SAN
|
|
parsedIP := net.ParseIP("127.0.0.1")
|
|
if parsedIP == nil {
|
|
t.Fatalf("failed to create parsed IP")
|
|
}
|
|
template.IPAddresses = []net.IP{parsedIP}
|
|
|
|
// Private key for CA cert
|
|
caPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Marshalling to be able to create PEM file
|
|
caPrivateKeyBytes := x509.MarshalPKCS1PrivateKey(caPrivateKey)
|
|
|
|
caPublicKey := &caPrivateKey.PublicKey
|
|
|
|
template.IsCA = true
|
|
|
|
caCertBytes, err := x509.CreateCertificate(rand.Reader, template, template, caPublicKey, caPrivateKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
caCert, err := x509.ParseCertificate(caCertBytes)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
parsedCaBundle := &certutil.ParsedCertBundle{
|
|
Certificate: caCert,
|
|
CertificateBytes: caCertBytes,
|
|
PrivateKeyBytes: caPrivateKeyBytes,
|
|
PrivateKeyType: certutil.RSAPrivateKey,
|
|
}
|
|
|
|
caCertBundle, err := parsedCaBundle.ToCertBundle()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
caCertFile, err := ioutil.TempFile("", "caCert")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer os.Remove(caCertFile.Name())
|
|
|
|
if _, err := caCertFile.Write([]byte(caCertBundle.Certificate)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := caCertFile.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
caKeyFile, err := ioutil.TempFile("", "caKey")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer os.Remove(caKeyFile.Name())
|
|
|
|
if _, err := caKeyFile.Write([]byte(caCertBundle.PrivateKey)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := caKeyFile.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Prepare template for non-CA cert
|
|
|
|
template.IsCA = false
|
|
template.SerialNumber = big.NewInt(5678)
|
|
|
|
template.KeyUsage = x509.KeyUsage(x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign)
|
|
issuedPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
issuedPrivateKeyBytes := x509.MarshalPKCS1PrivateKey(issuedPrivateKey)
|
|
|
|
issuedPublicKey := &issuedPrivateKey.PublicKey
|
|
|
|
// Keep a short certificate lifetime so logins can be tested both when
|
|
// cert is valid and when it gets expired
|
|
template.NotBefore = time.Now().Add(-2 * time.Second)
|
|
template.NotAfter = time.Now().Add(3 * time.Second)
|
|
|
|
issuedCertBytes, err := x509.CreateCertificate(rand.Reader, template, caCert, issuedPublicKey, caPrivateKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
issuedCert, err := x509.ParseCertificate(issuedCertBytes)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
parsedIssuedBundle := &certutil.ParsedCertBundle{
|
|
Certificate: issuedCert,
|
|
CertificateBytes: issuedCertBytes,
|
|
PrivateKeyBytes: issuedPrivateKeyBytes,
|
|
PrivateKeyType: certutil.RSAPrivateKey,
|
|
}
|
|
|
|
issuedCertBundle, err := parsedIssuedBundle.ToCertBundle()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
issuedCertFile, err := ioutil.TempFile("", "issuedCert")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer os.Remove(issuedCertFile.Name())
|
|
|
|
if _, err := issuedCertFile.Write([]byte(issuedCertBundle.Certificate)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := issuedCertFile.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
issuedKeyFile, err := ioutil.TempFile("", "issuedKey")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
defer os.Remove(issuedKeyFile.Name())
|
|
|
|
if _, err := issuedKeyFile.Write([]byte(issuedCertBundle.PrivateKey)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := issuedKeyFile.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
config := logical.TestBackendConfig()
|
|
storage := &logical.InmemStorage{}
|
|
config.StorageView = storage
|
|
|
|
b, err := Factory(context.Background(), config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Register the Non-CA certificate of the client key pair
|
|
certData := map[string]interface{}{
|
|
"certificate": issuedCertBundle.Certificate,
|
|
"policies": "abc",
|
|
"display_name": "cert1",
|
|
"ttl": 10000,
|
|
}
|
|
certReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "certs/cert1",
|
|
Storage: storage,
|
|
Data: certData,
|
|
}
|
|
|
|
resp, err = b.HandleRequest(context.Background(), certReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
// Create connection state using the certificates generated
|
|
connState, err := connectionState(caCertFile.Name(), caCertFile.Name(), caKeyFile.Name(), issuedCertFile.Name(), issuedKeyFile.Name())
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state:%v", err)
|
|
}
|
|
|
|
loginReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Storage: storage,
|
|
Path: "login",
|
|
Connection: &logical.Connection{
|
|
ConnState: &connState,
|
|
},
|
|
}
|
|
|
|
// Login when the certificate is still valid. Login should succeed.
|
|
resp, err = b.HandleRequest(context.Background(), loginReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
// Wait until the certificate expires
|
|
time.Sleep(5 * time.Second)
|
|
|
|
// Login attempt after certificate expiry should fail
|
|
resp, err = b.HandleRequest(context.Background(), loginReq)
|
|
if err == nil {
|
|
t.Fatalf("expected error due to expired certificate")
|
|
}
|
|
}
|
|
|
|
func TestBackend_RegisteredNonCA_CRL(t *testing.T) {
|
|
config := logical.TestBackendConfig()
|
|
storage := &logical.InmemStorage{}
|
|
config.StorageView = storage
|
|
|
|
b, err := Factory(context.Background(), config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
nonCACert, err := ioutil.ReadFile(testCertPath1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Register the Non-CA certificate of the client key pair
|
|
certData := map[string]interface{}{
|
|
"certificate": nonCACert,
|
|
"policies": "abc",
|
|
"display_name": "cert1",
|
|
"ttl": 10000,
|
|
}
|
|
certReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "certs/cert1",
|
|
Storage: storage,
|
|
Data: certData,
|
|
}
|
|
|
|
resp, err := b.HandleRequest(context.Background(), certReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
// Connection state is presenting the client Non-CA cert and its key.
|
|
// This is exactly what is registered at the backend.
|
|
connState, err := connectionState(serverCAPath, serverCertPath, serverKeyPath, testCertPath1, testKeyPath1)
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state:%v", err)
|
|
}
|
|
loginReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Storage: storage,
|
|
Path: "login",
|
|
Connection: &logical.Connection{
|
|
ConnState: &connState,
|
|
},
|
|
}
|
|
// Login should succeed.
|
|
resp, err = b.HandleRequest(context.Background(), loginReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
// Register a CRL containing the issued client certificate used above.
|
|
issuedCRL, err := ioutil.ReadFile(testIssuedCertCRL)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
crlData := map[string]interface{}{
|
|
"crl": issuedCRL,
|
|
}
|
|
crlReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Storage: storage,
|
|
Path: "crls/issuedcrl",
|
|
Data: crlData,
|
|
}
|
|
resp, err = b.HandleRequest(context.Background(), crlReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
// Ensure the CRL shows up on a list.
|
|
listReq := &logical.Request{
|
|
Operation: logical.ListOperation,
|
|
Storage: storage,
|
|
Path: "crls",
|
|
Data: map[string]interface{}{},
|
|
}
|
|
resp, err = b.HandleRequest(context.Background(), listReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
if len(resp.Data) != 1 || len(resp.Data["keys"].([]string)) != 1 || resp.Data["keys"].([]string)[0] != "issuedcrl" {
|
|
t.Fatalf("bad listing: resp:%v", resp)
|
|
}
|
|
|
|
// Attempt login with the same connection state but with the CRL registered
|
|
resp, err = b.HandleRequest(context.Background(), loginReq)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil || !resp.IsError() {
|
|
t.Fatalf("expected failure due to revoked certificate")
|
|
}
|
|
}
|
|
|
|
func TestBackend_CRLs(t *testing.T) {
|
|
config := logical.TestBackendConfig()
|
|
storage := &logical.InmemStorage{}
|
|
config.StorageView = storage
|
|
|
|
b, err := Factory(context.Background(), config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
clientCA1, err := ioutil.ReadFile(testRootCACertPath1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Register the CA certificate of the client key pair
|
|
certData := map[string]interface{}{
|
|
"certificate": clientCA1,
|
|
"policies": "abc",
|
|
"display_name": "cert1",
|
|
"ttl": 10000,
|
|
}
|
|
|
|
certReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "certs/cert1",
|
|
Storage: storage,
|
|
Data: certData,
|
|
}
|
|
|
|
resp, err := b.HandleRequest(context.Background(), certReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
// Connection state is presenting the client CA cert and its key.
|
|
// This is exactly what is registered at the backend.
|
|
connState, err := connectionState(serverCAPath, serverCertPath, serverKeyPath, testRootCACertPath1, testRootCAKeyPath1)
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state:%v", err)
|
|
}
|
|
loginReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Storage: storage,
|
|
Path: "login",
|
|
Connection: &logical.Connection{
|
|
ConnState: &connState,
|
|
},
|
|
}
|
|
resp, err = b.HandleRequest(context.Background(), loginReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
// Now, without changing the registered client CA cert, present from
|
|
// the client side, a cert issued using the registered CA.
|
|
connState, err = connectionState(serverCAPath, serverCertPath, serverKeyPath, testCertPath1, testKeyPath1)
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
loginReq.Connection.ConnState = &connState
|
|
|
|
// Attempt login with the updated connection
|
|
resp, err = b.HandleRequest(context.Background(), loginReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
// Register a CRL containing the issued client certificate used above.
|
|
issuedCRL, err := ioutil.ReadFile(testIssuedCertCRL)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
crlData := map[string]interface{}{
|
|
"crl": issuedCRL,
|
|
}
|
|
|
|
crlReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Storage: storage,
|
|
Path: "crls/issuedcrl",
|
|
Data: crlData,
|
|
}
|
|
resp, err = b.HandleRequest(context.Background(), crlReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
// Attempt login with the revoked certificate.
|
|
resp, err = b.HandleRequest(context.Background(), loginReq)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil || !resp.IsError() {
|
|
t.Fatalf("expected failure due to revoked certificate")
|
|
}
|
|
|
|
// Register a different client CA certificate.
|
|
clientCA2, err := ioutil.ReadFile(testRootCACertPath2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
certData["certificate"] = clientCA2
|
|
resp, err = b.HandleRequest(context.Background(), certReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
// Test login using a different client CA cert pair.
|
|
connState, err = connectionState(serverCAPath, serverCertPath, serverKeyPath, testRootCACertPath2, testRootCAKeyPath2)
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
loginReq.Connection.ConnState = &connState
|
|
|
|
// Attempt login with the updated connection
|
|
resp, err = b.HandleRequest(context.Background(), loginReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
// Register a CRL containing the root CA certificate used above.
|
|
rootCRL, err := ioutil.ReadFile(testRootCertCRL)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
crlData["crl"] = rootCRL
|
|
resp, err = b.HandleRequest(context.Background(), crlReq)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
}
|
|
|
|
// Attempt login with the same connection state but with the CRL registered
|
|
resp, err = b.HandleRequest(context.Background(), loginReq)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil || !resp.IsError() {
|
|
t.Fatalf("expected failure due to revoked certificate")
|
|
}
|
|
}
|
|
|
|
func testFactory(t *testing.T) logical.Backend {
|
|
storage := &logical.InmemStorage{}
|
|
b, err := Factory(context.Background(), &logical.BackendConfig{
|
|
System: &logical.StaticSystemView{
|
|
DefaultLeaseTTLVal: 1000 * time.Second,
|
|
MaxLeaseTTLVal: 1800 * time.Second,
|
|
},
|
|
StorageView: storage,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("error: %s", err)
|
|
}
|
|
if err := b.Initialize(context.Background(), &logical.InitializationRequest{
|
|
Storage: storage,
|
|
}); err != nil {
|
|
t.Fatalf("error: %s", err)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// Test the certificates being registered to the backend
|
|
func TestBackend_CertWrites(t *testing.T) {
|
|
// CA cert
|
|
ca1, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
// Non CA Cert
|
|
ca2, err := ioutil.ReadFile("test-fixtures/keys/cert.pem")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
// Non CA cert without TLS web client authentication
|
|
ca3, err := ioutil.ReadFile("test-fixtures/noclientauthcert.pem")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
tc := logicaltest.TestCase{
|
|
CredentialBackend: testFactory(t),
|
|
Steps: []logicaltest.TestStep{
|
|
testAccStepCert(t, "aaa", ca1, "foo", allowed{}, false),
|
|
testAccStepCert(t, "bbb", ca2, "foo", allowed{}, false),
|
|
testAccStepCert(t, "ccc", ca3, "foo", allowed{}, true),
|
|
},
|
|
}
|
|
tc.Steps = append(tc.Steps, testAccStepListCerts(t, []string{"aaa", "bbb"})...)
|
|
logicaltest.Test(t, tc)
|
|
}
|
|
|
|
// Test a client trusted by a CA
|
|
func TestBackend_basic_CA(t *testing.T) {
|
|
connState, err := testConnState("test-fixtures/keys/cert.pem",
|
|
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
|
CredentialBackend: testFactory(t),
|
|
Steps: []logicaltest.TestStep{
|
|
testAccStepCert(t, "web", ca, "foo", allowed{}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCertLease(t, "web", ca, "foo"),
|
|
testAccStepCertTTL(t, "web", ca, "foo"),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCertMaxTTL(t, "web", ca, "foo"),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCertNoLease(t, "web", ca, "foo"),
|
|
testAccStepLoginDefaultLease(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "*.example.com"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "*.invalid.com"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
},
|
|
})
|
|
}
|
|
|
|
// Test CRL behavior
|
|
func TestBackend_Basic_CRLs(t *testing.T) {
|
|
connState, err := testConnState("test-fixtures/keys/cert.pem",
|
|
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
crl, err := ioutil.ReadFile("test-fixtures/root/root.crl")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
|
CredentialBackend: testFactory(t),
|
|
Steps: []logicaltest.TestStep{
|
|
testAccStepCertNoLease(t, "web", ca, "foo"),
|
|
testAccStepLoginDefaultLease(t, connState),
|
|
testAccStepAddCRL(t, crl, connState),
|
|
testAccStepReadCRL(t, connState),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepDeleteCRL(t, connState),
|
|
testAccStepLoginDefaultLease(t, connState),
|
|
},
|
|
})
|
|
}
|
|
|
|
// Test a self-signed client (root CA) that is trusted
|
|
func TestBackend_basic_singleCert(t *testing.T) {
|
|
connState, err := testConnState("test-fixtures/root/rootcacert.pem",
|
|
"test-fixtures/root/rootcakey.pem", "test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
|
CredentialBackend: testFactory(t),
|
|
Steps: []logicaltest.TestStep{
|
|
testAccStepCert(t, "web", ca, "foo", allowed{}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "invalid"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{ext: "1.2.3.4:invalid"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestBackend_common_name_singleCert(t *testing.T) {
|
|
connState, err := testConnState("test-fixtures/root/rootcacert.pem",
|
|
"test-fixtures/root/rootcakey.pem", "test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
|
CredentialBackend: testFactory(t),
|
|
Steps: []logicaltest.TestStep{
|
|
testAccStepCert(t, "web", ca, "foo", allowed{}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{common_names: "example.com"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{common_names: "invalid"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{ext: "1.2.3.4:invalid"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
},
|
|
})
|
|
}
|
|
|
|
// Test a self-signed client with custom ext (root CA) that is trusted
|
|
func TestBackend_ext_singleCert(t *testing.T) {
|
|
connState, err := testConnState(
|
|
"test-fixtures/root/rootcawextcert.pem",
|
|
"test-fixtures/root/rootcawextkey.pem",
|
|
"test-fixtures/root/rootcacert.pem",
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
|
CredentialBackend: testFactory(t),
|
|
Steps: []logicaltest.TestStep{
|
|
testAccStepCert(t, "web", ca, "foo", allowed{ext: "2.1.1.1:A UTF8String Extension"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{ext: "2.1.1.1:*,2.1.1.2:A UTF8*"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{ext: "1.2.3.45:*"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{ext: "2.1.1.1:The Wrong Value"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{ext: "2.1.1.1:*,2.1.1.2:The Wrong Value"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{ext: "2.1.1.1:"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{ext: "2.1.1.1:,2.1.1.2:*"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com", ext: "2.1.1.1:A UTF8String Extension"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com", ext: "2.1.1.1:*,2.1.1.2:A UTF8*"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com", ext: "1.2.3.45:*"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com", ext: "2.1.1.1:The Wrong Value"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com", ext: "2.1.1.1:*,2.1.1.2:The Wrong Value"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "invalid", ext: "2.1.1.1:A UTF8String Extension"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "invalid", ext: "2.1.1.1:*,2.1.1.2:A UTF8*"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "invalid", ext: "1.2.3.45:*"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "invalid", ext: "2.1.1.1:The Wrong Value"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "invalid", ext: "2.1.1.1:*,2.1.1.2:The Wrong Value"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com", ext: "hex:2.5.29.17:*87047F000002*"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com", ext: "hex:2.5.29.17:*87047F000001*"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com", ext: "2.5.29.17:"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepReadConfig(t, config{EnableIdentityAliasMetadata: false}, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{metadata_ext: "2.1.1.1,1.2.3.45"}, false),
|
|
testAccStepLoginWithMetadata(t, connState, "web", map[string]string{"2-1-1-1": "A UTF8String Extension"}, false),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{metadata_ext: "1.2.3.45"}, false),
|
|
testAccStepLoginWithMetadata(t, connState, "web", map[string]string{}, false),
|
|
testAccStepSetConfig(t, config{EnableIdentityAliasMetadata: true}, connState),
|
|
testAccStepReadConfig(t, config{EnableIdentityAliasMetadata: true}, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{metadata_ext: "2.1.1.1,1.2.3.45"}, false),
|
|
testAccStepLoginWithMetadata(t, connState, "web", map[string]string{"2-1-1-1": "A UTF8String Extension"}, true),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{metadata_ext: "1.2.3.45"}, false),
|
|
testAccStepLoginWithMetadata(t, connState, "web", map[string]string{}, true),
|
|
},
|
|
})
|
|
}
|
|
|
|
// Test a self-signed client with URI alt names (root CA) that is trusted
|
|
func TestBackend_dns_singleCert(t *testing.T) {
|
|
certTemplate := &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "example.com",
|
|
},
|
|
DNSNames: []string{"example.com"},
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
|
x509.ExtKeyUsageServerAuth,
|
|
x509.ExtKeyUsageClientAuth,
|
|
},
|
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement,
|
|
SerialNumber: big.NewInt(mathrand.Int63()),
|
|
NotBefore: time.Now().Add(-30 * time.Second),
|
|
NotAfter: time.Now().Add(262980 * time.Hour),
|
|
}
|
|
|
|
tempDir, connState, err := generateTestCertAndConnState(t, certTemplate)
|
|
if tempDir != "" {
|
|
defer os.RemoveAll(tempDir)
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
ca, err := ioutil.ReadFile(filepath.Join(tempDir, "ca_cert.pem"))
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
|
CredentialBackend: testFactory(t),
|
|
Steps: []logicaltest.TestStep{
|
|
testAccStepCert(t, "web", ca, "foo", allowed{dns: "example.com"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{dns: "*ample.com"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{dns: "notincert.com"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{dns: "abc"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{dns: "*.example.com"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
},
|
|
})
|
|
}
|
|
|
|
// Test a self-signed client with URI alt names (root CA) that is trusted
|
|
func TestBackend_email_singleCert(t *testing.T) {
|
|
certTemplate := &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "example.com",
|
|
},
|
|
EmailAddresses: []string{"valid@example.com"},
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
|
x509.ExtKeyUsageServerAuth,
|
|
x509.ExtKeyUsageClientAuth,
|
|
},
|
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement,
|
|
SerialNumber: big.NewInt(mathrand.Int63()),
|
|
NotBefore: time.Now().Add(-30 * time.Second),
|
|
NotAfter: time.Now().Add(262980 * time.Hour),
|
|
}
|
|
|
|
tempDir, connState, err := generateTestCertAndConnState(t, certTemplate)
|
|
if tempDir != "" {
|
|
defer os.RemoveAll(tempDir)
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
ca, err := ioutil.ReadFile(filepath.Join(tempDir, "ca_cert.pem"))
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
|
CredentialBackend: testFactory(t),
|
|
Steps: []logicaltest.TestStep{
|
|
testAccStepCert(t, "web", ca, "foo", allowed{emails: "valid@example.com"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{emails: "*@example.com"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{emails: "invalid@notincert.com"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{emails: "abc"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{emails: "*.example.com"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
},
|
|
})
|
|
}
|
|
|
|
// Test a self-signed client with OU (root CA) that is trusted
|
|
func TestBackend_organizationalUnit_singleCert(t *testing.T) {
|
|
connState, err := testConnState(
|
|
"test-fixtures/root/rootcawoucert.pem",
|
|
"test-fixtures/root/rootcawoukey.pem",
|
|
"test-fixtures/root/rootcawoucert.pem",
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcawoucert.pem")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
|
CredentialBackend: testFactory(t),
|
|
Steps: []logicaltest.TestStep{
|
|
testAccStepCert(t, "web", ca, "foo", allowed{organizational_units: "engineering"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{organizational_units: "eng*"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{organizational_units: "engineering,finance"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{organizational_units: "foo"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
},
|
|
})
|
|
}
|
|
|
|
// Test a self-signed client with URI alt names (root CA) that is trusted
|
|
func TestBackend_uri_singleCert(t *testing.T) {
|
|
u, err := url.Parse("spiffe://example.com/host")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
certTemplate := &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: "example.com",
|
|
},
|
|
DNSNames: []string{"example.com"},
|
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
|
URIs: []*url.URL{u},
|
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
|
x509.ExtKeyUsageServerAuth,
|
|
x509.ExtKeyUsageClientAuth,
|
|
},
|
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement,
|
|
SerialNumber: big.NewInt(mathrand.Int63()),
|
|
NotBefore: time.Now().Add(-30 * time.Second),
|
|
NotAfter: time.Now().Add(262980 * time.Hour),
|
|
}
|
|
|
|
tempDir, connState, err := generateTestCertAndConnState(t, certTemplate)
|
|
if tempDir != "" {
|
|
defer os.RemoveAll(tempDir)
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
ca, err := ioutil.ReadFile(filepath.Join(tempDir, "ca_cert.pem"))
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
|
CredentialBackend: testFactory(t),
|
|
Steps: []logicaltest.TestStep{
|
|
testAccStepCert(t, "web", ca, "foo", allowed{uris: "spiffe://example.com/*"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{uris: "spiffe://example.com/host"}, false),
|
|
testAccStepLogin(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{uris: "spiffe://example.com/invalid"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{uris: "abc"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
testAccStepCert(t, "web", ca, "foo", allowed{uris: "http://www.google.com"}, false),
|
|
testAccStepLoginInvalid(t, connState),
|
|
},
|
|
})
|
|
}
|
|
|
|
// Test against a collection of matching and non-matching rules
|
|
func TestBackend_mixed_constraints(t *testing.T) {
|
|
connState, err := testConnState("test-fixtures/keys/cert.pem",
|
|
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
|
CredentialBackend: testFactory(t),
|
|
Steps: []logicaltest.TestStep{
|
|
testAccStepCert(t, "1unconstrained", ca, "foo", allowed{}, false),
|
|
testAccStepCert(t, "2matching", ca, "foo", allowed{names: "*.example.com,whatever"}, false),
|
|
testAccStepCert(t, "3invalid", ca, "foo", allowed{names: "invalid"}, false),
|
|
testAccStepLogin(t, connState),
|
|
// Assumes CertEntries are processed in alphabetical order (due to store.List), so we only match 2matching if 1unconstrained doesn't match
|
|
testAccStepLoginWithName(t, connState, "2matching"),
|
|
testAccStepLoginWithNameInvalid(t, connState, "3invalid"),
|
|
},
|
|
})
|
|
}
|
|
|
|
// Test an untrusted client
|
|
func TestBackend_untrusted(t *testing.T) {
|
|
connState, err := testConnState("test-fixtures/keys/cert.pem",
|
|
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
|
CredentialBackend: testFactory(t),
|
|
Steps: []logicaltest.TestStep{
|
|
testAccStepLoginInvalid(t, connState),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestBackend_validCIDR(t *testing.T) {
|
|
config := logical.TestBackendConfig()
|
|
storage := &logical.InmemStorage{}
|
|
config.StorageView = storage
|
|
|
|
b, err := Factory(context.Background(), config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
connState, err := testConnState("test-fixtures/keys/cert.pem",
|
|
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
name := "web"
|
|
boundCIDRs := []string{"127.0.0.1", "128.252.0.0/16"}
|
|
|
|
addCertReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "certs/" + name,
|
|
Data: map[string]interface{}{
|
|
"certificate": string(ca),
|
|
"policies": "foo",
|
|
"display_name": name,
|
|
"allowed_names": "",
|
|
"required_extensions": "",
|
|
"lease": 1000,
|
|
"bound_cidrs": boundCIDRs,
|
|
},
|
|
Storage: storage,
|
|
Connection: &logical.Connection{ConnState: &connState},
|
|
}
|
|
|
|
_, err = b.HandleRequest(context.Background(), addCertReq)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
readCertReq := &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "certs/" + name,
|
|
Storage: storage,
|
|
Connection: &logical.Connection{ConnState: &connState},
|
|
}
|
|
|
|
readResult, err := b.HandleRequest(context.Background(), readCertReq)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cidrsResult := readResult.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)
|
|
|
|
if cidrsResult[0].String() != boundCIDRs[0] ||
|
|
cidrsResult[1].String() != boundCIDRs[1] {
|
|
t.Fatalf("bound_cidrs couldn't be set correctly, EXPECTED: %v, ACTUAL: %v", boundCIDRs, cidrsResult)
|
|
}
|
|
|
|
loginReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "login",
|
|
Unauthenticated: true,
|
|
Data: map[string]interface{}{
|
|
"name": name,
|
|
},
|
|
Storage: storage,
|
|
Connection: &logical.Connection{ConnState: &connState},
|
|
}
|
|
|
|
// override the remote address with an IPV4 that is authorized
|
|
loginReq.Connection.RemoteAddr = "127.0.0.1/32"
|
|
|
|
_, err = b.HandleRequest(context.Background(), loginReq)
|
|
if err != nil {
|
|
t.Fatal(err.Error())
|
|
}
|
|
}
|
|
|
|
func TestBackend_invalidCIDR(t *testing.T) {
|
|
config := logical.TestBackendConfig()
|
|
storage := &logical.InmemStorage{}
|
|
config.StorageView = storage
|
|
|
|
b, err := Factory(context.Background(), config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
connState, err := testConnState("test-fixtures/keys/cert.pem",
|
|
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
name := "web"
|
|
|
|
addCertReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "certs/" + name,
|
|
Data: map[string]interface{}{
|
|
"certificate": string(ca),
|
|
"policies": "foo",
|
|
"display_name": name,
|
|
"allowed_names": "",
|
|
"required_extensions": "",
|
|
"lease": 1000,
|
|
"bound_cidrs": []string{"127.0.0.1/32", "128.252.0.0/16"},
|
|
},
|
|
Storage: storage,
|
|
Connection: &logical.Connection{ConnState: &connState},
|
|
}
|
|
|
|
_, err = b.HandleRequest(context.Background(), addCertReq)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
loginReq := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "login",
|
|
Unauthenticated: true,
|
|
Data: map[string]interface{}{
|
|
"name": name,
|
|
},
|
|
Storage: storage,
|
|
Connection: &logical.Connection{ConnState: &connState},
|
|
}
|
|
|
|
// override the remote address with an IPV4 that isn't authorized
|
|
loginReq.Connection.RemoteAddr = "127.0.0.1/8"
|
|
|
|
_, err = b.HandleRequest(context.Background(), loginReq)
|
|
if err == nil {
|
|
t.Fatal("expected \"ERROR: permission denied\"")
|
|
}
|
|
}
|
|
|
|
func testAccStepAddCRL(t *testing.T, crl []byte, connState tls.ConnectionState) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "crls/test",
|
|
ConnState: &connState,
|
|
Data: map[string]interface{}{
|
|
"crl": crl,
|
|
},
|
|
}
|
|
}
|
|
|
|
func testAccStepReadCRL(t *testing.T, connState tls.ConnectionState) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.ReadOperation,
|
|
Path: "crls/test",
|
|
ConnState: &connState,
|
|
Check: func(resp *logical.Response) error {
|
|
crlInfo := CRLInfo{}
|
|
err := mapstructure.Decode(resp.Data, &crlInfo)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if len(crlInfo.Serials) != 1 {
|
|
t.Fatalf("bad: expected CRL with length 1, got %d", len(crlInfo.Serials))
|
|
}
|
|
if _, ok := crlInfo.Serials["637101449987587619778072672905061040630001617053"]; !ok {
|
|
t.Fatalf("bad: expected serial number not found in CRL")
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func testAccStepDeleteCRL(t *testing.T, connState tls.ConnectionState) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.DeleteOperation,
|
|
Path: "crls/test",
|
|
ConnState: &connState,
|
|
}
|
|
}
|
|
|
|
func testAccStepSetConfig(t *testing.T, conf config, connState tls.ConnectionState) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "config",
|
|
ConnState: &connState,
|
|
Data: map[string]interface{}{
|
|
"enable_identity_alias_metadata": conf.EnableIdentityAliasMetadata,
|
|
},
|
|
}
|
|
}
|
|
|
|
func testAccStepReadConfig(t *testing.T, conf config, connState tls.ConnectionState) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.ReadOperation,
|
|
Path: "config",
|
|
ConnState: &connState,
|
|
Check: func(resp *logical.Response) error {
|
|
value, ok := resp.Data["enable_identity_alias_metadata"]
|
|
if !ok {
|
|
t.Fatalf("enable_identity_alias_metadata not found in response")
|
|
}
|
|
|
|
b, ok := value.(bool)
|
|
if !ok {
|
|
t.Fatalf("bad: expected enable_identity_alias_metadata to be a bool")
|
|
}
|
|
|
|
if b != conf.EnableIdentityAliasMetadata {
|
|
t.Fatalf("bad: expected enable_identity_alias_metadata to be %t, got %t", conf.EnableIdentityAliasMetadata, b)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func testAccStepLogin(t *testing.T, connState tls.ConnectionState) logicaltest.TestStep {
|
|
return testAccStepLoginWithName(t, connState, "")
|
|
}
|
|
|
|
func testAccStepLoginWithName(t *testing.T, connState tls.ConnectionState, certName string) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "login",
|
|
Unauthenticated: true,
|
|
ConnState: &connState,
|
|
Check: func(resp *logical.Response) error {
|
|
if resp.Auth.TTL != 1000*time.Second {
|
|
t.Fatalf("bad lease length: %#v", resp.Auth)
|
|
}
|
|
|
|
if certName != "" && resp.Auth.DisplayName != ("mnt-"+certName) {
|
|
t.Fatalf("matched the wrong cert: %#v", resp.Auth.DisplayName)
|
|
}
|
|
|
|
fn := logicaltest.TestCheckAuth([]string{"default", "foo"})
|
|
return fn(resp)
|
|
},
|
|
Data: map[string]interface{}{
|
|
"name": certName,
|
|
},
|
|
}
|
|
}
|
|
|
|
func testAccStepLoginDefaultLease(t *testing.T, connState tls.ConnectionState) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "login",
|
|
Unauthenticated: true,
|
|
ConnState: &connState,
|
|
Check: func(resp *logical.Response) error {
|
|
if resp.Auth.TTL != 1000*time.Second {
|
|
t.Fatalf("bad lease length: %#v", resp.Auth)
|
|
}
|
|
|
|
fn := logicaltest.TestCheckAuth([]string{"default", "foo"})
|
|
return fn(resp)
|
|
},
|
|
}
|
|
}
|
|
|
|
func testAccStepLoginWithMetadata(t *testing.T, connState tls.ConnectionState, certName string, metadata map[string]string, expectAliasMetadata bool) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "login",
|
|
Unauthenticated: true,
|
|
ConnState: &connState,
|
|
Check: func(resp *logical.Response) error {
|
|
// Check for fixed metadata too
|
|
metadata["cert_name"] = certName
|
|
metadata["common_name"] = connState.PeerCertificates[0].Subject.CommonName
|
|
metadata["serial_number"] = connState.PeerCertificates[0].SerialNumber.String()
|
|
metadata["subject_key_id"] = certutil.GetHexFormatted(connState.PeerCertificates[0].SubjectKeyId, ":")
|
|
metadata["authority_key_id"] = certutil.GetHexFormatted(connState.PeerCertificates[0].AuthorityKeyId, ":")
|
|
|
|
for key, expected := range metadata {
|
|
value, ok := resp.Auth.Metadata[key]
|
|
if !ok {
|
|
t.Fatalf("missing metadata key: %s", key)
|
|
}
|
|
|
|
if value != expected {
|
|
t.Fatalf("expected metadata key %s to equal %s, but got: %s", key, expected, value)
|
|
}
|
|
|
|
if expectAliasMetadata {
|
|
value, ok = resp.Auth.Alias.Metadata[key]
|
|
if !ok {
|
|
t.Fatalf("missing alias metadata key: %s", key)
|
|
}
|
|
|
|
if value != expected {
|
|
t.Fatalf("expected metadata key %s to equal %s, but got: %s", key, expected, value)
|
|
}
|
|
} else {
|
|
if len(resp.Auth.Alias.Metadata) > 0 {
|
|
t.Fatal("found alias metadata keys, but should not have any")
|
|
}
|
|
}
|
|
}
|
|
|
|
fn := logicaltest.TestCheckAuth([]string{"default", "foo"})
|
|
return fn(resp)
|
|
},
|
|
Data: map[string]interface{}{
|
|
"metadata": metadata,
|
|
},
|
|
}
|
|
}
|
|
|
|
func testAccStepLoginInvalid(t *testing.T, connState tls.ConnectionState) logicaltest.TestStep {
|
|
return testAccStepLoginWithNameInvalid(t, connState, "")
|
|
}
|
|
|
|
func testAccStepLoginWithNameInvalid(t *testing.T, connState tls.ConnectionState, certName string) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "login",
|
|
Unauthenticated: true,
|
|
ConnState: &connState,
|
|
Check: func(resp *logical.Response) error {
|
|
if resp.Auth != nil {
|
|
return fmt.Errorf("should not be authorized: %#v", resp)
|
|
}
|
|
return nil
|
|
},
|
|
Data: map[string]interface{}{
|
|
"name": certName,
|
|
},
|
|
ErrorOk: true,
|
|
}
|
|
}
|
|
|
|
func testAccStepListCerts(
|
|
t *testing.T, certs []string,
|
|
) []logicaltest.TestStep {
|
|
return []logicaltest.TestStep{
|
|
{
|
|
Operation: logical.ListOperation,
|
|
Path: "certs",
|
|
Check: func(resp *logical.Response) error {
|
|
if resp == nil {
|
|
return fmt.Errorf("nil response")
|
|
}
|
|
if resp.Data == nil {
|
|
return fmt.Errorf("nil data")
|
|
}
|
|
if resp.Data["keys"] == interface{}(nil) {
|
|
return fmt.Errorf("nil keys")
|
|
}
|
|
keys := resp.Data["keys"].([]string)
|
|
if !reflect.DeepEqual(keys, certs) {
|
|
return fmt.Errorf("mismatch: keys is %#v, certs is %#v", keys, certs)
|
|
}
|
|
return nil
|
|
},
|
|
}, {
|
|
Operation: logical.ListOperation,
|
|
Path: "certs/",
|
|
Check: func(resp *logical.Response) error {
|
|
if resp == nil {
|
|
return fmt.Errorf("nil response")
|
|
}
|
|
if resp.Data == nil {
|
|
return fmt.Errorf("nil data")
|
|
}
|
|
if resp.Data["keys"] == interface{}(nil) {
|
|
return fmt.Errorf("nil keys")
|
|
}
|
|
keys := resp.Data["keys"].([]string)
|
|
if !reflect.DeepEqual(keys, certs) {
|
|
return fmt.Errorf("mismatch: keys is %#v, certs is %#v", keys, certs)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
type allowed struct {
|
|
names string // allowed names in the certificate, looks at common, name, dns, email [depricated]
|
|
common_names string // allowed common names in the certificate
|
|
dns string // allowed dns names in the SAN extension of the certificate
|
|
emails string // allowed email names in SAN extension of the certificate
|
|
uris string // allowed uris in SAN extension of the certificate
|
|
organizational_units string // allowed OUs in the certificate
|
|
ext string // required extensions in the certificate
|
|
metadata_ext string // allowed metadata extensions to add to identity alias
|
|
}
|
|
|
|
func testAccStepCert(t *testing.T, name string, cert []byte, policies string, testData allowed, expectError bool) logicaltest.TestStep {
|
|
return testAccStepCertWithExtraParams(t, name, cert, policies, testData, expectError, nil)
|
|
}
|
|
|
|
func testAccStepCertWithExtraParams(t *testing.T, name string, cert []byte, policies string, testData allowed, expectError bool, extraParams map[string]interface{}) logicaltest.TestStep {
|
|
data := map[string]interface{}{
|
|
"certificate": string(cert),
|
|
"policies": policies,
|
|
"display_name": name,
|
|
"allowed_names": testData.names,
|
|
"allowed_common_names": testData.common_names,
|
|
"allowed_dns_sans": testData.dns,
|
|
"allowed_email_sans": testData.emails,
|
|
"allowed_uri_sans": testData.uris,
|
|
"allowed_organizational_units": testData.organizational_units,
|
|
"required_extensions": testData.ext,
|
|
"allowed_metadata_extensions": testData.metadata_ext,
|
|
"lease": 1000,
|
|
}
|
|
for k, v := range extraParams {
|
|
data[k] = v
|
|
}
|
|
return logicaltest.TestStep{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "certs/" + name,
|
|
ErrorOk: expectError,
|
|
Data: data,
|
|
Check: func(resp *logical.Response) error {
|
|
if resp == nil && expectError {
|
|
return fmt.Errorf("expected error but received nil")
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func testAccStepReadCertPolicy(t *testing.T, name string, expectError bool, expected map[string]interface{}) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.ReadOperation,
|
|
Path: "certs/" + name,
|
|
ErrorOk: expectError,
|
|
Data: nil,
|
|
Check: func(resp *logical.Response) error {
|
|
if (resp == nil || len(resp.Data) == 0) && expectError {
|
|
return fmt.Errorf("expected error but received nil")
|
|
}
|
|
for key, expectedValue := range expected {
|
|
actualValue := resp.Data[key]
|
|
if expectedValue != actualValue {
|
|
return fmt.Errorf("Expected to get [%v]=[%v] but read [%v]=[%v] from server for certs/%v: %v", key, expectedValue, key, actualValue, name, resp)
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func testAccStepCertLease(
|
|
t *testing.T, name string, cert []byte, policies string,
|
|
) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "certs/" + name,
|
|
Data: map[string]interface{}{
|
|
"certificate": string(cert),
|
|
"policies": policies,
|
|
"display_name": name,
|
|
"lease": 1000,
|
|
},
|
|
}
|
|
}
|
|
|
|
func testAccStepCertTTL(
|
|
t *testing.T, name string, cert []byte, policies string,
|
|
) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "certs/" + name,
|
|
Data: map[string]interface{}{
|
|
"certificate": string(cert),
|
|
"policies": policies,
|
|
"display_name": name,
|
|
"ttl": "1000s",
|
|
},
|
|
}
|
|
}
|
|
|
|
func testAccStepCertMaxTTL(
|
|
t *testing.T, name string, cert []byte, policies string,
|
|
) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "certs/" + name,
|
|
Data: map[string]interface{}{
|
|
"certificate": string(cert),
|
|
"policies": policies,
|
|
"display_name": name,
|
|
"ttl": "1000s",
|
|
"max_ttl": "1200s",
|
|
},
|
|
}
|
|
}
|
|
|
|
func testAccStepCertNoLease(
|
|
t *testing.T, name string, cert []byte, policies string,
|
|
) logicaltest.TestStep {
|
|
return logicaltest.TestStep{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "certs/" + name,
|
|
Data: map[string]interface{}{
|
|
"certificate": string(cert),
|
|
"policies": policies,
|
|
"display_name": name,
|
|
},
|
|
}
|
|
}
|
|
|
|
func testConnState(certPath, keyPath, rootCertPath string) (tls.ConnectionState, error) {
|
|
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
|
if err != nil {
|
|
return tls.ConnectionState{}, err
|
|
}
|
|
rootConfig := &rootcerts.Config{
|
|
CAFile: rootCertPath,
|
|
}
|
|
rootCAs, err := rootcerts.LoadCACerts(rootConfig)
|
|
if err != nil {
|
|
return tls.ConnectionState{}, err
|
|
}
|
|
listenConf := &tls.Config{
|
|
Certificates: []tls.Certificate{cert},
|
|
ClientAuth: tls.RequestClientCert,
|
|
InsecureSkipVerify: false,
|
|
RootCAs: rootCAs,
|
|
}
|
|
dialConf := listenConf.Clone()
|
|
// start a server
|
|
list, err := tls.Listen("tcp", "127.0.0.1:0", listenConf)
|
|
if err != nil {
|
|
return tls.ConnectionState{}, err
|
|
}
|
|
defer list.Close()
|
|
|
|
// Accept connections.
|
|
serverErrors := make(chan error, 1)
|
|
connState := make(chan tls.ConnectionState)
|
|
go func() {
|
|
defer close(connState)
|
|
serverConn, err := list.Accept()
|
|
serverErrors <- err
|
|
if err != nil {
|
|
close(serverErrors)
|
|
return
|
|
}
|
|
defer serverConn.Close()
|
|
|
|
// Read the ping
|
|
buf := make([]byte, 4)
|
|
_, err = serverConn.Read(buf)
|
|
if (err != nil) && (err != io.EOF) {
|
|
serverErrors <- err
|
|
close(serverErrors)
|
|
return
|
|
} else {
|
|
// EOF is a reasonable error condition, so swallow it.
|
|
serverErrors <- nil
|
|
}
|
|
close(serverErrors)
|
|
connState <- serverConn.(*tls.Conn).ConnectionState()
|
|
}()
|
|
|
|
// Establish a connection from the client side and write a few bytes.
|
|
clientErrors := make(chan error, 1)
|
|
go func() {
|
|
addr := list.Addr().String()
|
|
conn, err := tls.Dial("tcp", addr, dialConf)
|
|
clientErrors <- err
|
|
if err != nil {
|
|
close(clientErrors)
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
// Write ping
|
|
_, err = conn.Write([]byte("ping"))
|
|
clientErrors <- err
|
|
close(clientErrors)
|
|
}()
|
|
|
|
for err = range clientErrors {
|
|
if err != nil {
|
|
return tls.ConnectionState{}, fmt.Errorf("error in client goroutine:%v", err)
|
|
}
|
|
}
|
|
|
|
for err = range serverErrors {
|
|
if err != nil {
|
|
return tls.ConnectionState{}, fmt.Errorf("error in server goroutine:%v", err)
|
|
}
|
|
}
|
|
// Grab the current state
|
|
return <-connState, nil
|
|
}
|
|
|
|
func Test_Renew(t *testing.T) {
|
|
storage := &logical.InmemStorage{}
|
|
|
|
lb, err := Factory(context.Background(), &logical.BackendConfig{
|
|
System: &logical.StaticSystemView{
|
|
DefaultLeaseTTLVal: 300 * time.Second,
|
|
MaxLeaseTTLVal: 1800 * time.Second,
|
|
},
|
|
StorageView: storage,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("error: %s", err)
|
|
}
|
|
|
|
b := lb.(*backend)
|
|
connState, err := testConnState("test-fixtures/keys/cert.pem",
|
|
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatalf("error testing connection state: %v", err)
|
|
}
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
req := &logical.Request{
|
|
Connection: &logical.Connection{
|
|
ConnState: &connState,
|
|
},
|
|
Storage: storage,
|
|
Auth: &logical.Auth{},
|
|
}
|
|
|
|
fd := &framework.FieldData{
|
|
Raw: map[string]interface{}{
|
|
"name": "test",
|
|
"certificate": ca,
|
|
"policies": "foo,bar",
|
|
},
|
|
Schema: pathCerts(b).Fields,
|
|
}
|
|
|
|
resp, err := b.pathCertWrite(context.Background(), req, fd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
empty_login_fd := &framework.FieldData{
|
|
Raw: map[string]interface{}{},
|
|
Schema: pathLogin(b).Fields,
|
|
}
|
|
resp, err = b.pathLogin(context.Background(), req, empty_login_fd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp.IsError() {
|
|
t.Fatalf("got error: %#v", *resp)
|
|
}
|
|
req.Auth.InternalData = resp.Auth.InternalData
|
|
req.Auth.Metadata = resp.Auth.Metadata
|
|
req.Auth.LeaseOptions = resp.Auth.LeaseOptions
|
|
req.Auth.Policies = resp.Auth.Policies
|
|
req.Auth.TokenPolicies = req.Auth.Policies
|
|
req.Auth.Period = resp.Auth.Period
|
|
|
|
// Normal renewal
|
|
resp, err = b.pathLoginRenew(context.Background(), req, empty_login_fd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("got nil response from renew")
|
|
}
|
|
if resp.IsError() {
|
|
t.Fatalf("got error: %#v", *resp)
|
|
}
|
|
|
|
// Change the policies -- this should fail
|
|
fd.Raw["policies"] = "zip,zap"
|
|
resp, err = b.pathCertWrite(context.Background(), req, fd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
resp, err = b.pathLoginRenew(context.Background(), req, empty_login_fd)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
|
|
// Put the policies back, this should be okay
|
|
fd.Raw["policies"] = "bar,foo"
|
|
resp, err = b.pathCertWrite(context.Background(), req, fd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
resp, err = b.pathLoginRenew(context.Background(), req, empty_login_fd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("got nil response from renew")
|
|
}
|
|
if resp.IsError() {
|
|
t.Fatalf("got error: %#v", *resp)
|
|
}
|
|
|
|
// Add period value to cert entry
|
|
period := 350 * time.Second
|
|
fd.Raw["period"] = period.String()
|
|
resp, err = b.pathCertWrite(context.Background(), req, fd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
resp, err = b.pathLoginRenew(context.Background(), req, empty_login_fd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("got nil response from renew")
|
|
}
|
|
if resp.IsError() {
|
|
t.Fatalf("got error: %#v", *resp)
|
|
}
|
|
|
|
if resp.Auth.Period != period {
|
|
t.Fatalf("expected a period value of %s in the response, got: %s", period, resp.Auth.Period)
|
|
}
|
|
|
|
// Delete CA, make sure we can't renew
|
|
resp, err = b.pathCertDelete(context.Background(), req, fd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
resp, err = b.pathLoginRenew(context.Background(), req, empty_login_fd)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("got nil response from renew")
|
|
}
|
|
if !resp.IsError() {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestBackend_CertUpgrade(t *testing.T) {
|
|
s := &logical.InmemStorage{}
|
|
|
|
config := logical.TestBackendConfig()
|
|
config.StorageView = s
|
|
|
|
ctx := context.Background()
|
|
|
|
b := Backend()
|
|
if b == nil {
|
|
t.Fatalf("failed to create backend")
|
|
}
|
|
if err := b.Setup(ctx, config); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
foo := &CertEntry{
|
|
Policies: []string{"foo"},
|
|
Period: time.Second,
|
|
TTL: time.Second,
|
|
MaxTTL: time.Second,
|
|
BoundCIDRs: []*sockaddr.SockAddrMarshaler{{SockAddr: sockaddr.MustIPAddr("127.0.0.1")}},
|
|
}
|
|
|
|
entry, err := logical.StorageEntryJSON("cert/foo", foo)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = s.Put(ctx, entry)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
certEntry, err := b.Cert(ctx, s, "foo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
exp := &CertEntry{
|
|
Policies: []string{"foo"},
|
|
Period: time.Second,
|
|
TTL: time.Second,
|
|
MaxTTL: time.Second,
|
|
BoundCIDRs: []*sockaddr.SockAddrMarshaler{{SockAddr: sockaddr.MustIPAddr("127.0.0.1")}},
|
|
TokenParams: tokenutil.TokenParams{
|
|
TokenPolicies: []string{"foo"},
|
|
TokenPeriod: time.Second,
|
|
TokenTTL: time.Second,
|
|
TokenMaxTTL: time.Second,
|
|
TokenBoundCIDRs: []*sockaddr.SockAddrMarshaler{{SockAddr: sockaddr.MustIPAddr("127.0.0.1")}},
|
|
},
|
|
}
|
|
if diff := deep.Equal(certEntry, exp); diff != nil {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|