Add root rotation test.

pull/22/head
Mariano Cano 5 years ago
parent af9e6488fc
commit f99ae9da93

@ -3,12 +3,15 @@ package ca
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"time"
"github.com/pkg/errors"
"github.com/smallstep/certificates/api"
"github.com/smallstep/certificates/authority"
@ -18,6 +21,24 @@ import (
"gopkg.in/square/go-jose.v2/jwt"
)
func newLocalListener() net.Listener {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
if l, err = net.Listen("tcp6", "[::1]:0"); err != nil {
panic(fmt.Sprintf("failed to listen on a port: %v", err))
}
}
return l
}
func setMinCertDuration(d time.Duration) func() {
tmp := minCertDuration
minCertDuration = 1 * time.Second
return func() {
minCertDuration = tmp
}
}
func startCABootstrapServer() *httptest.Server {
config, err := authority.LoadConfiguration("testdata/ca.json")
if err != nil {
@ -267,3 +288,112 @@ func TestBootstrapClient(t *testing.T) {
})
}
}
func TestBootstrapClientRotation(t *testing.T) {
reset := setMinCertDuration(1 * time.Second)
defer reset()
// Configuration with current root
config, err := authority.LoadConfiguration("testdata/rotate-ca-0.json")
if err != nil {
panic(err)
}
// Get local address
listener := newLocalListener()
config.Address = listener.Addr().String()
srvURL := "https://" + listener.Addr().String()
// Start CA server
ca, err := New(config)
if err != nil {
panic(err)
}
go func() {
ca.srv.Serve(listener)
}()
defer ca.Stop()
time.Sleep(1 * time.Second)
// doTest does a request that requires mTLS
doTest := func(client *http.Client) error {
resp, err := client.Get(srvURL + "/roots")
if err != nil {
return errors.New("client.Get() failed getting roots")
}
var roots api.RootsResponse
if err := readJSON(resp.Body, &roots); err != nil {
return errors.Errorf("client.Get() error reading response: %v", err)
}
return nil
}
// Create bootstrap client
token := generateBootstrapToken(srvURL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
client, err := BootstrapClient(context.Background(), token)
if err != nil {
t.Errorf("BootstrapClient() error = %v", err)
return
}
// Test with default root
if err := doTest(client); err != nil {
t.Errorf("Test with rotate-ca-0.json failed: %v", err)
}
// wait for renew
time.Sleep(5 * time.Second)
// Reload with configuration with current and future root
ca.opts.configFile = "testdata/rotate-ca-1.json"
if err := doReload(ca); err != nil {
t.Errorf("ca.Reload() error = %v", err)
return
}
if err := doTest(client); err != nil {
t.Errorf("Test with rotate-ca-1.json failed: %v", err)
}
// wait for renew
time.Sleep(5 * time.Second)
// Reload with new and old root
ca.opts.configFile = "testdata/rotate-ca-2.json"
if err := doReload(ca); err != nil {
t.Errorf("ca.Reload() error = %v", err)
return
}
if err := doTest(client); err != nil {
t.Errorf("Test with rotate-ca-2.json failed: %v", err)
}
// wait for renew
time.Sleep(5 * time.Second)
// Reload with pnly the new root
ca.opts.configFile = "testdata/rotate-ca-3.json"
if err := doReload(ca); err != nil {
t.Errorf("ca.Reload() error = %v", err)
return
}
if err := doTest(client); err != nil {
t.Errorf("Test with rotate-ca-3.json failed: %v", err)
}
}
// doReload uses the reload implementation but overwrites the new address with
// the one being used.
func doReload(ca *CA) error {
config, err := authority.LoadConfiguration(ca.opts.configFile)
if err != nil {
return errors.Wrap(err, "error reloading ca")
}
newCA, err := New(config, WithPassword(ca.opts.password), WithConfigFile(ca.opts.configFile))
if err != nil {
return errors.Wrap(err, "error reloading ca")
}
// Use same address in new server
newCA.srv.Addr = ca.srv.Addr
return ca.srv.Reload(newCA.srv)
}

@ -18,7 +18,6 @@
]
},
"authority": {
"minCertDuration": "1m",
"provisioners": [
{
"name": "max",
@ -73,7 +72,7 @@
"y": "e3wycXwVB366F0wLE5J9gIpq8EIQ4900nHBNpIGebEA"
},
"claims": {
"minTLSCertDuration": "30s"
"minTLSCertDuration": "1s"
}
}, {
"name": "mariano",

@ -0,0 +1,46 @@
{
"root": "testdata/secrets/root_ca.crt",
"crt": "testdata/secrets/intermediate_ca.crt",
"key": "testdata/secrets/intermediate_ca_key",
"password": "password",
"address": "127.0.0.1:0",
"dnsNames": ["127.0.0.1"],
"logger": {"format": "text"},
"tls": {
"minVersion": 1.2,
"maxVersion": 1.2,
"renegotiation": false,
"cipherSuites": [
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
]
},
"authority": {
"provisioners": [
{
"name": "mariano",
"type": "jwk",
"encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6IlB1UnJVQ1RZZkR1T2F5MEh2cGl6bncifQ.7a-OP5xWGbFra8m2MN9YuLGt6v4y0wmB.u-54daK2y-0UO9na.3GQy6E52-fOSUu5NJ_sEbxj_T3CTyWb7wOPFv2oI2PBWXp5CLpiWJbCFpF4v2oD9fN5XbxMP14ootbrFjATnoMWfWgyLwG-KOj9BqMGNxhG2v37yC7Wrris6s30nrPa3uyNEYZ12AOQW1K04cU2X0u_qJM3vzMCle548ZFTWs6_d6L8lp3o0F9MEbCmJ4p6CLqQxjxYtn1aD79lM91NbIXpRP3iUFQRly-y_iC2mSkXCdd_cQ6-dqLUchXwWRyVO5nBHb4J87aZ91VApw7ldTLtwRZ2ZGJpqGQGgjTwi4sgjEcMuGg0_83XGk2ubdlKDpmGFedOHS5rYCbxotts.vSYfxsi2UU9LQeySDjAnnQ",
"key": {
"use": "sig",
"kty": "EC",
"kid": "FLIV7q23CXHrg75J2OSbvzwKJJqoxCYixjmsJirneOg",
"crv": "P-256",
"alg": "ES256",
"x": "tTKthEHN7RuybhkaC43J2oLfBG995FNSWbtahLAiK7Y",
"y": "e3wycXwVB366F0wLE5J9gIpq8EIQ4900nHBNpIGebEA"
},
"claims": {
"minTLSCertDuration": "1s",
"defaultTLSCertDuration": "5s"
}
}
],
"template": {
"country": "US",
"locality": "San Francisco",
"organization": "Smallstep"
}
}
}

@ -0,0 +1,46 @@
{
"root": ["testdata/secrets/root_ca.crt", "testdata/rotated/root_ca.crt"],
"crt": "testdata/secrets/intermediate_ca.crt",
"key": "testdata/secrets/intermediate_ca_key",
"password": "password",
"address": "127.0.0.1:0",
"dnsNames": ["127.0.0.1"],
"logger": {"format": "text"},
"tls": {
"minVersion": 1.2,
"maxVersion": 1.2,
"renegotiation": false,
"cipherSuites": [
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
]
},
"authority": {
"provisioners": [
{
"name": "mariano",
"type": "jwk",
"encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6IlB1UnJVQ1RZZkR1T2F5MEh2cGl6bncifQ.7a-OP5xWGbFra8m2MN9YuLGt6v4y0wmB.u-54daK2y-0UO9na.3GQy6E52-fOSUu5NJ_sEbxj_T3CTyWb7wOPFv2oI2PBWXp5CLpiWJbCFpF4v2oD9fN5XbxMP14ootbrFjATnoMWfWgyLwG-KOj9BqMGNxhG2v37yC7Wrris6s30nrPa3uyNEYZ12AOQW1K04cU2X0u_qJM3vzMCle548ZFTWs6_d6L8lp3o0F9MEbCmJ4p6CLqQxjxYtn1aD79lM91NbIXpRP3iUFQRly-y_iC2mSkXCdd_cQ6-dqLUchXwWRyVO5nBHb4J87aZ91VApw7ldTLtwRZ2ZGJpqGQGgjTwi4sgjEcMuGg0_83XGk2ubdlKDpmGFedOHS5rYCbxotts.vSYfxsi2UU9LQeySDjAnnQ",
"key": {
"use": "sig",
"kty": "EC",
"kid": "FLIV7q23CXHrg75J2OSbvzwKJJqoxCYixjmsJirneOg",
"crv": "P-256",
"alg": "ES256",
"x": "tTKthEHN7RuybhkaC43J2oLfBG995FNSWbtahLAiK7Y",
"y": "e3wycXwVB366F0wLE5J9gIpq8EIQ4900nHBNpIGebEA"
},
"claims": {
"minTLSCertDuration": "1s",
"defaultTLSCertDuration": "5s"
}
}
],
"template": {
"country": "US",
"locality": "San Francisco",
"organization": "Smallstep"
}
}
}

@ -0,0 +1,46 @@
{
"root": ["testdata/rotated/root_ca.crt", "testdata/secrets/root_ca.crt"],
"crt": "testdata/rotated/intermediate_ca.crt",
"key": "testdata/rotated/intermediate_ca_key",
"password": "asdf",
"address": "127.0.0.1:0",
"dnsNames": ["127.0.0.1"],
"logger": {"format": "text"},
"tls": {
"minVersion": 1.2,
"maxVersion": 1.2,
"renegotiation": false,
"cipherSuites": [
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
]
},
"authority": {
"provisioners": [
{
"name": "mariano",
"type": "jwk",
"encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6IlB1UnJVQ1RZZkR1T2F5MEh2cGl6bncifQ.7a-OP5xWGbFra8m2MN9YuLGt6v4y0wmB.u-54daK2y-0UO9na.3GQy6E52-fOSUu5NJ_sEbxj_T3CTyWb7wOPFv2oI2PBWXp5CLpiWJbCFpF4v2oD9fN5XbxMP14ootbrFjATnoMWfWgyLwG-KOj9BqMGNxhG2v37yC7Wrris6s30nrPa3uyNEYZ12AOQW1K04cU2X0u_qJM3vzMCle548ZFTWs6_d6L8lp3o0F9MEbCmJ4p6CLqQxjxYtn1aD79lM91NbIXpRP3iUFQRly-y_iC2mSkXCdd_cQ6-dqLUchXwWRyVO5nBHb4J87aZ91VApw7ldTLtwRZ2ZGJpqGQGgjTwi4sgjEcMuGg0_83XGk2ubdlKDpmGFedOHS5rYCbxotts.vSYfxsi2UU9LQeySDjAnnQ",
"key": {
"use": "sig",
"kty": "EC",
"kid": "FLIV7q23CXHrg75J2OSbvzwKJJqoxCYixjmsJirneOg",
"crv": "P-256",
"alg": "ES256",
"x": "tTKthEHN7RuybhkaC43J2oLfBG995FNSWbtahLAiK7Y",
"y": "e3wycXwVB366F0wLE5J9gIpq8EIQ4900nHBNpIGebEA"
},
"claims": {
"minTLSCertDuration": "1s",
"defaultTLSCertDuration": "5s"
}
}
],
"template": {
"country": "US",
"locality": "San Francisco",
"organization": "Smallstep"
}
}
}

@ -0,0 +1,46 @@
{
"root": "testdata/rotated/root_ca.crt",
"crt": "testdata/rotated/intermediate_ca.crt",
"key": "testdata/rotated/intermediate_ca_key",
"password": "asdf",
"address": "127.0.0.1:0",
"dnsNames": ["127.0.0.1"],
"logger": {"format": "text"},
"tls": {
"minVersion": 1.2,
"maxVersion": 1.2,
"renegotiation": false,
"cipherSuites": [
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
]
},
"authority": {
"provisioners": [
{
"name": "mariano",
"type": "jwk",
"encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjoxMDAwMDAsInAycyI6IlB1UnJVQ1RZZkR1T2F5MEh2cGl6bncifQ.7a-OP5xWGbFra8m2MN9YuLGt6v4y0wmB.u-54daK2y-0UO9na.3GQy6E52-fOSUu5NJ_sEbxj_T3CTyWb7wOPFv2oI2PBWXp5CLpiWJbCFpF4v2oD9fN5XbxMP14ootbrFjATnoMWfWgyLwG-KOj9BqMGNxhG2v37yC7Wrris6s30nrPa3uyNEYZ12AOQW1K04cU2X0u_qJM3vzMCle548ZFTWs6_d6L8lp3o0F9MEbCmJ4p6CLqQxjxYtn1aD79lM91NbIXpRP3iUFQRly-y_iC2mSkXCdd_cQ6-dqLUchXwWRyVO5nBHb4J87aZ91VApw7ldTLtwRZ2ZGJpqGQGgjTwi4sgjEcMuGg0_83XGk2ubdlKDpmGFedOHS5rYCbxotts.vSYfxsi2UU9LQeySDjAnnQ",
"key": {
"use": "sig",
"kty": "EC",
"kid": "FLIV7q23CXHrg75J2OSbvzwKJJqoxCYixjmsJirneOg",
"crv": "P-256",
"alg": "ES256",
"x": "tTKthEHN7RuybhkaC43J2oLfBG995FNSWbtahLAiK7Y",
"y": "e3wycXwVB366F0wLE5J9gIpq8EIQ4900nHBNpIGebEA"
},
"claims": {
"minTLSCertDuration": "1s",
"defaultTLSCertDuration": "5s"
}
}
],
"template": {
"country": "US",
"locality": "San Francisco",
"organization": "Smallstep"
}
}
}

@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIBxTCCAWugAwIBAgIQLIY6MR/1fBRQY4ZTTsPAJjAKBggqhkjOPQQDAjAcMRow
GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0xOTAxMDcyMDExMzBaFw0yOTAx
MDQyMDExMzBaMCQxIjAgBgNVBAMTGVNtYWxsc3RlcCBJbnRlcm1lZGlhdGUgQ0Ew
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARgtjL/KLNpdq81YYWaek1lrkPM/QF1
m+ujwv5jya21fAXljdBLh6m2xco1GPfwPBbwUGlNOdEqE9Nq3Qx3ngPKo4GGMIGD
MA4GA1UdDwEB/wQEAwIBpjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUqixeZ/K1HW9N6SVw7ONya98S
u8UwHwYDVR0jBBgwFoAUgIzlCLxh/RlwEany4JQHOorLAIEwCgYIKoZIzj0EAwID
SAAwRQIgdGX6lxThrKlt3v+3HJZlaWdmoeQ3vYwpJb9uHExZdVYCIQDCxsdI8EnB
bxjnJscbT4zvqVsq6AmycdbFwgy8RIeVzg==
-----END CERTIFICATE-----

@ -0,0 +1,8 @@
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,7dcc0a8c1d73c8d438184e0928875329
r6yrQrHg6zBZRSjQpe8RzyQALEfiT3/8lMvvPu3BX6yign5skMfCVMXZhzbmAwmR
BJBIX+5hkudR2VN+hrsOyuU7FvIk4gx2c8buIlFObfYXIml0mpuThfm52ciAtOTE
S0hkfYvPcOAjzaDZ+8Po/mYhkODgyvijogn4ioTF/Ss=
-----END EC PRIVATE KEY-----

@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBfTCCASKgAwIBAgIRAJPUE0MTA+fMz6f6i/XYmTwwCgYIKoZIzj0EAwIwHDEa
MBgGA1UEAxMRU21hbGxzdGVwIFJvb3QgQ0EwHhcNMTkwMTA3MjAxMTMwWhcNMjkw
MTA0MjAxMTMwWjAcMRowGAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTBZMBMGByqG
SM49AgEGCCqGSM49AwEHA0IABCOH/PGThn0cMOGDeqDxb22olsdCm8hVdyW9cHQL
jfIYAqpWNh9f7E5umlnxkOy6OEROTtpq7etzfBbzb52loVWjRTBDMA4GA1UdDwEB
/wQEAwIBpjASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBSAjOUIvGH9GXAR
qfLglAc6issAgTAKBggqhkjOPQQDAgNJADBGAiEAjs0yjbQ/9dmGoUn7JS3lE83z
YlnXZ0fHdeNakkIKhQICIQCUENhGZp63pMtm3ipgwp91EM0T7YtKgrFNvDekqufc
Sw==
-----END CERTIFICATE-----

@ -0,0 +1,8 @@
-----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,8ce79d28601b9809905ef7c362a20749
H+pTTL3B5fLYycgHLxFOW0fZsayr7Y+BW8THKf12h8dk0/eOE1wNoX2TuMtpbZgO
lMJdFPL+SAPCCmuZOZIcQDejRHVcYBq1wvrrnw/yfVawXC4xze+J4Y+q0J2WY+rM
xcLGlEOIRZkvdDVGmSitEZBl0Ibk0p9tG++7QGqAvnk=
-----END EC PRIVATE KEY-----
Loading…
Cancel
Save