Skip to content

Commit 6f47efe

Browse files
inkelDanCechjulienduchesne
authored
Generate certificates on the fly (#487)
* Remove certificates These will be generated on the fly before each test run. * Ignore certificate files in testdata/ Signed-off-by: Leandro López (inkel) <[email protected]> * Generate certificate files using Go This removes the additional dependency. Signed-off-by: Leandro López (inkel) <[email protected]> * Generate certificates before testing in Docker These certificates are only used when testing within Docker. With this change, every time we run `make testacc-docker{,-tls}` the certificates will get generated before running the tests, so they always be up to date. Signed-off-by: Leandro López (inkel) <[email protected]> * Extract writing certificate and private key files into a function This makes the code DRY and easier to refactor. Signed-off-by: Leandro López (inkel) <[email protected]> * Use absolute path names Signed-off-by: Leandro López (inkel) <[email protected]> * Reduce duplication by merging cert creation functions Signed-off-by: Leandro López (inkel) <[email protected]> * Reduce duplication even further Signed-off-by: Leandro López (inkel) <[email protected]> * Remove all duplication Signed-off-by: Leandro López (inkel) <[email protected]> * tweak to test cla * Refactor and make it all work! Here are the changes: - Made `docker-compose` always stop and re-create containers to make sure certs were always the most recent - The flow was quite complex (IMO) because of the CA using the same function as server and client certs. So I made it all one function which creates the CA and then the server and client certs - Gave the `x509.KeyUsageCertSign` to the CA cert - Gave common names and DNS names to certificates It all works now! Signed-off-by: Leandro López (inkel) <[email protected]> Co-authored-by: Dan Cech <[email protected]> Co-authored-by: Julien Duchesne <[email protected]>
1 parent 3690db2 commit 6f47efe

File tree

10 files changed

+139
-151
lines changed

10 files changed

+139
-151
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ website/node_modules
2626

2727
website/vendor
2828

29+
testdata/*.crt
30+
testdata/*.key
31+
2932
# Test exclusions
3033
!command/test-fixtures/**/*.tfstate
3134
!command/test-fixtures/**/.terraform/

GNUmakefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ testacc-cloud-instance:
2020
TF_ACC_CLOUD_INSTANCE=true make testacc
2121

2222
testacc-docker:
23+
make -C testdata generate
24+
docker-compose -f ./docker-compose.yml stop
2325
GRAFANA_VERSION=$(GRAFANA_VERSION) \
2426
docker-compose \
2527
-f ./docker-compose.yml \
@@ -28,6 +30,8 @@ testacc-docker:
2830
make testacc-oss
2931

3032
testacc-docker-tls:
33+
make -C testdata generate
34+
docker-compose -f ./docker-compose.yml -f ./docker-compose.tls.yml stop
3135
GRAFANA_VERSION=$(GRAFANA_VERSION) \
3236
docker-compose \
3337
-f ./docker-compose.yml \

testdata/GNUmakefile

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,2 @@
1-
deps:
2-
if ! command -v certin >/dev/null; then \
3-
go get -u github.com/joemiller/certin/cmd/certin; \
4-
fi
5-
6-
generate: deps
7-
@certin create ca.key ca.crt --is-ca --cn "CA"
8-
@certin create grafana.key grafana.crt --signer-key ca.key --signer-cert ca.crt --cn "grafana" --sans "mtls-proxy"
9-
@certin create client.key client.crt --signer-key ca.key --signer-cert ca.crt --cn "client"
1+
generate:
2+
go run .

testdata/ca.crt

Lines changed: 0 additions & 18 deletions
This file was deleted.

testdata/ca.key

Lines changed: 0 additions & 28 deletions
This file was deleted.

testdata/client.crt

Lines changed: 0 additions & 20 deletions
This file was deleted.

testdata/client.key

Lines changed: 0 additions & 28 deletions
This file was deleted.

testdata/grafana.crt

Lines changed: 0 additions & 20 deletions
This file was deleted.

testdata/grafana.key

Lines changed: 0 additions & 28 deletions
This file was deleted.

testdata/main.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"crypto/rand"
6+
"crypto/rsa"
7+
"crypto/x509"
8+
"crypto/x509/pkix"
9+
"encoding/pem"
10+
"fmt"
11+
"math/big"
12+
"net"
13+
"os"
14+
"path/filepath"
15+
"time"
16+
)
17+
18+
func main() {
19+
if err := makeCerts(); err != nil {
20+
fmt.Fprintln(os.Stderr, err)
21+
os.Exit(1)
22+
}
23+
}
24+
25+
func makeCerts() error {
26+
now := time.Now()
27+
28+
serialNumber := big.NewInt(1024)
29+
ca := &x509.Certificate{
30+
SerialNumber: serialNumber,
31+
BasicConstraintsValid: true,
32+
Subject: pkix.Name{
33+
Organization: []string{"Raintank, Inc."},
34+
},
35+
DNSNames: []string{
36+
"grafana",
37+
"mtls-proxy",
38+
},
39+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
40+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
41+
NotBefore: now,
42+
NotAfter: now.Add(1 * time.Hour),
43+
IsCA: true,
44+
}
45+
46+
// create our private and public key
47+
caPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
48+
if err != nil {
49+
return err
50+
}
51+
52+
// create the CA
53+
caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey)
54+
if err != nil {
55+
return err
56+
}
57+
58+
if err := writePEMFiles("ca", caBytes, caPrivKey); err != nil {
59+
return err
60+
}
61+
62+
// Create client and server certificates
63+
for _, name := range []string{"client", "grafana"} {
64+
serialNumber = serialNumber.Add(serialNumber, big.NewInt(1))
65+
// copy CA data
66+
crt := &x509.Certificate{}
67+
*crt = *ca
68+
69+
// overwrite CA data that's not needed for certificates
70+
crt.Subject.CommonName = name
71+
crt.SerialNumber = serialNumber
72+
crt.SubjectKeyId = []byte{1, 2, 3, 4, 6}
73+
crt.KeyUsage = x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
74+
crt.IsCA = false
75+
crt.IPAddresses = []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback}
76+
77+
crtPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
78+
if err != nil {
79+
return fmt.Errorf("cannot generate RSA key for certificate %s: %w", name, err)
80+
}
81+
82+
crtBytes, err := x509.CreateCertificate(rand.Reader, crt, ca, &crtPrivKey.PublicKey, caPrivKey)
83+
if err != nil {
84+
return fmt.Errorf("cannot create certificate %s: %w", name, err)
85+
}
86+
87+
if err := writePEMFiles(name, crtBytes, crtPrivKey); err != nil {
88+
return err
89+
}
90+
91+
}
92+
93+
return nil
94+
}
95+
96+
func writePEMFiles(name string, crtBytes []byte, crtPrivKey *rsa.PrivateKey) error {
97+
if err := writePEMFile(name+".crt", "CERTIFICATE", crtBytes); err != nil {
98+
return fmt.Errorf("cannot write certificate file: %w", err)
99+
}
100+
101+
if err := writePEMFile(name+".key", "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(crtPrivKey)); err != nil {
102+
return fmt.Errorf("cannot write key file: %w", err)
103+
}
104+
105+
return nil
106+
}
107+
108+
func writePEMFile(name, pemType string, data []byte) error {
109+
buf := new(bytes.Buffer)
110+
111+
err := pem.Encode(buf, &pem.Block{
112+
Type: pemType,
113+
Bytes: data,
114+
})
115+
if err != nil {
116+
return fmt.Errorf("cannot PEM encode %s: %w", pemType, err)
117+
}
118+
119+
name, err = filepath.Abs(name)
120+
if err != nil {
121+
return err
122+
}
123+
124+
err = os.WriteFile(name, buf.Bytes(), 0600)
125+
if err != nil {
126+
return fmt.Errorf("cannot write to PEM file %s: %w", name, err)
127+
}
128+
129+
return nil
130+
}

0 commit comments

Comments
 (0)