Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 01:00 UTC
| 1 | package caddytls |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "crypto/ecdsa" |
| 6 | "crypto/elliptic" |
| 7 | "crypto/rand" |
| 8 | "crypto/x509" |
| 9 | "net/http" |
| 10 | "net/http/httptest" |
| 11 | "sync/atomic" |
| 12 | "testing" |
| 13 | "time" |
| 14 | |
| 15 | "github.com/caddyserver/caddy/v2" |
| 16 | _ "github.com/caddyserver/caddy/v2/modules/internal/network" |
| 17 | "github.com/caddyserver/certmagic" |
| 18 | "github.com/mholt/acmez/v3/acme" |
| 19 | "go.uber.org/zap" |
| 20 | ) |
| 21 | |
| 22 | func TestACMEIssuerExpandPlaceholders(t *testing.T) { |
| 23 | t.Setenv("CADDY_TEST_CA_URL", "https://acme.example.com/directory") |
| 24 | t.Setenv("CADDY_TEST_TEST_CA_URL", "https://acme2.example.com/directory") |
| 25 | t.Setenv("CADDY_TEST_EAB_KEY_ID", "example-key-id") |
| 26 | t.Setenv("CADDY_TEST_EAB_MAC_KEY", "example-mac-key") |
| 27 | |
| 28 | caddyCtx, cancel := caddy.NewContext(caddy.Context{Context: t.Context()}) |
| 29 | defer cancel() |
| 30 | |
| 31 | iss := &ACMEIssuer{ |
| 32 | CA: "{env.CADDY_TEST_CA_URL}", |
| 33 | TestCA: "{env.CADDY_TEST_TEST_CA_URL}", |
| 34 | ExternalAccount: &acme.EAB{ |
| 35 | KeyID: "{env.CADDY_TEST_EAB_KEY_ID}", |
| 36 | MACKey: "{env.CADDY_TEST_EAB_MAC_KEY}", |
| 37 | }, |
| 38 | } |
| 39 | |
| 40 | if err := iss.Provision(caddyCtx); err != nil { |
| 41 | t.Fatalf("Provision() returned unexpected error: %v", err) |
| 42 | } |
| 43 | |
| 44 | if want := "https://acme.example.com/directory"; iss.CA != want { |
| 45 | t.Errorf("CA: got %q, want %q", iss.CA, want) |
| 46 | } |
| 47 | if want := "https://acme2.example.com/directory"; iss.TestCA != want { |
| 48 | t.Errorf("TestCA: got %q, want %q", iss.TestCA, want) |
| 49 | } |
| 50 | if want := "example-key-id"; iss.ExternalAccount.KeyID != want { |
| 51 | t.Errorf("ExternalAccount.KeyID: got %q, want %q", iss.ExternalAccount.KeyID, want) |
| 52 | } |
| 53 | if want := "example-mac-key"; iss.ExternalAccount.MACKey != want { |
| 54 | t.Errorf("ExternalAccount.MACKey: got %q, want %q", iss.ExternalAccount.MACKey, want) |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | // The HTTP app provisions ACME issuers again to fill in its ports and then |
| 59 | // rebuilds the CertMagic config; requests must still use the configured proxy (#8283). |
| 60 | func TestACMEIssuerNetworkProxyAfterRebuildCertMagic(t *testing.T) { |
| 61 | var proxied atomic.Int32 |
| 62 | proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 63 | if r.Method == http.MethodConnect && r.Host == "acme.invalid:443" { |
| 64 | proxied.Add(1) |
| 65 | } |
| 66 | http.Error(w, "test proxy", http.StatusBadGateway) |
| 67 | })) |
| 68 | defer proxy.Close() |
| 69 | |
| 70 | certCacheMu.Lock() |
| 71 | prevCache := certCache |
| 72 | certCache = certmagic.NewCache(certmagic.CacheOptions{ |
| 73 | GetConfigForCert: func(certmagic.Certificate) (*certmagic.Config, error) { return nil, nil }, |
| 74 | }) |
| 75 | certCacheMu.Unlock() |
| 76 | defer func() { |
| 77 | certCacheMu.Lock() |
| 78 | certCache.Stop() |
| 79 | certCache = prevCache |
| 80 | certCacheMu.Unlock() |
| 81 | }() |
| 82 | |
| 83 | caddyCtx, cancel := caddy.NewContext(caddy.Context{Context: t.Context()}) |
| 84 | defer cancel() |
| 85 | tlsApp := &TLS{ctx: caddyCtx, logger: zap.NewNop()} |
| 86 | |
| 87 | iss := &ACMEIssuer{ |
| 88 | CA: "https://acme.invalid/directory", |
| 89 | NetworkProxyRaw: []byte(`{"from":"url","url":"` + proxy.URL + `"}`), |
| 90 | } |
| 91 | if err := iss.Provision(caddyCtx); err != nil { |
| 92 | t.Fatalf("provisioning issuer: %v", err) |
| 93 | } |
| 94 | ap := &AutomationPolicy{ |
| 95 | Issuers: []certmagic.Issuer{iss}, |
| 96 | storage: &certmagic.FileStorage{Path: t.TempDir()}, |
| 97 | } |
| 98 | if err := ap.Provision(tlsApp); err != nil { |
| 99 | t.Fatalf("provisioning automation policy: %v", err) |
| 100 | } |
| 101 | |
| 102 | // what the HTTP app's fillInACMEIssuer and createAutomationPolicies do |
| 103 | if err := iss.Provision(caddyCtx); err != nil { |
| 104 | t.Fatalf("re-provisioning issuer: %v", err) |
| 105 | } |
| 106 | if err := ap.RebuildCertMagic(tlsApp); err != nil { |
| 107 | t.Fatalf("rebuilding certmagic config: %v", err) |
| 108 | } |
| 109 | |
| 110 | key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) |
| 111 | if err != nil { |
| 112 | t.Fatal(err) |
| 113 | } |
| 114 | csrDER, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{DNSNames: []string{"example.com"}}, key) |
| 115 | if err != nil { |
| 116 | t.Fatal(err) |
| 117 | } |
| 118 | csr, err := x509.ParseCertificateRequest(csrDER) |
| 119 | if err != nil { |
| 120 | t.Fatal(err) |
| 121 | } |
| 122 | |
| 123 | ctx, cancelIssue := context.WithTimeout(t.Context(), 10*time.Second) |
| 124 | defer cancelIssue() |
| 125 | if _, err := iss.Issue(ctx, csr); err == nil { |
| 126 | t.Fatal("expected issuance to fail against the test proxy") |
| 127 | } |
| 128 | if proxied.Load() == 0 { |
| 129 | t.Error("ACME request did not go through the configured network proxy") |
| 130 | } |
| 131 | } |