initial commit [141.14.140.180,mike]
This commit is contained in:
+160
@@ -0,0 +1,160 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// resetTransport clears the memoised client so a test can change the
|
||||
// verification mode.
|
||||
func resetTransport(t *testing.T, mode caMode) {
|
||||
t.Helper()
|
||||
client, clientMode, caCurrent = nil, -1, mode
|
||||
opt.timeout = 10
|
||||
t.Cleanup(func() {
|
||||
client, clientMode, caCurrent = nil, -1, caSystem
|
||||
opt.cacert, opt.insecure = "", false
|
||||
})
|
||||
}
|
||||
|
||||
// tlsServer returns a server with a certificate no public CA has signed, plus
|
||||
// that certificate as PEM - the stand-in for a root the system does not know.
|
||||
func tlsServer(t *testing.T) (*httptest.Server, []byte) {
|
||||
t.Helper()
|
||||
srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
io.WriteString(w, `{"tag_name":"v1.2.3"}`)
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: srv.Certificate().Raw})
|
||||
return srv, certPEM
|
||||
}
|
||||
|
||||
func TestSystemStoreRejectsUnknownIssuer(t *testing.T) {
|
||||
srv, _ := tlsServer(t)
|
||||
resetTransport(t, caSystem)
|
||||
|
||||
// Nothing to fall back to: the bundled list does not know it either.
|
||||
old := caBundle
|
||||
caBundle = nil
|
||||
t.Cleanup(func() { caBundle = old })
|
||||
|
||||
_, err := httpGet(srv.URL, nil, nil)
|
||||
if err == nil {
|
||||
t.Fatal("expected the system store to reject this certificate")
|
||||
}
|
||||
if !isUnknownAuthority(err) {
|
||||
t.Errorf("not recognised as an unknown authority: %v", err)
|
||||
}
|
||||
if !isPermanent(err) {
|
||||
t.Error("a rejected certificate must not be retried")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "--cacert") {
|
||||
t.Errorf("error should point at the way out, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// The failure from the old box: the system store predates the issuer's root.
|
||||
// upd must notice, switch to its own list and carry on.
|
||||
func TestFallbackToBundledCAs(t *testing.T) {
|
||||
srv, certPEM := tlsServer(t)
|
||||
resetTransport(t, caSystem)
|
||||
|
||||
old := caBundle
|
||||
caBundle = certPEM
|
||||
t.Cleanup(func() { caBundle = old })
|
||||
|
||||
resp, err := httpGet(srv.URL, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("fallback did not happen: %v", err)
|
||||
}
|
||||
if resp.status != http.StatusOK {
|
||||
t.Errorf("status %d", resp.status)
|
||||
}
|
||||
if caCurrent != caBundled {
|
||||
t.Errorf("mode is %v, want the bundled list", caCurrent)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacertFile(t *testing.T) {
|
||||
srv, certPEM := tlsServer(t)
|
||||
|
||||
path := filepath.Join(t.TempDir(), "ca.pem")
|
||||
if err := os.WriteFile(path, certPEM, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
resetTransport(t, caFile)
|
||||
opt.cacert = path
|
||||
if _, err := httpGet(srv.URL, nil, nil); err != nil {
|
||||
t.Fatalf("--cacert did not verify: %v", err)
|
||||
}
|
||||
|
||||
// A bundle without the right root must still fail, and say which file.
|
||||
resetTransport(t, caFile)
|
||||
opt.cacert = filepath.Join(t.TempDir(), "empty.pem")
|
||||
os.WriteFile(opt.cacert, []byte("not a certificate\n"), 0o644)
|
||||
if _, err := httpGet(srv.URL, nil, nil); err == nil {
|
||||
t.Error("expected an error for a bundle without certificates")
|
||||
} else if !strings.Contains(err.Error(), "no certificates found") {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsecureSkipsVerification(t *testing.T) {
|
||||
srv, _ := tlsServer(t)
|
||||
resetTransport(t, caNone)
|
||||
|
||||
if _, err := httpGet(srv.URL, nil, nil); err != nil {
|
||||
t.Fatalf("--insecure should have connected anyway: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// The bundled list has to be a usable pool and carry the roots that the old
|
||||
// machines are missing - that is the whole point of embedding it.
|
||||
func TestBundledCAList(t *testing.T) {
|
||||
pool := bundledPool()
|
||||
if pool == nil {
|
||||
t.Fatal("the embedded CA bundle does not parse")
|
||||
}
|
||||
|
||||
var subjects []string
|
||||
for block, rest := pem.Decode(caBundle); block != nil; block, rest = pem.Decode(rest) {
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("unparsable certificate in the bundle: %v", err)
|
||||
}
|
||||
subjects = append(subjects, cert.Subject.CommonName)
|
||||
}
|
||||
if len(subjects) < 100 {
|
||||
t.Errorf("only %d roots in the bundle, expected the full Mozilla list", len(subjects))
|
||||
}
|
||||
for _, want := range []string{"ISRG Root X1", "ISRG Root X2"} {
|
||||
found := false
|
||||
for _, s := range subjects {
|
||||
if s == want {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("%s missing - Let's Encrypt hosts would still fail", want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPermanentClassification(t *testing.T) {
|
||||
// A DNS failure for a name that cannot exist is permanent, a plain
|
||||
// connection refused is not: the second one is worth another attempt.
|
||||
resetTransport(t, caSystem)
|
||||
if _, err := httpGet("https://no-such-host.invalid/x", nil, nil); err == nil {
|
||||
t.Fatal("expected a DNS failure")
|
||||
} else if !isPermanent(err) {
|
||||
t.Errorf("an unresolvable host should not be retried: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user