forked from TrueCloudLab/certificates
Add context to bootstrap methods.
This commit is contained in:
parent
299eea233b
commit
ba88c8c5cb
5 changed files with 99 additions and 41 deletions
|
@ -43,12 +43,22 @@ func Bootstrap(token string) (*Client, error) {
|
||||||
// certificate will automatically rotate if necessary.
|
// certificate will automatically rotate if necessary.
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// srv, err := ca.BootstrapServer(":443", token, handler)
|
// // make sure to cancel the rotation goroutine
|
||||||
|
// ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
// defer cancel()
|
||||||
|
// srv, err := ca.BootstrapServer(ctx, token, &http.Server{
|
||||||
|
// Addr: ":443",
|
||||||
|
// Handler: handler,
|
||||||
|
// })
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// return err
|
// return err
|
||||||
// }
|
// }
|
||||||
// srv.ListenAndServeTLS("", "")
|
// srv.ListenAndServeTLS("", "")
|
||||||
func BootstrapServer(addr, token string, handler http.Handler) (*http.Server, error) {
|
func BootstrapServer(ctx context.Context, token string, base *http.Server) (*http.Server, error) {
|
||||||
|
if base.TLSConfig != nil {
|
||||||
|
return nil, errors.New("server TLSConfig is already set")
|
||||||
|
}
|
||||||
|
|
||||||
client, err := Bootstrap(token)
|
client, err := Bootstrap(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -64,16 +74,13 @@ func BootstrapServer(addr, token string, handler http.Handler) (*http.Server, er
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConfig, err := client.GetServerTLSConfig(context.Background(), sign, pk)
|
tlsConfig, err := client.GetServerTLSConfig(ctx, sign, pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &http.Server{
|
base.TLSConfig = tlsConfig
|
||||||
Addr: addr,
|
return base, nil
|
||||||
Handler: handler,
|
|
||||||
TLSConfig: tlsConfig,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BootstrapClient is a helper function that using the given bootstrap token
|
// BootstrapClient is a helper function that using the given bootstrap token
|
||||||
|
@ -82,12 +89,15 @@ func BootstrapServer(addr, token string, handler http.Handler) (*http.Server, er
|
||||||
// authority. The certificate will automatically rotate if necessary.
|
// authority. The certificate will automatically rotate if necessary.
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// client, err := ca.BootstrapClient(token)
|
// // make sure to cancel the rotation goroutine
|
||||||
|
// ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
// defer cancel()
|
||||||
|
// client, err := ca.BootstrapClient(ctx, token)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// return err
|
// return err
|
||||||
// }
|
// }
|
||||||
// resp, err := client.Get("https://internal.smallstep.com")
|
// resp, err := client.Get("https://internal.smallstep.com")
|
||||||
func BootstrapClient(token string) (*http.Client, error) {
|
func BootstrapClient(ctx context.Context, token string) (*http.Client, error) {
|
||||||
client, err := Bootstrap(token)
|
client, err := Bootstrap(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -103,7 +113,7 @@ func BootstrapClient(token string) (*http.Client, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
transport, err := client.Transport(context.Background(), sign, pk)
|
transport, err := client.Transport(ctx, sign, pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package ca
|
package ca
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -128,25 +130,23 @@ func TestBootstrapServer(t *testing.T) {
|
||||||
token := func() string {
|
token := func() string {
|
||||||
return generateBootstrapToken(srv.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
return generateBootstrapToken(srv.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
||||||
}
|
}
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Write([]byte("ok"))
|
|
||||||
})
|
|
||||||
type args struct {
|
type args struct {
|
||||||
addr string
|
ctx context.Context
|
||||||
token string
|
token string
|
||||||
handler http.Handler
|
base *http.Server
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", args{":0", token(), handler}, false},
|
{"ok", args{context.Background(), token(), &http.Server{}}, false},
|
||||||
{"fail", args{":0", "bad-token", handler}, true},
|
{"fail", args{context.Background(), "bad-token", &http.Server{}}, true},
|
||||||
|
{"fail with TLSConfig", args{context.Background(), token(), &http.Server{TLSConfig: &tls.Config{}}}, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := BootstrapServer(tt.args.addr, tt.args.token, tt.args.handler)
|
got, err := BootstrapServer(tt.args.ctx, tt.args.token, tt.args.base)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("BootstrapServer() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("BootstrapServer() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
|
@ -156,8 +156,11 @@ func TestBootstrapServer(t *testing.T) {
|
||||||
t.Errorf("BootstrapServer() = %v, want nil", got)
|
t.Errorf("BootstrapServer() = %v, want nil", got)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !reflect.DeepEqual(got.Addr, tt.args.addr) {
|
expected := &http.Server{
|
||||||
t.Errorf("BootstrapServer() Addr = %v, want %v", got.Addr, tt.args.addr)
|
TLSConfig: got.TLSConfig,
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, expected) {
|
||||||
|
t.Errorf("BootstrapServer() = %v, want %v", got, expected)
|
||||||
}
|
}
|
||||||
if got.TLSConfig == nil || got.TLSConfig.ClientCAs == nil || got.TLSConfig.RootCAs == nil || got.TLSConfig.GetCertificate == nil || got.TLSConfig.GetClientCertificate == nil {
|
if got.TLSConfig == nil || got.TLSConfig.ClientCAs == nil || got.TLSConfig.RootCAs == nil || got.TLSConfig.GetCertificate == nil || got.TLSConfig.GetClientCertificate == nil {
|
||||||
t.Errorf("BootstrapServer() invalid TLSConfig = %#v", got.TLSConfig)
|
t.Errorf("BootstrapServer() invalid TLSConfig = %#v", got.TLSConfig)
|
||||||
|
@ -174,6 +177,7 @@ func TestBootstrapClient(t *testing.T) {
|
||||||
return generateBootstrapToken(srv.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
return generateBootstrapToken(srv.URL, "subject", "ef742f95dc0d8aa82d3cca4017af6dac3fce84290344159891952d18c53eefe7")
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
|
ctx context.Context
|
||||||
token string
|
token string
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
@ -181,12 +185,12 @@ func TestBootstrapClient(t *testing.T) {
|
||||||
args args
|
args args
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"ok", args{token()}, false},
|
{"ok", args{context.Background(), token()}, false},
|
||||||
{"fail", args{"bad-token"}, true},
|
{"fail", args{context.Background(), "bad-token"}, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := BootstrapClient(tt.args.token)
|
got, err := BootstrapClient(tt.args.ctx, tt.args.token)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("BootstrapClient() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("BootstrapClient() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
|
|
|
@ -119,13 +119,13 @@ tr, err := client.Transport(ctx, sign, pk)
|
||||||
|
|
||||||
To run the example you need to start the certificate authority:
|
To run the example you need to start the certificate authority:
|
||||||
|
|
||||||
```
|
```sh
|
||||||
certificates $ bin/step-ca examples/pki/config/ca.json
|
certificates $ bin/step-ca examples/pki/config/ca.json
|
||||||
2018/11/02 18:29:25 Serving HTTPS on :9000 ...
|
2018/11/02 18:29:25 Serving HTTPS on :9000 ...
|
||||||
```
|
```
|
||||||
|
|
||||||
And just run the client.go with a new token:
|
And just run the client.go with a new token:
|
||||||
```
|
```sh
|
||||||
certificates $ export STEPPATH=examples/pki
|
certificates $ export STEPPATH=examples/pki
|
||||||
certificates $ export STEP_CA_URL=https://localhost:9000
|
certificates $ export STEP_CA_URL=https://localhost:9000
|
||||||
certificates $ go run examples/basic-client/client.go $(step ca new-token client.smallstep.com)
|
certificates $ go run examples/basic-client/client.go $(step ca new-token client.smallstep.com)
|
||||||
|
@ -140,15 +140,46 @@ server.
|
||||||
The examples directory already contains a sample pki configuration with the
|
The examples directory already contains a sample pki configuration with the
|
||||||
password `password` hardcoded, but you can create your own using `step ca init`.
|
password `password` hardcoded, but you can create your own using `step ca init`.
|
||||||
|
|
||||||
First we will start the certificate authority:
|
These examples show the use of other helper methods, they are simple ways to
|
||||||
|
create TLS configured http.Server and http.Client objects. The methods are
|
||||||
|
`BootstrapServer` and `BootstrapClient` and they are used like:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Get a cancelable context to stop the renewal goroutines and timers.
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
// Create an http.Server
|
||||||
|
srv, err := ca.BootstrapServer(ctx, token, &http.Server{
|
||||||
|
Addr: ":8443",
|
||||||
|
Handler: handler,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
srv.ListenAndServeTLS("", "")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Get a cancelable context to stop the renewal goroutines and timers.
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
// Create an http.Client
|
||||||
|
client, err := ca.BootstrapClient(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
resp, err := client.Get("https://localhost:8443")
|
||||||
|
```
|
||||||
|
|
||||||
|
To run the example first we will start the certificate authority:
|
||||||
|
```sh
|
||||||
certificates $ bin/step-ca examples/pki/config/ca.json
|
certificates $ bin/step-ca examples/pki/config/ca.json
|
||||||
2018/11/02 18:29:25 Serving HTTPS on :9000 ...
|
2018/11/02 18:29:25 Serving HTTPS on :9000 ...
|
||||||
```
|
```
|
||||||
|
|
||||||
We will start the server and we will type `password` when step asks for the
|
We will start the server and we will type `password` when step asks for the
|
||||||
provisioner password:
|
provisioner password:
|
||||||
```
|
```sh
|
||||||
certificates $ export STEPPATH=examples/pki
|
certificates $ export STEPPATH=examples/pki
|
||||||
certificates $ export STEP_CA_URL=https://localhost:9000
|
certificates $ export STEP_CA_URL=https://localhost:9000
|
||||||
certificates $ go run examples/bootstrap-server/server.go $(step ca new-token localhost)
|
certificates $ go run examples/bootstrap-server/server.go $(step ca new-token localhost)
|
||||||
|
@ -177,7 +208,7 @@ HTTPS-proxy has similar options --proxy-cacert and --proxy-insecure.
|
||||||
```
|
```
|
||||||
|
|
||||||
But if we use the root certificate it will properly work:
|
But if we use the root certificate it will properly work:
|
||||||
```
|
```sh
|
||||||
certificates $ curl --cacert examples/pki/secrets/root_ca.crt https://localhost:8443
|
certificates $ curl --cacert examples/pki/secrets/root_ca.crt https://localhost:8443
|
||||||
Hello nobody at 2018-11-03 01:49:25.66912 +0000 UTC!!!
|
Hello nobody at 2018-11-03 01:49:25.66912 +0000 UTC!!!
|
||||||
```
|
```
|
||||||
|
@ -186,7 +217,7 @@ Notice that in the response we see `nobody`, this is because the server didn't
|
||||||
detected a TLS client configuration.
|
detected a TLS client configuration.
|
||||||
|
|
||||||
But if we the client with the certificate name Mike we'll see:
|
But if we the client with the certificate name Mike we'll see:
|
||||||
```
|
```sh
|
||||||
certificates $ export STEPPATH=examples/pki
|
certificates $ export STEPPATH=examples/pki
|
||||||
certificates $ export STEP_CA_URL=https://localhost:9000
|
certificates $ export STEP_CA_URL=https://localhost:9000
|
||||||
certificates $ go run examples/bootstrap-client/client.go $(step ca new-token Mike)
|
certificates $ go run examples/bootstrap-client/client.go $(step ca new-token Mike)
|
||||||
|
@ -206,7 +237,7 @@ this provisioner is configured with a default certificate duration of 2 minutes.
|
||||||
If we run the server, and inspect the used certificate, we can verify how it
|
If we run the server, and inspect the used certificate, we can verify how it
|
||||||
rotates after approximately two thirds of the duration has passed.
|
rotates after approximately two thirds of the duration has passed.
|
||||||
|
|
||||||
```
|
```sh
|
||||||
certificates $ export STEPPATH=examples/pki
|
certificates $ export STEPPATH=examples/pki
|
||||||
certificates $ export STEP_CA_URL=https://localhost:9000
|
certificates $ export STEP_CA_URL=https://localhost:9000
|
||||||
certificates $ go run examples/bootstrap-server/server.go $(step ca new-token localhost))
|
certificates $ go run examples/bootstrap-server/server.go $(step ca new-token localhost))
|
||||||
|
@ -222,6 +253,6 @@ number between 0 and 6.
|
||||||
We can use the following command to check the certificate expiration and to make
|
We can use the following command to check the certificate expiration and to make
|
||||||
sure the certificate changes after 74-80 seconds.
|
sure the certificate changes after 74-80 seconds.
|
||||||
|
|
||||||
```
|
```sh
|
||||||
certificates $ step certificate inspect --insecure https://localhost:8443
|
certificates $ step certificate inspect --insecure https://localhost:8443
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
@ -17,7 +18,11 @@ func main() {
|
||||||
|
|
||||||
token := os.Args[1]
|
token := os.Args[1]
|
||||||
|
|
||||||
client, err := ca.BootstrapClient(token)
|
// make sure to cancel the renew goroutine
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
client, err := ca.BootstrapClient(ctx, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -17,13 +18,20 @@ func main() {
|
||||||
|
|
||||||
token := os.Args[1]
|
token := os.Args[1]
|
||||||
|
|
||||||
srv, err := ca.BootstrapServer(":8443", token, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
// make sure to cancel the renew goroutine
|
||||||
name := "nobody"
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
|
defer cancel()
|
||||||
name = r.TLS.PeerCertificates[0].Subject.CommonName
|
|
||||||
}
|
srv, err := ca.BootstrapServer(ctx, token, &http.Server{
|
||||||
w.Write([]byte(fmt.Sprintf("Hello %s at %s!!!", name, time.Now().UTC())))
|
Addr: ":8443",
|
||||||
}))
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
name := "nobody"
|
||||||
|
if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
|
||||||
|
name = r.TLS.PeerCertificates[0].Subject.CommonName
|
||||||
|
}
|
||||||
|
w.Write([]byte(fmt.Sprintf("Hello %s at %s!!!", name, time.Now().UTC())))
|
||||||
|
}),
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue