1package cert
2
3import (
4	"bytes"
5	"crypto/rand"
6	"crypto/rsa"
7	"crypto/tls"
8	"crypto/x509"
9	"crypto/x509/pkix"
10	"encoding/pem"
11	"fmt"
12	"io"
13	"io/ioutil"
14	"log"
15	"math/big"
16	"net/http"
17	"net/http/httptest"
18	"os"
19	"os/exec"
20	"path/filepath"
21	"reflect"
22	"strings"
23	"testing"
24	"time"
25
26	"golang.org/x/net/http2"
27
28	"github.com/fabiolb/fabio/config"
29	consulapi "github.com/hashicorp/consul/api"
30	vaultapi "github.com/hashicorp/vault/api"
31	"github.com/pascaldekloe/goe/verify"
32)
33
34func TestTLSConfig(t *testing.T) {
35	certPEM, keyPEM := makePEM("localhost", time.Minute)
36	cert, err := tls.X509KeyPair(certPEM, keyPEM)
37	if err != nil {
38		t.Fatalf("X509KeyPair: got %s want nil", err)
39	}
40	pool := makeCertPool(certPEM)
41	src := &StaticSource{cert, pool}
42	tlsmin := uint16(0x1000)
43	tlsmax := uint16(0x2000)
44	tlsciphers := []uint16{0x1234, 0x5678}
45	nextprotos := []string{"h2", "http/1.1"}
46
47	cfg, err := TLSConfig(src, false, tlsmin, tlsmax, tlsciphers)
48	if err != nil {
49		t.Fatalf("got error %v want nil", err)
50	}
51	if got, want := cfg.MinVersion, tlsmin; got != want {
52		t.Fatalf("got tls min version %04x want %04x", got, want)
53	}
54	if got, want := cfg.MaxVersion, tlsmax; got != want {
55		t.Fatalf("got tls max version %04x want %04x", got, want)
56	}
57	if got, want := cfg.CipherSuites, tlsciphers; !reflect.DeepEqual(got, want) {
58		t.Fatalf("got tls ciphers %v want %v", got, want)
59	}
60	if got, want := cfg.NextProtos, nextprotos; !reflect.DeepEqual(got, want) {
61		t.Fatalf("got next protos %v want %v", got, want)
62	}
63	if got, want := cfg.ClientCAs, pool; got != want {
64		t.Fatalf("got client CAs %v want %v", got, want)
65	}
66	if got, want := cfg.ClientAuth, tls.RequireAndVerifyClientCert; got != want {
67		t.Fatalf("got client auth type %v want %v", got, want)
68	}
69	if cfg.GetCertificate == nil {
70		t.Fatalf("got GetCertificate() nil want not nil")
71	}
72}
73
74func TestNewSource(t *testing.T) {
75	certsource := func(typ string) config.CertSource {
76		return config.CertSource{
77			Type:         typ,
78			Name:         "name",
79			CertPath:     "cert",
80			KeyPath:      "key",
81			ClientCAPath: "clientca",
82			CAUpgradeCN:  "upgcn",
83			Refresh:      3 * time.Second,
84			Header:       http.Header{"A": []string{"b"}},
85		}
86	}
87	tests := []struct {
88		desc string
89		cfg  config.CertSource
90		src  Source
91		err  string
92	}{
93		{
94			desc: "invalid",
95			cfg: config.CertSource{
96				Type: "invalid",
97			},
98			src: nil,
99			err: `invalid certificate source "invalid"`,
100		},
101		{
102			desc: "file",
103			cfg:  certsource("file"),
104			src: FileSource{
105				CertFile:       "cert",
106				KeyFile:        "key",
107				ClientAuthFile: "clientca",
108				CAUpgradeCN:    "upgcn",
109			},
110		},
111		{
112			desc: "path",
113			cfg:  certsource("path"),
114			src: PathSource{
115				CertPath:     "cert",
116				ClientCAPath: "clientca",
117				CAUpgradeCN:  "upgcn",
118				Refresh:      3 * time.Second,
119			},
120		},
121		{
122			desc: "http",
123			cfg:  certsource("http"),
124			src: HTTPSource{
125				CertURL:     "cert",
126				ClientCAURL: "clientca",
127				CAUpgradeCN: "upgcn",
128				Refresh:     3 * time.Second,
129			},
130		},
131		{
132			desc: "consul",
133			cfg:  certsource("consul"),
134			src: ConsulSource{
135				CertURL:     "cert",
136				ClientCAURL: "clientca",
137				CAUpgradeCN: "upgcn",
138			},
139		},
140		{
141			desc: "vault",
142			cfg:  certsource("vault"),
143			src: &VaultSource{
144				Client:       DefaultVaultClient,
145				CertPath:     "cert",
146				ClientCAPath: "clientca",
147				CAUpgradeCN:  "upgcn",
148				Refresh:      3 * time.Second,
149			},
150		},
151	}
152
153	for i, tt := range tests {
154		tt := tt // capture loop var
155		t.Run(tt.desc, func(t *testing.T) {
156			var errmsg string
157			src, err := NewSource(tt.cfg)
158			if err != nil {
159				errmsg = err.Error()
160			}
161			if got, want := errmsg, tt.err; got != want {
162				t.Fatalf("%d: got %q want %q", i, got, want)
163			}
164			got, want := src, tt.src
165			verify.Values(t, "src", got, want)
166		})
167	}
168}
169
170type StaticSource struct {
171	cert tls.Certificate
172	pool *x509.CertPool
173}
174
175func (s StaticSource) Certificates() chan []tls.Certificate {
176	ch := make(chan []tls.Certificate, 1)
177	ch <- []tls.Certificate{s.cert}
178	close(ch)
179	return ch
180}
181
182func (s StaticSource) LoadClientCAs() (*x509.CertPool, error) {
183	return s.pool, nil
184}
185
186func TestStaticSource(t *testing.T) {
187	certPEM, keyPEM := makePEM("localhost", time.Minute)
188	cert, err := tls.X509KeyPair(certPEM, keyPEM)
189	if err != nil {
190		t.Fatalf("X509KeyPair: got %s want nil", err)
191	}
192	testSource(t, StaticSource{cert, nil}, makeCertPool(certPEM), 0)
193}
194
195func TestFileSource(t *testing.T) {
196	dir := tempDir()
197	defer os.RemoveAll(dir)
198	certPEM, keyPEM := makePEM("localhost", time.Minute)
199	certFile, keyFile := saveCert(dir, "localhost", certPEM, keyPEM)
200	testSource(t, FileSource{CertFile: certFile, KeyFile: keyFile}, makeCertPool(certPEM), 0)
201}
202
203func TestPathSource(t *testing.T) {
204	dir := tempDir()
205	defer os.RemoveAll(dir)
206	certPEM, keyPEM := makePEM("localhost", time.Minute)
207	saveCert(dir, "localhost", certPEM, keyPEM)
208	testSource(t, PathSource{CertPath: dir}, makeCertPool(certPEM), 10*time.Millisecond)
209}
210
211func TestHTTPSource(t *testing.T) {
212	dir := tempDir()
213	defer os.RemoveAll(dir)
214	certPEM, keyPEM := makePEM("localhost", time.Minute)
215	certFile, keyFile := saveCert(dir, "localhost", certPEM, keyPEM)
216	listFile := filepath.Base(certFile) + "\n" + filepath.Base(keyFile) + "\n"
217	writeFile(filepath.Join(dir, "list"), []byte(listFile))
218
219	srv := httptest.NewServer(http.FileServer(http.Dir(dir)))
220	defer srv.Close()
221
222	testSource(t, HTTPSource{CertURL: srv.URL + "/list"}, makeCertPool(certPEM), 500*time.Millisecond)
223}
224
225func TestConsulSource(t *testing.T) {
226	const certURL = "http://127.0.0.1:8500/v1/kv/fabio/test/consul-server"
227
228	// run a consul server if it isn't already running
229	_, err := http.Get("http://127.0.0.1:8500/v1/status/leader")
230	if err != nil {
231		consul := os.Getenv("CONSUL_EXE")
232		if consul == "" {
233			consul = "consul"
234		}
235
236		version, err := exec.Command(consul, "--version").Output()
237		if err != nil {
238			t.Fatalf("Failed to run %s --version", consul)
239		}
240		cr := bytes.IndexRune(version, '\n')
241		t.Logf("Starting %s: %s", consul, string(version[:cr]))
242
243		start := time.Now()
244		cmd := exec.Command(consul, "agent", "-bind", "127.0.0.1", "-server", "-dev")
245		if err := cmd.Start(); err != nil {
246			t.Fatalf("Failed to start consul server. %s", err)
247		}
248		defer cmd.Process.Kill()
249
250		isUp := func() bool {
251			resp, err := http.Get("http://127.0.0.1:8500/v1/status/leader")
252			// /v1/status/leader returns '\n""' while consul is in leader election mode
253			// and '"127.0.0.1:8300"' when not. So we punt by checking the
254			// body length instead of the actual body content :)
255			if err != nil {
256				return false
257			}
258			defer resp.Body.Close()
259
260			if resp.StatusCode != 200 {
261				return false
262			}
263
264			n, err := io.Copy(ioutil.Discard, resp.Body)
265			return err == nil && n > 10
266		}
267
268		// We need give consul ~8-10 seconds to become ready until I've
269		// figured out whether we can speed this up. Make sure that this is
270		// less than the global test timeout in Makefile.
271		if !waitFor(12*time.Second, isUp) {
272			t.Fatalf("Timeout waiting for consul server after %2.1f seconds", time.Since(start).Seconds())
273		}
274		t.Logf("Consul is ready after %2.1f seconds", time.Since(start).Seconds())
275	} else {
276		t.Log("Using existing consul server")
277	}
278
279	config, key, err := parseConsulURL(certURL)
280	if err != nil {
281		t.Fatalf("Failed to parse consul url: %s", err)
282	}
283
284	client, err := consulapi.NewClient(config)
285	if err != nil {
286		t.Fatalf("Failed to create consul client: %s", err)
287	}
288	defer func() { client.KV().DeleteTree(key, &consulapi.WriteOptions{}) }()
289
290	write := func(name string, value []byte) {
291		p := &consulapi.KVPair{Key: key + "/" + name, Value: value}
292		_, err := client.KV().Put(p, &consulapi.WriteOptions{})
293		if err != nil {
294			t.Fatalf("Failed to write %q to consul: %s", p.Key, err)
295		}
296	}
297
298	certPEM, keyPEM := makePEM("localhost", time.Minute)
299	write("localhost-cert.pem", certPEM)
300	write("localhost-key.pem", keyPEM)
301
302	testSource(t, ConsulSource{CertURL: certURL}, makeCertPool(certPEM), 500*time.Millisecond)
303}
304
305// vaultServer starts a vault server in dev mode and waits until is ready.
306func vaultServer(t *testing.T, addr, rootToken string) (*exec.Cmd, *vaultapi.Client) {
307	vault := os.Getenv("VAULT_EXE")
308	if vault == "" {
309		vault = "vault"
310	}
311
312	version, err := exec.Command(vault, "--version").Output()
313	if err != nil {
314		t.Fatalf("Failed to run %s --version", vault)
315	}
316	t.Logf("Starting %s: %q", vault, string(version))
317
318	cmd := exec.Command(vault, "server", "-dev", "-dev-root-token-id="+rootToken, "-dev-listen-address="+addr)
319	if err := cmd.Start(); err != nil {
320		t.Fatalf("Failed to start vault server. %s", err)
321	}
322
323	c, err := vaultapi.NewClient(&vaultapi.Config{Address: "http://" + addr})
324	if err != nil {
325		cmd.Process.Kill()
326		t.Fatalf("NewClient failed: %s", err)
327	}
328	c.SetToken(rootToken)
329
330	isUp := func() bool {
331		ok, err := c.Sys().InitStatus()
332		return err == nil && ok
333	}
334	if !waitFor(time.Second, isUp) {
335		cmd.Process.Kill()
336		t.Fatal("Timeout waiting for vault server")
337	}
338
339	policy := `
340	# Vault < 0.7
341	path "secret/fabio/cert" {
342	  capabilities = ["list"]
343	}
344	# Vault >= 0.7. Note the trailing slash.
345	path "secret/fabio/cert/" {
346	  capabilities = ["list"]
347	}
348
349	path "secret/fabio/cert/*" {
350	  capabilities = ["read"]
351	}
352
353	# Vault >= 0.10. (KV Version 2)
354	path "secret/metadata/fabio/cert/" {
355	  capabilities = ["list"]
356	}
357
358	path "secret/data/fabio/cert/*" {
359	  capabilities = ["read"]
360	}
361
362	path "test-pki/issue/fabio" {
363	  capabilities = ["update"]
364	}
365	`
366
367	if err := c.Sys().PutPolicy("fabio", policy); err != nil {
368		cmd.Process.Kill()
369		t.Fatalf("Could not create policy: %s", err)
370	}
371
372	return cmd, c
373}
374
375func makeToken(t *testing.T, c *vaultapi.Client, wrapTTL string, req *vaultapi.TokenCreateRequest) string {
376	c.SetWrappingLookupFunc(func(string, string) string { return wrapTTL })
377
378	resp, err := c.Auth().Token().Create(req)
379	if err != nil {
380		t.Fatalf("Could not create a token: %s", err)
381	}
382
383	if wrapTTL != "" {
384		if resp.WrapInfo == nil || resp.WrapInfo.Token == "" {
385			t.Fatalf("Could not create a wrapped token")
386		}
387		return resp.WrapInfo.Token
388	}
389
390	if resp.WrapInfo != nil && resp.WrapInfo.Token != "" {
391		t.Fatalf("Got a wrapped token but was not expecting one")
392	}
393
394	return resp.Auth.ClientToken
395}
396
397var vaultTestCases = []struct {
398	desc     string
399	wrapTTL  string
400	req      *vaultapi.TokenCreateRequest
401	dropWarn bool
402}{
403	{
404		desc: "renewable token",
405		req:  &vaultapi.TokenCreateRequest{Lease: "1m", TTL: "1m", Policies: []string{"fabio"}},
406	},
407	{
408		desc:     "non-renewable token",
409		req:      &vaultapi.TokenCreateRequest{Lease: "1m", TTL: "1m", Renewable: new(bool), Policies: []string{"fabio"}},
410		dropWarn: true,
411	},
412	{
413		desc: "renewable orphan token",
414		req:  &vaultapi.TokenCreateRequest{Lease: "1m", TTL: "1m", NoParent: true, Policies: []string{"fabio"}},
415	},
416	{
417		desc:     "non-renewable orphan token",
418		req:      &vaultapi.TokenCreateRequest{Lease: "1m", TTL: "1m", NoParent: true, Renewable: new(bool), Policies: []string{"fabio"}},
419		dropWarn: true,
420	},
421	{
422		desc:    "renewable wrapped token",
423		wrapTTL: "10s",
424		req:     &vaultapi.TokenCreateRequest{Lease: "1m", TTL: "1m", Policies: []string{"fabio"}},
425	},
426	{
427		desc:     "non-renewable wrapped token",
428		wrapTTL:  "10s",
429		req:      &vaultapi.TokenCreateRequest{Lease: "1m", TTL: "1m", Renewable: new(bool), Policies: []string{"fabio"}},
430		dropWarn: true,
431	},
432}
433
434func TestVaultSource(t *testing.T) {
435	const (
436		addr      = "127.0.0.1:58421"
437		rootToken = "token"
438		certPath  = "secret/fabio/cert"
439	)
440
441	// start a vault server
442	vault, client := vaultServer(t, addr, rootToken)
443	defer vault.Process.Kill()
444
445	// create a cert and store it in vault
446	certPEM, keyPEM := makePEM("localhost", time.Minute)
447	data := map[string]interface{}{"cert": string(certPEM), "key": string(keyPEM)}
448
449	var nilSource *VaultSource // for calling helper methods
450
451	mountPath, v2, err := nilSource.isKVv2(certPath, client)
452	if err != nil {
453		t.Fatal(err)
454	}
455
456	p := certPath + "/localhost"
457	if v2 {
458		t.Log("Vault: KV backend: V2")
459		data = map[string]interface{}{
460			"data":    data,
461			"options": map[string]interface{}{},
462		}
463		p = nilSource.addPrefixToVKVPath(p, mountPath, "data")
464	} else {
465		t.Log("Vault: KV backend: V1")
466	}
467	if _, err := client.Logical().Write(p, data); err != nil {
468		t.Fatalf("logical.Write failed: %s", err)
469	}
470
471	pool := makeCertPool(certPEM)
472	timeout := 500 * time.Millisecond
473	for _, tt := range vaultTestCases {
474		tt := tt // capture loop var
475		t.Run(tt.desc, func(t *testing.T) {
476			src := &VaultSource{
477				Client: &vaultClient{
478					addr:  "http://" + addr,
479					token: makeToken(t, client, tt.wrapTTL, tt.req),
480				},
481				CertPath: certPath,
482			}
483
484			// suppress the log warning about a non-renewable token
485			// since this is the expected behavior.
486			dropNotRenewableWarning = tt.dropWarn
487			testSource(t, src, pool, timeout)
488			dropNotRenewableWarning = false
489		})
490	}
491}
492
493func TestVaultPKISource(t *testing.T) {
494	const (
495		addr      = "127.0.0.1:58421"
496		rootToken = "token"
497		certPath  = "test-pki/issue/fabio"
498	)
499
500	// start a vault server
501	vault, client := vaultServer(t, addr, rootToken)
502	defer vault.Process.Kill()
503
504	// mount the PKI backend
505	err := client.Sys().Mount("test-pki", &vaultapi.MountInput{
506		Type: "pki",
507		Config: vaultapi.MountConfigInput{
508			DefaultLeaseTTL: "1h", // default validity period of issued certificates
509			MaxLeaseTTL:     "2h", // maximum validity period of issued certificates
510		},
511	})
512	if err != nil {
513		t.Fatalf("Mount pki backend failed: %s", err)
514	}
515
516	// generate root CA cert
517	resp, err := client.Logical().Write("test-pki/root/generate/internal", map[string]interface{}{
518		"common_name": "fabio-ca.com",
519		"ttl":         "2h",
520	})
521	if err != nil {
522		t.Fatalf("Generate root failed: %s", err)
523	}
524	caPool := makeCertPool([]byte(resp.Data["certificate"].(string)))
525
526	// create role
527	role := filepath.Base(certPath)
528	_, err = client.Logical().Write("test-pki/roles/"+role, map[string]interface{}{
529		"allowed_domains": "",
530		"allow_localhost": true,
531		"allow_ip_sans":   true,
532		"organization":    "Fabio Test",
533	})
534	if err != nil {
535		t.Fatalf("Write role failed: %s", err)
536	}
537
538	for _, tt := range vaultTestCases {
539		tt := tt // capture loop var
540		t.Run(tt.desc, func(t *testing.T) {
541			src := NewVaultPKISource()
542			src.Client = &vaultClient{
543				addr:  "http://" + addr,
544				token: makeToken(t, client, tt.wrapTTL, tt.req),
545			}
546			src.CertPath = certPath
547
548			// suppress the log warning about a non-renewable token
549			// since this is the expected behavior.
550			dropNotRenewableWarning = tt.dropWarn
551			testSource(t, src, caPool, 0)
552			dropNotRenewableWarning = false
553		})
554	}
555}
556
557// testSource runs an integration test by making an HTTPS request
558// to https://localhost/ expecting that the source provides a valid
559// certificate for "localhost". rootCAs is expected to contain a
560// valid root certificate or the server certificate itself so that
561// the HTTPS client can validate the certificate presented by the
562// server.
563func testSource(t *testing.T, source Source, rootCAs *x509.CertPool, sleep time.Duration) {
564	const NoStrictMatch = false
565	srvConfig, err := TLSConfig(source, NoStrictMatch, 0, 0, nil)
566	if err != nil {
567		t.Fatalf("TLSConfig: got %q want nil", err)
568	}
569
570	// give the source some time to initialize if necessary
571	time.Sleep(sleep)
572
573	// create an http client that will accept the root CAs
574	// otherwise the HTTPS client will not verify the
575	// certificate presented by the server.
576	http11 := http11Client(rootCAs)
577	http20, err := http20Client(rootCAs)
578	if err != nil {
579		t.Fatal("http20Client: ", err)
580	}
581
582	// disable log output for the next call to prevent
583	// confusing log messages since they are expected
584	// http: TLS handshake error from 127.0.0.1:55044: remote error: bad certificate
585	log.SetOutput(ioutil.Discard)
586	defer log.SetOutput(os.Stderr)
587
588	// fail calls https://localhost.org/ for which certificate validation
589	// should fail since the hostname differs from the one in the certificate.
590	fail := func(client *http.Client) {
591		_, _, err := roundtrip("localhost.org", srvConfig, client)
592		got, want := err, "x509: certificate is valid for localhost, not localhost.org"
593		if got == nil || !strings.Contains(got.Error(), want) {
594			t.Fatalf("got %q want %q", got, want)
595		}
596	}
597
598	// succeed executes a roundtrip to https://localhost/ which
599	// should return 200 OK and wantBody.
600	succeed := func(client *http.Client, wantBody string) {
601		code, body, err := roundtrip("localhost", srvConfig, client)
602		if err != nil {
603			t.Fatalf("got %v want nil", err)
604		}
605		if got, want := code, 200; got != want {
606			t.Fatalf("got %v want %v", got, want)
607		}
608		if got, want := body, wantBody; got != want {
609			t.Fatalf("got %v want %v", got, want)
610		}
611	}
612
613	// make a call for which certificate validation succeeds.
614	succeed(http11, "OK HTTP/1.1")
615	succeed(http20, "OK HTTP/2.0")
616
617	// now make the call that should fail.
618	fail(http11)
619	fail(http20)
620}
621
622// roundtrip starts a TLS server with the given server configuration and
623// then sends an SNI request with the given serverName.
624func roundtrip(serverName string, srvConfig *tls.Config, client *http.Client) (code int, body string, err error) {
625	// create an HTTPS server and start it. It will be listening on 127.0.0.1
626	srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
627		fmt.Fprint(w, "OK ", r.Proto)
628	}))
629	srv.TLS = srvConfig
630	srv.StartTLS()
631	defer srv.Close()
632
633	// configure SNI
634	client.Transport.(*http.Transport).TLSClientConfig.ServerName = serverName
635
636	// give the tls server some time to start up
637	time.Sleep(10 * time.Millisecond)
638
639	resp, err := client.Get(srv.URL)
640	if err != nil {
641		return 0, "", err
642	}
643	defer resp.Body.Close()
644
645	data, err := ioutil.ReadAll(resp.Body)
646	if err != nil {
647		return 0, "", err
648	}
649	return resp.StatusCode, string(data), nil
650}
651
652// http11Client returns an HTTP client which can only
653// execute HTTP/1.1 requests via TLS.
654func http11Client(rootCAs *x509.CertPool) *http.Client {
655	t := &http.Transport{
656		TLSClientConfig: &tls.Config{
657			RootCAs: rootCAs,
658		},
659	}
660	return &http.Client{Transport: t}
661}
662
663// http20Client returns an HTTP client which can
664// execute HTTP/2.0 requests via TLS if the server
665// supports it.
666func http20Client(rootCAs *x509.CertPool) (*http.Client, error) {
667	t := &http.Transport{
668		TLSClientConfig: &tls.Config{
669			RootCAs: rootCAs,
670		},
671	}
672	if err := http2.ConfigureTransport(t); err != nil {
673		return nil, err
674	}
675	return &http.Client{Transport: t}, nil
676}
677
678func tempDir() string {
679	dir, err := ioutil.TempDir("", "fabio")
680	if err != nil {
681		panic(err.Error())
682	}
683	return dir
684}
685
686func writeFile(filename string, data []byte) {
687	if err := ioutil.WriteFile(filename, data, 0644); err != nil {
688		panic(err.Error())
689	}
690}
691
692func makeCertPool(x ...[]byte) *x509.CertPool {
693	p := x509.NewCertPool()
694	for _, b := range x {
695		// https://github.com/fabiolb/fabio/issues/434
696		if ok := p.AppendCertsFromPEM(b); !ok {
697			panic("failed to add cert from PEM. Is the CN a DNS compatible name?")
698		}
699	}
700	return p
701}
702
703func saveCert(dir, host string, certPEM, keyPEM []byte) (certFile, keyFile string) {
704	certFile, keyFile = filepath.Join(dir, host+"-cert.pem"), filepath.Join(dir, host+"-key.pem")
705	writeFile(certFile, certPEM)
706	writeFile(keyFile, keyPEM)
707	return certFile, keyFile
708}
709
710// makePEM creates a self-signed RSA certificate as two PEM blocks.
711// taken from crypto/tls/generate_cert.go
712func makePEM(host string, validFor time.Duration) (certPEM, keyPEM []byte) {
713	const bits = 1024
714	priv, err := rsa.GenerateKey(rand.Reader, bits)
715	if err != nil {
716		panic("Failed to generate private key: " + err.Error())
717	}
718
719	template := x509.Certificate{
720		SerialNumber: big.NewInt(1),
721		Subject: pkix.Name{
722			Organization: []string{"Fabio Co"},
723		},
724		NotBefore:             time.Now(),
725		NotAfter:              time.Now().Add(validFor),
726		IsCA:                  true,
727		DNSNames:              []string{host},
728		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
729		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
730		BasicConstraintsValid: true,
731	}
732
733	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
734	if err != nil {
735		panic("Failed to create certificate: " + err.Error())
736	}
737
738	var cert, key bytes.Buffer
739	pem.Encode(&cert, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
740	pem.Encode(&key, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
741	return cert.Bytes(), key.Bytes()
742}
743
744func makeCert(host string, validFor time.Duration) tls.Certificate {
745	certPEM, keyPEM := makePEM(host, validFor)
746	cert, err := tls.X509KeyPair(certPEM, keyPEM)
747	if err != nil {
748		panic("Failed to create certificate: " + err.Error())
749	}
750	return cert
751}
752
753func waitFor(timeout time.Duration, up func() bool) bool {
754	until := time.Now().Add(timeout)
755	for {
756		if time.Now().After(until) {
757			return false
758		}
759		if up() {
760			return true
761		}
762		time.Sleep(100 * time.Millisecond)
763	}
764}
765