2015-04-24 04:46:21 +00:00
|
|
|
package cert
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
2016-03-15 18:07:40 +00:00
|
|
|
"reflect"
|
2015-04-24 04:46:21 +00:00
|
|
|
"testing"
|
2015-04-24 19:58:39 +00:00
|
|
|
"time"
|
2015-04-24 04:46:21 +00:00
|
|
|
|
2016-05-03 16:23:25 +00:00
|
|
|
"github.com/hashicorp/go-rootcerts"
|
2015-04-24 04:46:21 +00:00
|
|
|
"github.com/hashicorp/vault/logical"
|
2016-03-17 18:22:24 +00:00
|
|
|
"github.com/hashicorp/vault/logical/framework"
|
2015-04-24 04:46:21 +00:00
|
|
|
logicaltest "github.com/hashicorp/vault/logical/testing"
|
2015-10-15 17:04:54 +00:00
|
|
|
"github.com/mitchellh/mapstructure"
|
2015-04-24 04:46:21 +00:00
|
|
|
)
|
|
|
|
|
2016-04-29 06:32:48 +00:00
|
|
|
const (
|
2016-05-02 16:34:46 +00:00
|
|
|
serverCertPath = "test-fixtures/cacert.pem"
|
|
|
|
serverKeyPath = "test-fixtures/cakey.pem"
|
2016-04-29 06:32:48 +00:00
|
|
|
serverCAPath = serverCertPath
|
|
|
|
|
2016-05-02 16:34:46 +00:00
|
|
|
testRootCACertPath1 = "test-fixtures/testcacert1.pem"
|
|
|
|
testRootCAKeyPath1 = "test-fixtures/testcakey1.pem"
|
|
|
|
testCertPath1 = "test-fixtures/testissuedcert4.pem"
|
|
|
|
testKeyPath1 = "test-fixtures/testissuedkey4.pem"
|
|
|
|
testIssuedCertCRL = "test-fixtures/issuedcertcrl"
|
2016-04-29 06:32:48 +00:00
|
|
|
|
2016-05-02 16:34:46 +00:00
|
|
|
testRootCACertPath2 = "test-fixtures/testcacert2.pem"
|
|
|
|
testRootCAKeyPath2 = "test-fixtures/testcakey2.pem"
|
|
|
|
testRootCertCRL = "test-fixtures/cacert2crl"
|
2016-04-29 06:32:48 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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(t *testing.T, serverCAPath, serverCertPath, serverKeyPath, clientCertPath, clientKeyPath string) tls.ConnectionState {
|
|
|
|
serverKeyPair, err := tls.LoadX509KeyPair(serverCertPath, serverKeyPath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(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 {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
// Load the CA cert required by the client to authenticate the server.
|
2016-05-03 16:23:25 +00:00
|
|
|
rootConfig := &rootcerts.Config{
|
|
|
|
CAFile: serverCAPath,
|
|
|
|
}
|
|
|
|
serverCAs, err := rootcerts.LoadCACerts(rootConfig)
|
2016-04-29 06:32:48 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(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 {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer list.Close()
|
|
|
|
|
|
|
|
// Establish a connection from the client side and write a few bytes.
|
|
|
|
go func() {
|
|
|
|
addr := list.Addr().String()
|
|
|
|
conn, err := tls.Dial("tcp", addr, dialConf)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
defer conn.Close()
|
|
|
|
|
|
|
|
// Write ping
|
|
|
|
conn.Write([]byte("ping"))
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Accept the connection on the server side.
|
|
|
|
serverConn, err := list.Accept()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer serverConn.Close()
|
|
|
|
|
|
|
|
// Read the ping
|
|
|
|
buf := make([]byte, 4)
|
|
|
|
serverConn.Read(buf)
|
|
|
|
|
|
|
|
// Grab the current state
|
|
|
|
connState := serverConn.(*tls.Conn).ConnectionState()
|
|
|
|
return connState
|
|
|
|
}
|
|
|
|
|
2016-05-12 18:37:07 +00:00
|
|
|
func TestBackend_RegisteredNonCA_CRL(t *testing.T) {
|
|
|
|
config := logical.TestBackendConfig()
|
|
|
|
storage := &logical.InmemStorage{}
|
|
|
|
config.StorageView = storage
|
|
|
|
|
|
|
|
b, err := Factory(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(certReq)
|
2016-06-01 20:01:28 +00:00
|
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
|
|
}
|
2016-05-12 18:37:07 +00:00
|
|
|
|
|
|
|
// Connection state is presenting the client Non-CA cert and its key.
|
|
|
|
// This is exactly what is registered at the backend.
|
|
|
|
connState := connectionState(t, serverCAPath, serverCertPath, serverKeyPath, testCertPath1, testKeyPath1)
|
|
|
|
loginReq := &logical.Request{
|
|
|
|
Operation: logical.UpdateOperation,
|
|
|
|
Storage: storage,
|
|
|
|
Path: "login",
|
|
|
|
Connection: &logical.Connection{
|
|
|
|
ConnState: &connState,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
// Login should succeed.
|
|
|
|
resp, err = b.HandleRequest(loginReq)
|
2016-06-01 20:01:28 +00:00
|
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
|
|
}
|
2016-05-12 18:37:07 +00:00
|
|
|
|
|
|
|
// 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(crlReq)
|
2016-06-01 20:01:28 +00:00
|
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
|
|
}
|
2016-05-12 18:37:07 +00:00
|
|
|
|
|
|
|
// Attempt login with the same connection state but with the CRL registered
|
|
|
|
resp, err = b.HandleRequest(loginReq)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if resp == nil || !resp.IsError() {
|
|
|
|
t.Fatalf("expected failure due to revoked certificate")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-29 06:32:48 +00:00
|
|
|
func TestBackend_CRLs(t *testing.T) {
|
|
|
|
config := logical.TestBackendConfig()
|
|
|
|
storage := &logical.InmemStorage{}
|
|
|
|
config.StorageView = storage
|
|
|
|
|
|
|
|
b, err := Factory(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,
|
|
|
|
}
|
|
|
|
|
2016-05-04 05:46:25 +00:00
|
|
|
resp, err := b.HandleRequest(certReq)
|
2016-06-01 20:01:28 +00:00
|
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
|
|
}
|
2016-04-29 06:32:48 +00:00
|
|
|
|
|
|
|
// Connection state is presenting the client CA cert and its key.
|
|
|
|
// This is exactly what is registered at the backend.
|
|
|
|
connState := connectionState(t, serverCAPath, serverCertPath, serverKeyPath, testRootCACertPath1, testRootCAKeyPath1)
|
|
|
|
loginReq := &logical.Request{
|
|
|
|
Operation: logical.UpdateOperation,
|
|
|
|
Storage: storage,
|
|
|
|
Path: "login",
|
|
|
|
Connection: &logical.Connection{
|
|
|
|
ConnState: &connState,
|
|
|
|
},
|
|
|
|
}
|
2016-05-04 05:46:25 +00:00
|
|
|
resp, err = b.HandleRequest(loginReq)
|
2016-06-01 20:01:28 +00:00
|
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
|
|
}
|
2016-04-29 06:32:48 +00:00
|
|
|
|
|
|
|
// Now, without changing the registered client CA cert, present from
|
|
|
|
// the client side, a cert issued using the registered CA.
|
|
|
|
connState = connectionState(t, serverCAPath, serverCertPath, serverKeyPath, testCertPath1, testKeyPath1)
|
|
|
|
loginReq.Connection.ConnState = &connState
|
|
|
|
|
|
|
|
// Attempt login with the updated connection
|
|
|
|
resp, err = b.HandleRequest(loginReq)
|
2016-06-01 20:01:28 +00:00
|
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
|
|
}
|
2016-04-29 06:32:48 +00:00
|
|
|
|
|
|
|
// 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,
|
|
|
|
}
|
2016-05-04 05:46:25 +00:00
|
|
|
resp, err = b.HandleRequest(crlReq)
|
2016-06-01 20:01:28 +00:00
|
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
|
|
}
|
2016-04-29 06:32:48 +00:00
|
|
|
|
|
|
|
// Attempt login with the revoked certificate.
|
|
|
|
resp, err = b.HandleRequest(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
|
2016-05-04 05:46:25 +00:00
|
|
|
resp, err = b.HandleRequest(certReq)
|
2016-06-01 20:01:28 +00:00
|
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
|
|
}
|
2016-04-29 06:32:48 +00:00
|
|
|
|
|
|
|
// Test login using a different client CA cert pair.
|
|
|
|
connState = connectionState(t, serverCAPath, serverCertPath, serverKeyPath, testRootCACertPath2, testRootCAKeyPath2)
|
|
|
|
loginReq.Connection.ConnState = &connState
|
|
|
|
|
|
|
|
// Attempt login with the updated connection
|
|
|
|
resp, err = b.HandleRequest(loginReq)
|
2016-06-01 20:01:28 +00:00
|
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
|
|
}
|
2016-04-29 06:32:48 +00:00
|
|
|
|
|
|
|
// Register a CRL containing the root CA certificate used above.
|
|
|
|
rootCRL, err := ioutil.ReadFile(testRootCertCRL)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
crlData["crl"] = rootCRL
|
2016-05-04 05:46:25 +00:00
|
|
|
resp, err = b.HandleRequest(crlReq)
|
2016-06-01 20:01:28 +00:00
|
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
|
|
t.Fatalf("err:%v resp:%#v", err, resp)
|
|
|
|
}
|
2016-04-29 06:32:48 +00:00
|
|
|
|
|
|
|
// Attempt login with the same connection state but with the CRL registered
|
|
|
|
resp, err = b.HandleRequest(loginReq)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if resp == nil || !resp.IsError() {
|
|
|
|
t.Fatalf("expected failure due to revoked certificate")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-06 01:10:20 +00:00
|
|
|
func testFactory(t *testing.T) logical.Backend {
|
|
|
|
b, err := Factory(&logical.BackendConfig{
|
|
|
|
System: &logical.StaticSystemView{
|
|
|
|
DefaultLeaseTTLVal: 300 * time.Second,
|
|
|
|
MaxLeaseTTLVal: 1800 * time.Second,
|
|
|
|
},
|
2015-10-15 17:04:54 +00:00
|
|
|
StorageView: &logical.InmemStorage{},
|
2015-10-06 01:10:20 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
2016-04-13 19:38:29 +00:00
|
|
|
t.Fatalf("error: %s", err)
|
2015-10-06 01:10:20 +00:00
|
|
|
}
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
2016-02-29 21:02:19 +00:00
|
|
|
// 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)
|
|
|
|
}
|
2016-03-15 18:07:40 +00:00
|
|
|
|
|
|
|
tc := logicaltest.TestCase{
|
2016-04-05 19:10:44 +00:00
|
|
|
AcceptanceTest: true,
|
|
|
|
Backend: testFactory(t),
|
2016-02-29 21:02:19 +00:00
|
|
|
Steps: []logicaltest.TestStep{
|
2016-03-15 18:07:40 +00:00
|
|
|
testAccStepCert(t, "aaa", ca1, "foo", false),
|
|
|
|
testAccStepCert(t, "bbb", ca2, "foo", false),
|
|
|
|
testAccStepCert(t, "ccc", ca3, "foo", true),
|
2016-02-29 21:02:19 +00:00
|
|
|
},
|
2016-03-15 18:07:40 +00:00
|
|
|
}
|
|
|
|
tc.Steps = append(tc.Steps, testAccStepListCerts(t, []string{"aaa", "bbb"})...)
|
|
|
|
logicaltest.Test(t, tc)
|
2016-02-29 21:02:19 +00:00
|
|
|
}
|
|
|
|
|
2015-04-24 04:46:21 +00:00
|
|
|
// Test a client trusted by a CA
|
|
|
|
func TestBackend_basic_CA(t *testing.T) {
|
2016-02-29 20:48:09 +00:00
|
|
|
connState := testConnState(t, "test-fixtures/keys/cert.pem",
|
|
|
|
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
|
|
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
2015-04-24 04:46:21 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
2016-04-05 19:10:44 +00:00
|
|
|
AcceptanceTest: true,
|
|
|
|
Backend: testFactory(t),
|
2015-04-24 04:46:21 +00:00
|
|
|
Steps: []logicaltest.TestStep{
|
2016-02-29 21:02:19 +00:00
|
|
|
testAccStepCert(t, "web", ca, "foo", false),
|
2015-04-24 04:46:21 +00:00
|
|
|
testAccStepLogin(t, connState),
|
2015-10-06 01:10:20 +00:00
|
|
|
testAccStepCertLease(t, "web", ca, "foo"),
|
2015-09-18 18:01:28 +00:00
|
|
|
testAccStepCertTTL(t, "web", ca, "foo"),
|
|
|
|
testAccStepLogin(t, connState),
|
|
|
|
testAccStepCertNoLease(t, "web", ca, "foo"),
|
|
|
|
testAccStepLoginDefaultLease(t, connState),
|
2015-10-30 17:32:51 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test CRL behavior
|
2016-04-29 06:32:48 +00:00
|
|
|
func TestBackend_Basic_CRLs(t *testing.T) {
|
2016-02-29 20:48:09 +00:00
|
|
|
connState := testConnState(t, "test-fixtures/keys/cert.pem",
|
|
|
|
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
|
|
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
2015-10-30 17:32:51 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2016-02-29 20:48:09 +00:00
|
|
|
crl, err := ioutil.ReadFile("test-fixtures/root/root.crl")
|
2015-10-30 17:32:51 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
2016-04-05 19:10:44 +00:00
|
|
|
AcceptanceTest: true,
|
|
|
|
Backend: testFactory(t),
|
2015-10-30 17:32:51 +00:00
|
|
|
Steps: []logicaltest.TestStep{
|
|
|
|
testAccStepCertNoLease(t, "web", ca, "foo"),
|
|
|
|
testAccStepLoginDefaultLease(t, connState),
|
2015-10-15 17:04:54 +00:00
|
|
|
testAccStepAddCRL(t, crl, connState),
|
|
|
|
testAccStepReadCRL(t, connState),
|
|
|
|
testAccStepLoginInvalid(t, connState),
|
|
|
|
testAccStepDeleteCRL(t, connState),
|
|
|
|
testAccStepLoginDefaultLease(t, connState),
|
2015-04-24 04:46:21 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test a self-signed client that is trusted
|
|
|
|
func TestBackend_basic_singleCert(t *testing.T) {
|
2016-02-29 20:48:09 +00:00
|
|
|
connState := testConnState(t, "test-fixtures/keys/cert.pem",
|
|
|
|
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
|
|
|
|
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
2015-04-24 04:46:21 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
2016-04-05 19:10:44 +00:00
|
|
|
AcceptanceTest: true,
|
|
|
|
Backend: testFactory(t),
|
2015-04-24 04:46:21 +00:00
|
|
|
Steps: []logicaltest.TestStep{
|
2016-02-29 21:02:19 +00:00
|
|
|
testAccStepCert(t, "web", ca, "foo", false),
|
2015-04-24 04:46:21 +00:00
|
|
|
testAccStepLogin(t, connState),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test an untrusted self-signed client
|
|
|
|
func TestBackend_untrusted(t *testing.T) {
|
2016-02-29 20:48:09 +00:00
|
|
|
connState := testConnState(t, "test-fixtures/keys/cert.pem",
|
|
|
|
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
|
2015-04-24 04:46:21 +00:00
|
|
|
logicaltest.Test(t, logicaltest.TestCase{
|
2016-04-05 19:10:44 +00:00
|
|
|
AcceptanceTest: true,
|
|
|
|
Backend: testFactory(t),
|
2015-04-24 04:46:21 +00:00
|
|
|
Steps: []logicaltest.TestStep{
|
|
|
|
testAccStepLoginInvalid(t, connState),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-10-15 17:04:54 +00:00
|
|
|
func testAccStepAddCRL(t *testing.T, crl []byte, connState tls.ConnectionState) logicaltest.TestStep {
|
|
|
|
return logicaltest.TestStep{
|
2016-01-07 15:30:47 +00:00
|
|
|
Operation: logical.UpdateOperation,
|
2015-10-15 17:04:54 +00:00
|
|
|
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))
|
|
|
|
}
|
2016-02-29 03:15:54 +00:00
|
|
|
if _, ok := crlInfo.Serials["637101449987587619778072672905061040630001617053"]; !ok {
|
2016-02-29 15:41:10 +00:00
|
|
|
t.Fatalf("bad: expected serial number not found in CRL")
|
2015-10-15 17:04:54 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func testAccStepDeleteCRL(t *testing.T, connState tls.ConnectionState) logicaltest.TestStep {
|
|
|
|
return logicaltest.TestStep{
|
|
|
|
Operation: logical.DeleteOperation,
|
|
|
|
Path: "crls/test",
|
|
|
|
ConnState: &connState,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-24 04:46:21 +00:00
|
|
|
func testAccStepLogin(t *testing.T, connState tls.ConnectionState) logicaltest.TestStep {
|
|
|
|
return logicaltest.TestStep{
|
2016-01-07 15:30:47 +00:00
|
|
|
Operation: logical.UpdateOperation,
|
2015-04-24 04:46:21 +00:00
|
|
|
Path: "login",
|
|
|
|
Unauthenticated: true,
|
|
|
|
ConnState: &connState,
|
2015-04-24 19:58:39 +00:00
|
|
|
Check: func(resp *logical.Response) error {
|
2015-08-21 00:47:17 +00:00
|
|
|
if resp.Auth.TTL != 1000*time.Second {
|
2015-04-24 19:58:39 +00:00
|
|
|
t.Fatalf("bad lease length: %#v", resp.Auth)
|
|
|
|
}
|
|
|
|
|
2016-03-15 18:07:40 +00:00
|
|
|
fn := logicaltest.TestCheckAuth([]string{"default", "foo"})
|
2015-04-24 19:58:39 +00:00
|
|
|
return fn(resp)
|
|
|
|
},
|
2015-04-24 04:46:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-18 18:01:28 +00:00
|
|
|
func testAccStepLoginDefaultLease(t *testing.T, connState tls.ConnectionState) logicaltest.TestStep {
|
|
|
|
return logicaltest.TestStep{
|
2016-01-07 15:30:47 +00:00
|
|
|
Operation: logical.UpdateOperation,
|
2015-09-18 18:01:28 +00:00
|
|
|
Path: "login",
|
|
|
|
Unauthenticated: true,
|
|
|
|
ConnState: &connState,
|
|
|
|
Check: func(resp *logical.Response) error {
|
|
|
|
if resp.Auth.TTL != 300*time.Second {
|
|
|
|
t.Fatalf("bad lease length: %#v", resp.Auth)
|
|
|
|
}
|
|
|
|
|
2016-03-15 18:07:40 +00:00
|
|
|
fn := logicaltest.TestCheckAuth([]string{"default", "foo"})
|
2015-09-18 18:01:28 +00:00
|
|
|
return fn(resp)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-24 04:46:21 +00:00
|
|
|
func testAccStepLoginInvalid(t *testing.T, connState tls.ConnectionState) logicaltest.TestStep {
|
|
|
|
return logicaltest.TestStep{
|
2016-01-07 15:30:47 +00:00
|
|
|
Operation: logical.UpdateOperation,
|
2015-04-24 04:46:21 +00:00
|
|
|
Path: "login",
|
|
|
|
Unauthenticated: true,
|
|
|
|
ConnState: &connState,
|
|
|
|
Check: func(resp *logical.Response) error {
|
2015-04-24 17:39:44 +00:00
|
|
|
if resp.Auth != nil {
|
|
|
|
return fmt.Errorf("should not be authorized: %#v", resp)
|
2015-04-24 04:46:21 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
2015-04-24 17:39:44 +00:00
|
|
|
ErrorOk: true,
|
2015-04-24 04:46:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-15 18:07:40 +00:00
|
|
|
func testAccStepListCerts(
|
|
|
|
t *testing.T, certs []string) []logicaltest.TestStep {
|
|
|
|
return []logicaltest.TestStep{
|
|
|
|
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
|
|
|
|
},
|
|
|
|
}, logicaltest.TestStep{
|
|
|
|
Operation: logical.ListOperation,
|
|
|
|
Path: "certs/",
|
|
|
|
Check: func(resp *logical.Response) error {
|
2016-03-15 18:18:35 +00:00
|
|
|
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)
|
|
|
|
}
|
2016-03-15 18:07:40 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-24 04:46:21 +00:00
|
|
|
func testAccStepCert(
|
2016-02-29 21:02:19 +00:00
|
|
|
t *testing.T, name string, cert []byte, policies string, expectError bool) logicaltest.TestStep {
|
2015-04-24 04:46:21 +00:00
|
|
|
return logicaltest.TestStep{
|
2016-01-07 15:30:47 +00:00
|
|
|
Operation: logical.UpdateOperation,
|
2015-04-24 17:31:57 +00:00
|
|
|
Path: "certs/" + name,
|
2016-02-29 23:29:41 +00:00
|
|
|
ErrorOk: expectError,
|
2015-04-24 04:46:21 +00:00
|
|
|
Data: map[string]interface{}{
|
|
|
|
"certificate": string(cert),
|
2015-04-24 17:31:57 +00:00
|
|
|
"policies": policies,
|
2015-04-24 04:46:21 +00:00
|
|
|
"display_name": name,
|
2015-04-24 19:58:39 +00:00
|
|
|
"lease": 1000,
|
2015-04-24 04:46:21 +00:00
|
|
|
},
|
2016-02-29 21:02:19 +00:00
|
|
|
Check: func(resp *logical.Response) error {
|
|
|
|
if resp == nil && expectError {
|
|
|
|
return fmt.Errorf("expected error but received nil")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
2015-04-24 04:46:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-06 01:10:20 +00:00
|
|
|
func testAccStepCertLease(
|
|
|
|
t *testing.T, name string, cert []byte, policies string) logicaltest.TestStep {
|
|
|
|
return logicaltest.TestStep{
|
2016-01-07 15:30:47 +00:00
|
|
|
Operation: logical.UpdateOperation,
|
2015-10-06 01:10:20 +00:00
|
|
|
Path: "certs/" + name,
|
|
|
|
Data: map[string]interface{}{
|
|
|
|
"certificate": string(cert),
|
|
|
|
"policies": policies,
|
|
|
|
"display_name": name,
|
|
|
|
"lease": 1000,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-18 18:01:28 +00:00
|
|
|
func testAccStepCertTTL(
|
|
|
|
t *testing.T, name string, cert []byte, policies string) logicaltest.TestStep {
|
|
|
|
return logicaltest.TestStep{
|
2016-01-07 15:30:47 +00:00
|
|
|
Operation: logical.UpdateOperation,
|
2015-09-18 18:01:28 +00:00
|
|
|
Path: "certs/" + name,
|
|
|
|
Data: map[string]interface{}{
|
|
|
|
"certificate": string(cert),
|
|
|
|
"policies": policies,
|
|
|
|
"display_name": name,
|
2015-10-06 01:10:20 +00:00
|
|
|
"ttl": "1000s",
|
2015-09-18 18:01:28 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func testAccStepCertNoLease(
|
|
|
|
t *testing.T, name string, cert []byte, policies string) logicaltest.TestStep {
|
|
|
|
return logicaltest.TestStep{
|
2016-01-07 15:30:47 +00:00
|
|
|
Operation: logical.UpdateOperation,
|
2015-09-18 18:01:28 +00:00
|
|
|
Path: "certs/" + name,
|
|
|
|
Data: map[string]interface{}{
|
|
|
|
"certificate": string(cert),
|
|
|
|
"policies": policies,
|
|
|
|
"display_name": name,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-29 03:15:54 +00:00
|
|
|
func testConnState(t *testing.T, certPath, keyPath, rootCertPath string) tls.ConnectionState {
|
2015-04-24 04:46:21 +00:00
|
|
|
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2016-05-03 16:23:25 +00:00
|
|
|
rootConfig := &rootcerts.Config{
|
|
|
|
CAFile: rootCertPath,
|
|
|
|
}
|
|
|
|
rootCAs, err := rootcerts.LoadCACerts(rootConfig)
|
2016-02-29 03:15:54 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-09-03 17:04:32 +00:00
|
|
|
listenConf := &tls.Config{
|
2015-04-24 04:46:21 +00:00
|
|
|
Certificates: []tls.Certificate{cert},
|
|
|
|
ClientAuth: tls.RequestClientCert,
|
2016-02-29 03:15:54 +00:00
|
|
|
InsecureSkipVerify: false,
|
|
|
|
RootCAs: rootCAs,
|
2015-04-24 04:46:21 +00:00
|
|
|
}
|
2015-09-03 17:04:32 +00:00
|
|
|
dialConf := new(tls.Config)
|
|
|
|
*dialConf = *listenConf
|
|
|
|
list, err := tls.Listen("tcp", "127.0.0.1:0", listenConf)
|
2015-04-24 04:46:21 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
defer list.Close()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
addr := list.Addr().String()
|
2015-09-03 17:04:32 +00:00
|
|
|
conn, err := tls.Dial("tcp", addr, dialConf)
|
2015-04-24 04:46:21 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
defer conn.Close()
|
|
|
|
|
|
|
|
// Write ping
|
|
|
|
conn.Write([]byte("ping"))
|
|
|
|
}()
|
|
|
|
|
|
|
|
serverConn, err := list.Accept()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
defer serverConn.Close()
|
|
|
|
|
|
|
|
// Read the pign
|
|
|
|
buf := make([]byte, 4)
|
|
|
|
serverConn.Read(buf)
|
|
|
|
|
|
|
|
// Grab the current state
|
|
|
|
connState := serverConn.(*tls.Conn).ConnectionState()
|
|
|
|
return connState
|
|
|
|
}
|
2016-03-17 18:22:24 +00:00
|
|
|
|
|
|
|
func Test_Renew(t *testing.T) {
|
|
|
|
storage := &logical.InmemStorage{}
|
|
|
|
|
|
|
|
lb, err := Factory(&logical.BackendConfig{
|
|
|
|
System: &logical.StaticSystemView{
|
|
|
|
DefaultLeaseTTLVal: 300 * time.Second,
|
|
|
|
MaxLeaseTTLVal: 1800 * time.Second,
|
|
|
|
},
|
|
|
|
StorageView: storage,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("error: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
b := lb.(*backend)
|
|
|
|
connState := testConnState(t, "test-fixtures/keys/cert.pem",
|
|
|
|
"test-fixtures/keys/key.pem", "test-fixtures/root/rootcacert.pem")
|
|
|
|
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(req, fd)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err = b.pathLogin(req, nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
req.Auth.InternalData = resp.Auth.InternalData
|
|
|
|
req.Auth.Metadata = resp.Auth.Metadata
|
|
|
|
req.Auth.LeaseOptions = resp.Auth.LeaseOptions
|
2016-03-24 13:41:51 +00:00
|
|
|
req.Auth.Policies = resp.Auth.Policies
|
2016-03-17 18:22:24 +00:00
|
|
|
req.Auth.IssueTime = time.Now()
|
|
|
|
|
|
|
|
// Normal renewal
|
|
|
|
resp, err = b.pathLoginRenew(req, nil)
|
|
|
|
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(req, fd)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err = b.pathLoginRenew(req, nil)
|
2016-05-26 15:06:46 +00:00
|
|
|
if err == nil {
|
2016-03-17 18:22:24 +00:00
|
|
|
t.Fatal("expected error")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Put the policies back, this shold be okay
|
|
|
|
fd.Raw["policies"] = "bar,foo"
|
|
|
|
resp, err = b.pathCertWrite(req, fd)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err = b.pathLoginRenew(req, nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if resp == nil {
|
|
|
|
t.Fatal("got nil response from renew")
|
|
|
|
}
|
|
|
|
if resp.IsError() {
|
|
|
|
t.Fatal("got error: %#v", *resp)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete CA, make sure we can't renew
|
|
|
|
resp, err = b.pathCertDelete(req, fd)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err = b.pathLoginRenew(req, nil)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if resp == nil {
|
|
|
|
t.Fatal("got nil response from renew")
|
|
|
|
}
|
|
|
|
if !resp.IsError() {
|
|
|
|
t.Fatal("expected error")
|
|
|
|
}
|
|
|
|
}
|