author | Alberto Bertogli
<albertito@blitiri.com.ar> 2020-07-28 00:36:36 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2020-07-28 00:38:12 UTC |
parent | 31265e77643fdfa02fa25b727d5d98e5c6b24367 |
internal/httpresolver/resolver.go | +1 | -1 |
tests/external.sh | +30 | -0 |
tests/generate_cert.go | +123 | -0 |
diff --git a/internal/httpresolver/resolver.go b/internal/httpresolver/resolver.go index 6bf2cac..665fa92 100644 --- a/internal/httpresolver/resolver.go +++ b/internal/httpresolver/resolver.go @@ -73,7 +73,7 @@ func (r *httpsResolver) Init() error { } transport.TLSClientConfig = &tls.Config{ - ClientCAs: pool, + RootCAs: pool, } } diff --git a/tests/external.sh b/tests/external.sh index ccd5f5b..732e3b8 100755 --- a/tests/external.sh +++ b/tests/external.sh @@ -87,6 +87,16 @@ function get() { wget -O.wget.out $URL > .wget.log 2>&1 } +function generate_certs() { + mkdir -p .certs/localhost + ( + cd .certs/localhost + go run ../../tests/generate_cert.go \ + -ca -duration=1h --host=localhost + ) +} + + echo "## Misc" # Missing arguments (expect it to fail). dnss @@ -137,6 +147,26 @@ kill $PID kill $HTTP_PID +echo "## HTTPS with custom certificates" +generate_certs +dnss -enable_https_to_dns \ + -https_key .certs/localhost/privkey.pem \ + -https_cert .certs/localhost/fullchain.pem \ + -https_server_addr "localhost:1999" +HTTP_PID=$PID +mv .dnss.log .dnss.http.log +wait_until_ready tcp 1999 + +dnss -enable_dns_to_https -dns_listen_addr "localhost:1053" \ + -https_client_cafile .certs/localhost/fullchain.pem \ + -https_upstream "https://localhost:1999/dns-query" + +resolve + +kill $PID +kill $HTTP_PID + + # DoH integration test against some publicly available servers. # https://github.com/curl/curl/wiki/DNS-over-HTTPS#publicly-available-servers # Note not all of the ones in the list are actually functional. diff --git a/tests/generate_cert.go b/tests/generate_cert.go new file mode 100644 index 0000000..4fbf706 --- /dev/null +++ b/tests/generate_cert.go @@ -0,0 +1,123 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +// Generate a self-signed X.509 certificate for a TLS server. Outputs to +// 'cert.pem' and 'key.pem' and will overwrite existing files. + +package main + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "flag" + "fmt" + "log" + "math/big" + "net" + "os" + "strings" + "time" + + "golang.org/x/net/idna" +) + +var ( + host = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for") + validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011") + validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for") + isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority") +) + +func main() { + flag.Parse() + + if len(*host) == 0 { + log.Fatalf("Missing required --host parameter") + } + + priv, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + log.Fatalf("failed to generate private key: %s", err) + } + + var notBefore time.Time + if len(*validFrom) == 0 { + notBefore = time.Now() + } else { + notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err) + os.Exit(1) + } + } + + notAfter := notBefore.Add(*validFor) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + log.Fatalf("failed to generate serial number: %s", err) + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + hosts := strings.Split(*host, ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + // We use IDNA-encoded DNS names, otherwise the TLS library won't + // load the certificates. + ih, err := idna.ToASCII(h) + if err != nil { + log.Fatalf("host %q cannot be IDNA-encoded: %v", h, err) + } + template.DNSNames = append(template.DNSNames, ih) + } + } + + if *isCA { + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + log.Fatalf("Failed to create certificate: %s", err) + } + + certOut, err := os.Create("fullchain.pem") + if err != nil { + log.Fatalf("failed to open fullchain.pem for writing: %s", err) + } + pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + certOut.Close() + + keyOut, err := os.OpenFile("privkey.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + log.Fatalf("failed to open privkey.pem for writing: %s", err) + return + } + + block := &pem.Block{Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(priv)} + pem.Encode(keyOut, block) + keyOut.Close() +}