forked from TrueCloudLab/certificates
Avoid doing unauthenticated requests on the SDK
When step-ca runs with mTLS required on some endpoints, the SDK used in autocert will fail to start because the identity certificate is missing. This certificate is only required to retrieve all roots, in most cases there's only one, and the SDK has access to it.
This commit is contained in:
parent
fbd3fd2145
commit
b0b2e77b0e
3 changed files with 107 additions and 6 deletions
|
@ -63,6 +63,11 @@ func BootstrapClient(ctx context.Context, token string, options ...TLSOption) (*
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version, err := client.Version()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
req, pk, err := CreateSignRequest(token)
|
req, pk, err := CreateSignRequest(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -73,8 +78,14 @@ func BootstrapClient(ctx context.Context, token string, options ...TLSOption) (*
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the tlsConfig have all supported roots on RootCAs
|
// Make sure the tlsConfig have all supported roots on RootCAs.
|
||||||
options = append(options, AddRootsToRootCAs())
|
//
|
||||||
|
// The roots request is only supported if identity certificates are not
|
||||||
|
// required. In all cases the current root is also added after applying all
|
||||||
|
// options too.
|
||||||
|
if !version.RequireClientAuthentication {
|
||||||
|
options = append(options, AddRootsToRootCAs())
|
||||||
|
}
|
||||||
|
|
||||||
transport, err := client.Transport(ctx, sign, pk, options...)
|
transport, err := client.Transport(ctx, sign, pk, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -125,6 +136,11 @@ func BootstrapServer(ctx context.Context, token string, base *http.Server, optio
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version, err := client.Version()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
req, pk, err := CreateSignRequest(token)
|
req, pk, err := CreateSignRequest(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -135,8 +151,14 @@ func BootstrapServer(ctx context.Context, token string, base *http.Server, optio
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the tlsConfig have all supported roots on ClientCAs and RootCAs
|
// Make sure the tlsConfig have all supported roots on RootCAs.
|
||||||
options = append(options, AddRootsToCAs())
|
//
|
||||||
|
// The roots request is only supported if identity certificates are not
|
||||||
|
// required. In all cases the current root is also added after applying all
|
||||||
|
// options too.
|
||||||
|
if !version.RequireClientAuthentication {
|
||||||
|
options = append(options, AddRootsToCAs())
|
||||||
|
}
|
||||||
|
|
||||||
tlsConfig, err := client.GetServerTLSConfig(ctx, sign, pk, options...)
|
tlsConfig, err := client.GetServerTLSConfig(ctx, sign, pk, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -177,6 +199,11 @@ func BootstrapListener(ctx context.Context, token string, inner net.Listener, op
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version, err := client.Version()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
req, pk, err := CreateSignRequest(token)
|
req, pk, err := CreateSignRequest(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -187,8 +214,14 @@ func BootstrapListener(ctx context.Context, token string, inner net.Listener, op
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the tlsConfig have all supported roots on ClientCAs and RootCAs
|
// Make sure the tlsConfig have all supported roots on RootCAs.
|
||||||
options = append(options, AddRootsToCAs())
|
//
|
||||||
|
// The roots request is only supported if identity certificates are not
|
||||||
|
// required. In all cases the current root is also added after applying all
|
||||||
|
// options too.
|
||||||
|
if !version.RequireClientAuthentication {
|
||||||
|
options = append(options, AddRootsToCAs())
|
||||||
|
}
|
||||||
|
|
||||||
tlsConfig, err := client.GetServerTLSConfig(ctx, sign, pk, options...)
|
tlsConfig, err := client.GetServerTLSConfig(ctx, sign, pk, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -15,6 +16,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/api"
|
"github.com/smallstep/certificates/api"
|
||||||
"github.com/smallstep/certificates/authority"
|
"github.com/smallstep/certificates/authority"
|
||||||
|
"github.com/smallstep/certificates/errs"
|
||||||
"go.step.sm/crypto/jose"
|
"go.step.sm/crypto/jose"
|
||||||
"go.step.sm/crypto/randutil"
|
"go.step.sm/crypto/randutil"
|
||||||
)
|
)
|
||||||
|
@ -74,6 +76,30 @@ func startCAServer(configFile string) (*CA, string, error) {
|
||||||
return ca, caURL, nil
|
return ca, caURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mTLSMiddleware(next http.Handler, nonAuthenticatedPaths ...string) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path == "/version" {
|
||||||
|
api.JSON(w, api.VersionResponse{
|
||||||
|
Version: "test",
|
||||||
|
RequireClientAuthentication: true,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range nonAuthenticatedPaths {
|
||||||
|
if strings.HasPrefix(r.URL.Path, s) || strings.HasPrefix(r.URL.Path, "/1.0"+s) {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isMTLS := r.TLS != nil && len(r.TLS.PeerCertificates) > 0
|
||||||
|
if !isMTLS {
|
||||||
|
api.WriteError(w, errs.Unauthorized("missing peer certificate"))
|
||||||
|
} else {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func generateBootstrapToken(ca, subject, sha string) string {
|
func generateBootstrapToken(ca, subject, sha string) string {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
jwk, err := jose.ReadKey("testdata/secrets/ott_mariano_priv.jwk", jose.WithPassword([]byte("password")))
|
jwk, err := jose.ReadKey("testdata/secrets/ott_mariano_priv.jwk", jose.WithPassword([]byte("password")))
|
||||||
|
@ -171,6 +197,15 @@ func TestBootstrapServerWithoutMTLS(t *testing.T) {
|
||||||
token := func() string {
|
token := func() string {
|
||||||
return generateBootstrapToken(srv.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
return generateBootstrapToken(srv.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mtlsServer := startCABootstrapServer()
|
||||||
|
next := mtlsServer.Config.Handler
|
||||||
|
mtlsServer.Config.Handler = mTLSMiddleware(next, "/root/", "/sign")
|
||||||
|
defer mtlsServer.Close()
|
||||||
|
mtlsToken := func() string {
|
||||||
|
return generateBootstrapToken(mtlsServer.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
||||||
|
}
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
token string
|
token string
|
||||||
|
@ -182,6 +217,7 @@ func TestBootstrapServerWithoutMTLS(t *testing.T) {
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", args{context.Background(), token(), &http.Server{}}, false},
|
{"ok", args{context.Background(), token(), &http.Server{}}, false},
|
||||||
|
{"ok mtls", args{context.Background(), mtlsToken(), &http.Server{}}, false},
|
||||||
{"fail", args{context.Background(), "bad-token", &http.Server{}}, true},
|
{"fail", args{context.Background(), "bad-token", &http.Server{}}, true},
|
||||||
{"fail with TLSConfig", args{context.Background(), token(), &http.Server{TLSConfig: &tls.Config{}}}, true},
|
{"fail with TLSConfig", args{context.Background(), token(), &http.Server{TLSConfig: &tls.Config{}}}, true},
|
||||||
}
|
}
|
||||||
|
@ -217,6 +253,15 @@ func TestBootstrapServerWithMTLS(t *testing.T) {
|
||||||
token := func() string {
|
token := func() string {
|
||||||
return generateBootstrapToken(srv.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
return generateBootstrapToken(srv.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mtlsServer := startCABootstrapServer()
|
||||||
|
next := mtlsServer.Config.Handler
|
||||||
|
mtlsServer.Config.Handler = mTLSMiddleware(next, "/root/", "/sign")
|
||||||
|
defer mtlsServer.Close()
|
||||||
|
mtlsToken := func() string {
|
||||||
|
return generateBootstrapToken(mtlsServer.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
||||||
|
}
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
token string
|
token string
|
||||||
|
@ -228,6 +273,7 @@ func TestBootstrapServerWithMTLS(t *testing.T) {
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", args{context.Background(), token(), &http.Server{}}, false},
|
{"ok", args{context.Background(), token(), &http.Server{}}, false},
|
||||||
|
{"ok mtls", args{context.Background(), mtlsToken(), &http.Server{}}, false},
|
||||||
{"fail", args{context.Background(), "bad-token", &http.Server{}}, true},
|
{"fail", args{context.Background(), "bad-token", &http.Server{}}, true},
|
||||||
{"fail with TLSConfig", args{context.Background(), token(), &http.Server{TLSConfig: &tls.Config{}}}, true},
|
{"fail with TLSConfig", args{context.Background(), token(), &http.Server{TLSConfig: &tls.Config{}}}, true},
|
||||||
}
|
}
|
||||||
|
@ -263,6 +309,15 @@ func TestBootstrapClient(t *testing.T) {
|
||||||
token := func() string {
|
token := func() string {
|
||||||
return generateBootstrapToken(srv.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
return generateBootstrapToken(srv.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mtlsServer := startCABootstrapServer()
|
||||||
|
next := mtlsServer.Config.Handler
|
||||||
|
mtlsServer.Config.Handler = mTLSMiddleware(next, "/root/", "/sign")
|
||||||
|
defer mtlsServer.Close()
|
||||||
|
mtlsToken := func() string {
|
||||||
|
return generateBootstrapToken(mtlsServer.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
||||||
|
}
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
token string
|
token string
|
||||||
|
@ -273,6 +328,7 @@ func TestBootstrapClient(t *testing.T) {
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", args{context.Background(), token()}, false},
|
{"ok", args{context.Background(), token()}, false},
|
||||||
|
{"ok mtls", args{context.Background(), mtlsToken()}, false},
|
||||||
{"fail", args{context.Background(), "bad-token"}, true},
|
{"fail", args{context.Background(), "bad-token"}, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -541,6 +597,15 @@ func TestBootstrapListener(t *testing.T) {
|
||||||
token := func() string {
|
token := func() string {
|
||||||
return generateBootstrapToken(srv.URL, "127.0.0.1", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
return generateBootstrapToken(srv.URL, "127.0.0.1", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mtlsServer := startCABootstrapServer()
|
||||||
|
next := mtlsServer.Config.Handler
|
||||||
|
mtlsServer.Config.Handler = mTLSMiddleware(next, "/root/", "/sign")
|
||||||
|
defer mtlsServer.Close()
|
||||||
|
mtlsToken := func() string {
|
||||||
|
return generateBootstrapToken(mtlsServer.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
||||||
|
}
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
token string
|
token string
|
||||||
}
|
}
|
||||||
|
@ -550,6 +615,7 @@ func TestBootstrapListener(t *testing.T) {
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", args{token()}, false},
|
{"ok", args{token()}, false},
|
||||||
|
{"ok mtls", args{mtlsToken()}, false},
|
||||||
{"fail", args{"bad-token"}, true},
|
{"fail", args{"bad-token"}, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -115,6 +115,7 @@ func AddRootCA(cert *x509.Certificate) TLSOption {
|
||||||
if ctx.Config.RootCAs == nil {
|
if ctx.Config.RootCAs == nil {
|
||||||
ctx.Config.RootCAs = x509.NewCertPool()
|
ctx.Config.RootCAs = x509.NewCertPool()
|
||||||
}
|
}
|
||||||
|
ctx.hasRootCA = true
|
||||||
ctx.Config.RootCAs.AddCert(cert)
|
ctx.Config.RootCAs.AddCert(cert)
|
||||||
ctx.mutableConfig.AddImmutableRootCACert(cert)
|
ctx.mutableConfig.AddImmutableRootCACert(cert)
|
||||||
return nil
|
return nil
|
||||||
|
@ -129,6 +130,7 @@ func AddClientCA(cert *x509.Certificate) TLSOption {
|
||||||
if ctx.Config.ClientCAs == nil {
|
if ctx.Config.ClientCAs == nil {
|
||||||
ctx.Config.ClientCAs = x509.NewCertPool()
|
ctx.Config.ClientCAs = x509.NewCertPool()
|
||||||
}
|
}
|
||||||
|
ctx.hasClientCA = true
|
||||||
ctx.Config.ClientCAs.AddCert(cert)
|
ctx.Config.ClientCAs.AddCert(cert)
|
||||||
ctx.mutableConfig.AddImmutableClientCACert(cert)
|
ctx.mutableConfig.AddImmutableClientCACert(cert)
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in a new issue