From 091506a994e9a2ecd8fa3eeed7b950bfd41048cc Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Fri, 2 Nov 2018 18:54:49 -0700 Subject: [PATCH] Add bootstrap helpers that uses just a token. --- ca/bootstrap.go | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 ca/bootstrap.go diff --git a/ca/bootstrap.go b/ca/bootstrap.go new file mode 100644 index 00000000..522fa91b --- /dev/null +++ b/ca/bootstrap.go @@ -0,0 +1,114 @@ +package ca + +import ( + "context" + "net/http" + "strings" + + "github.com/pkg/errors" + "github.com/smallstep/cli/jose" + "gopkg.in/square/go-jose.v2/jwt" +) + +type tokenClaims struct { + SHA string `json:"sha"` + jose.Claims +} + +// Bootstrap is a helper function that initializes a client with the +// configuration in the bootstrap token. +func Bootstrap(token string) (*Client, error) { + tok, err := jwt.ParseSigned(token) + if err != nil { + return nil, errors.Wrap(err, "error parsing token") + } + var claims tokenClaims + if err := tok.UnsafeClaimsWithoutVerification(&claims); err != nil { + return nil, errors.Wrap(err, "error parsing ott") + } + + // Validate bootstrap token + switch { + case len(claims.SHA) == 0: + return nil, errors.New("invalid bootstrap token: sha claim is not present") + case !strings.HasPrefix(strings.ToLower(claims.Audience[0]), "http"): + return nil, errors.New("invalid bootstrap token: aud claim is not a url") + } + + return NewClient(claims.Audience[0], WithRootSHA256(claims.SHA)) +} + +// BootstrapServer is a helper function that returns an http.Server configured +// with the given address and handler, and prepared to use TLS connections. The +// certificate will automatically rotate if necessary. +// +// Usage: +// srv, err := ca.BootstrapServer(":443", token, handler) +// if err != nil { +// return err +// } +// srv.ListenAndServeTLS("", "") +func BootstrapServer(addr, token string, handler http.Handler) (*http.Server, error) { + client, err := Bootstrap(token) + if err != nil { + return nil, err + } + + req, pk, err := CreateSignRequest(token) + if err != nil { + return nil, err + } + + sign, err := client.Sign(req) + if err != nil { + return nil, err + } + + tlsConfig, err := client.GetServerTLSConfig(context.Background(), sign, pk) + if err != nil { + return nil, err + } + + return &http.Server{ + Addr: addr, + Handler: handler, + TLSConfig: tlsConfig, + }, nil +} + +// BootstrapClient is a helper function that using the given bootstrap token +// return an http.Client configured with a Transport prepared to do TLS +// connections using the client certificate returned by the certificate +// authority. The certificate will automatically rotate if necessary. +// +// Usage: +// client, err := ca.BootstrapClient(token) +// if err != nil { +// return err +// } +// resp, err := client.Get("https://internal.smallstep.com") +func BootstrapClient(token string) (*http.Client, error) { + client, err := Bootstrap(token) + if err != nil { + return nil, err + } + + req, pk, err := CreateSignRequest(token) + if err != nil { + return nil, err + } + + sign, err := client.Sign(req) + if err != nil { + return nil, err + } + + transport, err := client.Transport(context.Background(), sign, pk) + if err != nil { + return nil, err + } + + return &http.Client{ + Transport: transport, + }, nil +}