Skip to content

Commit 07e71f7

Browse files
authored
Add support for custom CA certs (#323)
This adds a new environment variable 'EXTRA_CA_CERTS' which can contain a comma-separated list of file paths to CA certs. These certs will be used in addition to the system defaults.
1 parent 80e1750 commit 07e71f7

File tree

4 files changed

+57
-3
lines changed

4 files changed

+57
-3
lines changed

cmd/verify/main.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package main
22

33
import (
44
"context"
5+
"encoding/csv"
56
"os"
7+
"strings"
68

79
"github.com/rs/zerolog/log"
810

@@ -27,12 +29,22 @@ func main() {
2729
firestoreProjectID = v
2830
}
2931

32+
var extraCaCerts []string
33+
if v, ok := os.LookupEnv("EXTRA_CA_CERTS"); ok {
34+
var err error
35+
extraCaCerts, err = csv.NewReader(strings.NewReader(v)).Read()
36+
if err != nil {
37+
log.Fatal().Err(err).Msg("failed to parse $EXTRA_CA_CERTS (expected comma-separated list of file paths)")
38+
}
39+
}
40+
3041
srv := verify.New(
3142
verify.WithBindAddress(addr),
3243
verify.WithFirestoreProjectID(firestoreProjectID),
3344
verify.WithJWKSEndpoint(jwksEndpoint),
3445
verify.WithExpectedJWTIssuer(os.Getenv("EXPECTED_JWT_ISSUER")),
3546
verify.WithExpectedJWTAudience(os.Getenv("EXPECTED_JWT_AUDIENCE")),
47+
verify.WithExtraCACerts(extraCaCerts...),
3648
)
3749
err := srv.Run(context.Background())
3850
if err != nil {

config.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type config struct {
1515
jwksEndpoint string
1616
expectedJWTIssuer string
1717
expectedJWTAudience string
18+
extraCACerts []string
1819
}
1920

2021
// An Option customizes the config.
@@ -57,6 +58,15 @@ func WithFirestoreProjectID(projectID string) Option {
5758
}
5859
}
5960

61+
// WithExtraCACerts adds paths to custom CA certificates to the config.
62+
// Certificates added with this option will be used in addition to the system
63+
// default pool.
64+
func WithExtraCACerts(paths ...string) Option {
65+
return func(cfg *config) {
66+
cfg.extraCACerts = append(cfg.extraCACerts, paths...)
67+
}
68+
}
69+
6070
func getConfig(options ...Option) *config {
6171
cfg := new(config)
6272
WithBindAddress(DefaultBindAddress)(cfg)

tls.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,21 @@ import (
1414

1515
const maxRemoteWait = 5 * time.Second
1616

17+
type tlsVerifierOptions struct {
18+
rootCAs *x509.CertPool
19+
}
20+
1721
type tlsVerifier struct {
22+
tlsVerifierOptions
1823
mu sync.Mutex
1924
errors map[string]error
2025
}
2126

22-
func newTLSVerifier() *tlsVerifier {
23-
return &tlsVerifier{errors: make(map[string]error)}
27+
func newTLSVerifier(opts tlsVerifierOptions) *tlsVerifier {
28+
return &tlsVerifier{
29+
tlsVerifierOptions: opts,
30+
errors: make(map[string]error),
31+
}
2432
}
2533

2634
func (v *tlsVerifier) DialTLSContext(ctx context.Context, network, addr string) (net.Conn, error) {
@@ -57,6 +65,7 @@ func (v *tlsVerifier) VerifyPeerCertificate(serverName string, rawCerts [][]byte
5765

5866
opts := x509.VerifyOptions{
5967
DNSName: serverName,
68+
Roots: v.rootCAs,
6069
Intermediates: x509.NewCertPool(),
6170
}
6271
for _, cert := range certs[1:] {

verify.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package verify
22

33
import (
44
"context"
5+
"crypto/x509"
56
"net"
67
"net/http"
8+
"os"
79

810
"cloud.google.com/go/firestore"
911
"github.com/go-chi/chi"
@@ -26,9 +28,30 @@ type Server struct {
2628
// New creates a new Server.
2729
func New(options ...Option) *Server {
2830
cfg := getConfig(options...)
31+
32+
var verifierOpts tlsVerifierOptions
33+
if len(cfg.extraCACerts) > 0 {
34+
pool, err := x509.SystemCertPool()
35+
if err != nil {
36+
log.Fatal().Err(err).Msg("failed to load system CA certs")
37+
}
38+
for _, certPath := range cfg.extraCACerts {
39+
cert, err := os.ReadFile(certPath)
40+
if err != nil {
41+
log.Fatal().Err(err).Str("path", certPath).Msg("failed to read CA cert")
42+
} else {
43+
log.Info().Str("path", certPath).Msg("adding extra CA cert")
44+
}
45+
ok := pool.AppendCertsFromPEM(cert)
46+
if !ok {
47+
log.Warn().Str("path", certPath).Msg("no CA certs found in file")
48+
}
49+
}
50+
verifierOpts.rootCAs = pool
51+
}
2952
return &Server{
3053
cfg: cfg,
31-
tlsVerifier: newTLSVerifier(),
54+
tlsVerifier: newTLSVerifier(verifierOpts),
3255
}
3356
}
3457

0 commit comments

Comments
 (0)