@marcel91200 / caddy Mirror · read-only / commits / 57a708d1
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 13, 21:45 UTC

caddytls: Support external certificate Managers (like Tailscale) (#4541)

Huge thank-you to Tailscale (https://tailscale.com) for making this change possible! This is a great feature for Caddy and Tailscale is a great fit for a standard implementation. * caddytls: GetCertificate modules; Tailscale * Caddyfile support for get_certificate Also fix AP provisioning in case of empty subject list (persist loaded module on struct, much like Issuers, to surive reprovisioning). And implement start of HTTP cert getter, still WIP. * Update modules/caddytls/automation.go Co-authored-by: Francis Lavoie <lavofr@gmail.com> * Use tsclient package, check status for name * Implement HTTP cert getter And use reuse CertMagic's PEM functions for private keys. * Remove cache option from Tailscale getter Tailscale does its own caching and we don't need the added complexity... for now, at least. * Several updates - Option to disable cert automation in auto HTTPS - Support multiple cert managers - Remove cache feature from cert manager modules - Minor improvements to auto HTTPS logging * Run go mod tidy * Try to get certificates from Tailscale implicitly Only for domains ending in .ts.net. I think this is really cool! Co-authored-by: Francis Lavoie <lavofr@gmail.com>

Matt Holt committed Feb 17, 2022 at 15:40 UTC 57a708d189cfe4ccc20ae92df95dd35e52a434a8
13 files changed +450 -158
caddyconfig/httpcaddyfile/builtins.go
+24
@@ -82,6 +82,7 @@ func parseBind(h Helper) ([]ConfigValue, error) {
82 // on_demand
83 // eab <key_id> <mac_key>
84 // issuer <module_name> [...]
85 +// get_certificate <module_name> [...]
86 // }
87 //
88 func parseTLS(h Helper) ([]ConfigValue, error) {
@@ -93,6 +94,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
94 var keyType string
95 var internalIssuer *caddytls.InternalIssuer
96 var issuers []certmagic.Issuer
97 + var certManagers []certmagic.CertificateManager
98 var onDemand bool
99
100 for h.Next() {
@@ -307,6 +309,22 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
309 }
310 issuers = append(issuers, issuer)
311
312 + case "get_certificate":
313 + if !h.NextArg() {
314 + return nil, h.ArgErr()
315 + }
316 + modName := h.Val()
317 + modID := "tls.get_certificate." + modName
318 + unm, err := caddyfile.UnmarshalModule(h.Dispenser, modID)
319 + if err != nil {
320 + return nil, err
321 + }
322 + certManager, ok := unm.(certmagic.CertificateManager)
323 + if !ok {
324 + return nil, h.Errf("module %s (%T) is not a certmagic.CertificateManager", modID, unm)
325 + }
326 + certManagers = append(certManagers, certManager)
327 +
328 case "dns":
329 if !h.NextArg() {
330 return nil, h.ArgErr()
@@ -453,6 +471,12 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
471 Value: true,
472 })
473 }
474 + for _, certManager := range certManagers {
475 + configVals = append(configVals, ConfigValue{
476 + Class: "tls.cert_manager",
477 + Value: certManager,
478 + })
479 + }
480
481 // custom certificate selection
482 if len(certSelector.AnyTag) > 0 {
caddyconfig/httpcaddyfile/httptype.go
+6 -5
@@ -446,13 +446,14 @@ func (st *ServerType) serversFromPairings(
446 // handle the auto_https global option
447 if autoHTTPS != "on" {
448 srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
449 - if autoHTTPS == "off" {
449 + switch autoHTTPS {
450 + case "off":
451 srv.AutoHTTPS.Disabled = true
451 - }
452 - if autoHTTPS == "disable_redirects" {
452 + case "disable_redirects":
453 srv.AutoHTTPS.DisableRedir = true
454 - }
455 - if autoHTTPS == "ignore_loaded_certs" {
454 + case "disable_certs":
455 + srv.AutoHTTPS.DisableCerts = true
456 + case "ignore_loaded_certs":
457 srv.AutoHTTPS.IgnoreLoadedCerts = true
458 }
459 }
caddyconfig/httpcaddyfile/options.go
+2 -2
@@ -384,8 +384,8 @@ func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ interface{}) (interface{}, erro
384 if d.Next() {
385 return "", d.ArgErr()
386 }
387 - if val != "off" && val != "disable_redirects" && val != "ignore_loaded_certs" {
388 - return "", d.Errf("auto_https must be one of 'off', 'disable_redirects' or 'ignore_loaded_certs'")
387 + if val != "off" && val != "disable_redirects" && val != "disable_certs" && val != "ignore_loaded_certs" {
388 + return "", d.Errf("auto_https must be one of 'off', 'disable_redirects', 'disable_certs', or 'ignore_loaded_certs'")
389 }
390 return val, nil
391 }
caddyconfig/httpcaddyfile/tlsapp.go
+7
@@ -133,6 +133,13 @@ func (st ServerType) buildTLSApp(
133 ap.Issuers = issuers
134 }
135
136 + // certificate managers
137 + if certManagerVals, ok := sblock.pile["tls.cert_manager"]; ok {
138 + for _, certManager := range certManagerVals {
139 + certGetterName := certManager.Value.(caddy.Module).CaddyModule().ID.Name()
140 + ap.ManagersRaw = append(ap.ManagersRaw, caddyconfig.JSONModuleObject(certManager.Value, "via", certGetterName, &warnings))
141 + }
142 + }
143 // custom bind host
144 for _, cfgVal := range sblock.pile["bind"] {
145 for _, iss := range ap.Issuers {
go.mod
+8 -2
@@ -3,11 +3,14 @@ module github.com/caddyserver/caddy/v2
3 go 1.16
4
5 require (
6 + cloud.google.com/go/kms v1.1.0 // indirect
7 + github.com/BurntSushi/toml v0.4.1 // indirect
8 github.com/Masterminds/sprig/v3 v3.2.2
9 github.com/alecthomas/chroma v0.10.0
10 github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b
9 - github.com/caddyserver/certmagic v0.15.3
11 + github.com/caddyserver/certmagic v0.15.4-0.20220217213750-797d29bcf32f
12 github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac
13 + github.com/fsnotify/fsnotify v1.5.1 // indirect
14 github.com/go-chi/chi v4.1.2+incompatible
15 github.com/google/cel-go v0.9.0
16 github.com/google/uuid v1.3.0
@@ -22,14 +25,17 @@ require (
25 github.com/smallstep/cli v0.18.0
26 github.com/smallstep/nosql v0.3.9
27 github.com/smallstep/truststore v0.10.1
28 + github.com/tailscale/tscert v0.0.0-20220125204807-4509a5fbaf74
29 github.com/yuin/goldmark v1.4.4
30 github.com/yuin/goldmark-highlighting v0.0.0-20210516132338-9216f9c5aa01
31 go.uber.org/zap v1.20.0
32 golang.org/x/crypto v0.0.0-20210915214749-c084706c2272
33 golang.org/x/net v0.0.0-20210913180222-943fd674d43e
34 golang.org/x/term v0.0.0-20210503060354-a79de5458b56
31 - google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2
35 + google.golang.org/genproto v0.0.0-20211018162055-cf77aa76bad2
36 google.golang.org/protobuf v1.27.1
37 + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
38 gopkg.in/natefinch/lumberjack.v2 v2.0.0
39 gopkg.in/yaml.v2 v2.4.0
40 + howett.net/plist v1.0.0 // indirect
41 )
go.sum
+64 -11
@@ -25,8 +25,14 @@ cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPT
25 cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
26 cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
27 cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
28 -cloud.google.com/go v0.83.0 h1:bAMqZidYkmIsUqe6PtkEPT7Q+vfizScn+jfNA6jwK9c=
28 cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
29 +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
30 +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
31 +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
32 +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
33 +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
34 +cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8=
35 +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
36 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
37 cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
38 cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@@ -36,6 +42,8 @@ cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM7
42 cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
43 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
44 cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
45 +cloud.google.com/go/kms v1.1.0 h1:1yc4rLqCkVDS9Zvc7m+3mJ47kw0Uo5Q5+sMjcmUVUeM=
46 +cloud.google.com/go/kms v1.1.0/go.mod h1:WdbppnCDMDpOvoYBMn1+gNmOeEoZYqAv+HeuKARGCXI=
47 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
48 cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
49 cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -84,8 +92,9 @@ github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcP
92 github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
93 github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
94 github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
87 -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
95 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
96 +github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
97 +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
98 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
99 github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo=
100 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
@@ -180,8 +189,8 @@ github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl
189 github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
190 github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
191 github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw=
183 -github.com/caddyserver/certmagic v0.15.3 h1:ScY3KVV1eMIUfW74i20kDnD4eWL8T0rG6S6Wnc6nc9U=
184 -github.com/caddyserver/certmagic v0.15.3/go.mod h1:qhkAOthf72ufAcp3Y5jF2RaGE96oip3UbEQRIzwe3/8=
192 +github.com/caddyserver/certmagic v0.15.4-0.20220217213750-797d29bcf32f h1:sdjilRh0dxXpofiwDSFU7itmxuXetKB6xWd+lNRVq9s=
193 +github.com/caddyserver/certmagic v0.15.4-0.20220217213750-797d29bcf32f/go.mod h1:qhkAOthf72ufAcp3Y5jF2RaGE96oip3UbEQRIzwe3/8=
194 github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo=
195 github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
196 github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
@@ -294,8 +303,9 @@ github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiD
303 github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
304 github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
305 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
297 -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
306 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
307 +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
308 +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
309 github.com/fullstorydev/grpcurl v1.8.0/go.mod h1:Mn2jWbdMrQGJQ8UD62uNyMumT2acsZUCkZIqFxsQf1o=
310 github.com/fullstorydev/grpcurl v1.8.1/go.mod h1:3BWhvHZwNO7iLXaQlojdg5NA6SxUDePli4ecpK1N7gw=
311 github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
@@ -431,6 +441,8 @@ github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLe
441 github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
442 github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
443 github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
444 +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
445 +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
446 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
447 github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg=
448 github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
@@ -449,8 +461,10 @@ github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp
461 github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
462 github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
463 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
452 -github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
464 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
465 +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
466 +github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
467 +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
468 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
469 github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
470 github.com/goreleaser/goreleaser v0.134.0/go.mod h1:ZT6Y2rSYa6NxQzIsdfWWNWAlYGXGbreo66NmE+3X3WQ=
@@ -643,6 +657,8 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1
657 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
658 github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
659 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
660 +github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
661 +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
662 github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
663 github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
664 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
@@ -897,6 +913,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
913 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
914 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
915 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
916 +github.com/tailscale/tscert v0.0.0-20220125204807-4509a5fbaf74 h1:uFx5aih29p2IaRUF0lJwtVViCXStlvnPPE3NEmM4Ivs=
917 +github.com/tailscale/tscert v0.0.0-20220125204807-4509a5fbaf74/go.mod h1:hL4gB6APAasMR2NNi/JHzqKkxW3EPQlFgLEq9PMi2t0=
918 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
919 github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
920 github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
@@ -1169,8 +1187,11 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ
1187 golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
1188 golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
1189 golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
1172 -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI=
1190 golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
1191 +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
1192 +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
1193 +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=
1194 +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
1195 golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
1196 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
1197 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1266,8 +1287,14 @@ golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBc
1287 golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1288 golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1289 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1290 +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1291 +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1292 +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1293 +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1294 golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1295 +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1296 golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1297 +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1298 golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1299 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
1300 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1361,6 +1388,7 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
1388 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
1389 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
1390 golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
1391 +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
1392 golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
1393 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
1394 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1400,8 +1428,15 @@ google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk
1428 google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA=
1429 google.golang.org/api v0.46.0/go.mod h1:ceL4oozhkAiTID8XMmJBsIxID/9wMXJVVFXPg4ylg3I=
1430 google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
1403 -google.golang.org/api v0.48.0 h1:RDAPWfNFY06dffEXfn7hZF5Fr1ZbnChzfQZAPyBd1+I=
1431 google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
1432 +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
1433 +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
1434 +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
1435 +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
1436 +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
1437 +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
1438 +google.golang.org/api v0.58.0 h1:MDkAbYIB1JpSgCTOCYYoIec/coMlKK4oVbpnBLLcyT0=
1439 +google.golang.org/api v0.58.0/go.mod h1:cAbP2FsxoGVNwtgNAmmn3y5G1TWAiVYRmg4yku3lv+E=
1440 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
1441 google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
1442 google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1473,9 +1508,23 @@ google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQ
1508 google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
1509 google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
1510 google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
1511 +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
1512 +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
1513 +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
1514 +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
1515 google.golang.org/genproto v0.0.0-20210719143636-1d5a45f8e492/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
1477 -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2 h1:NHN4wOCScVzKhPenJ2dt+BTs3X/XkBVI/Rh4iDt55T8=
1516 +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
1517 +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
1518 +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
1519 +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
1520 +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
1521 google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
1522 +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
1523 +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
1524 +google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
1525 +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
1526 +google.golang.org/genproto v0.0.0-20211018162055-cf77aa76bad2 h1:CUp93KYgL06Y/PdI8aRJaFiAHevPIGWQmijSqaUhue8=
1527 +google.golang.org/genproto v0.0.0-20211018162055-cf77aa76bad2/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
1528 google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
1529 google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
1530 google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
@@ -1508,6 +1557,7 @@ google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ
1557 google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
1558 google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
1559 google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
1560 +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
1561 google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
1562 google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
1563 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
@@ -1529,8 +1579,9 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
1579 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
1580 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1581 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1532 -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
1582 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1583 +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
1584 +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
1585 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
1586 gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
1587 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
@@ -1550,6 +1601,7 @@ gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQb
1601 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
1602 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
1603 gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
1604 +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
1605 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
1606 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
1607 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -1573,8 +1625,9 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
1625 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
1626 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
1627 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
1576 -howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M=
1628 howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
1629 +howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
1630 +howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
1631 pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=
1632 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
1633 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
modules/caddyhttp/autohttps.go
+67 -32
@@ -31,13 +31,20 @@ import (
31 // HTTPS is enabled automatically and by default when
32 // qualifying hostnames are available from the config.
33 type AutoHTTPSConfig struct {
34 - // If true, automatic HTTPS will be entirely disabled.
34 + // If true, automatic HTTPS will be entirely disabled,
35 + // including certificate management and redirects.
36 Disabled bool `json:"disable,omitempty"`
37
38 // If true, only automatic HTTP->HTTPS redirects will
38 - // be disabled.
39 + // be disabled, but other auto-HTTPS features will
40 + // remain enabled.
41 DisableRedir bool `json:"disable_redirects,omitempty"`
42
43 + // If true, automatic certificate management will be
44 + // disabled, but other auto-HTTPS features will
45 + // remain enabled.
46 + DisableCerts bool `json:"disable_certificates,omitempty"`
47 +
48 // Hosts/domain names listed here will not be included
49 // in automatic HTTPS (they will not have certificates
50 // loaded nor redirects applied).
@@ -104,12 +111,13 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
111 srv.AutoHTTPS = new(AutoHTTPSConfig)
112 }
113 if srv.AutoHTTPS.Disabled {
114 + app.logger.Warn("automatic HTTPS is completely disabled for server", zap.String("server_name", srvName))
115 continue
116 }
117
118 // skip if all listeners use the HTTP port
119 if !srv.listenersUseAnyPortOtherThan(app.httpPort()) {
112 - app.logger.Info("server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server",
120 + app.logger.Warn("server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server",
121 zap.String("server_name", srvName),
122 zap.Int("http_port", app.httpPort()),
123 )
@@ -166,30 +174,35 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
174
175 // for all the hostnames we found, filter them so we have
176 // a deduplicated list of names for which to obtain certs
169 - for d := range serverDomainSet {
170 - if certmagic.SubjectQualifiesForCert(d) &&
171 - !srv.AutoHTTPS.Skipped(d, srv.AutoHTTPS.SkipCerts) {
172 - // if a certificate for this name is already loaded,
173 - // don't obtain another one for it, unless we are
174 - // supposed to ignore loaded certificates
175 - if !srv.AutoHTTPS.IgnoreLoadedCerts &&
176 - len(app.tlsApp.AllMatchingCertificates(d)) > 0 {
177 - app.logger.Info("skipping automatic certificate management because one or more matching certificates are already loaded",
178 - zap.String("domain", d),
179 - zap.String("server_name", srvName),
180 - )
181 - continue
182 - }
177 + // (only if cert management not disabled for this server)
178 + if srv.AutoHTTPS.DisableCerts {
179 + app.logger.Warn("skipping automated certificate management for server because it is disabled", zap.String("server_name", srvName))
180 + } else {
181 + for d := range serverDomainSet {
182 + if certmagic.SubjectQualifiesForCert(d) &&
183 + !srv.AutoHTTPS.Skipped(d, srv.AutoHTTPS.SkipCerts) {
184 + // if a certificate for this name is already loaded,
185 + // don't obtain another one for it, unless we are
186 + // supposed to ignore loaded certificates
187 + if !srv.AutoHTTPS.IgnoreLoadedCerts &&
188 + len(app.tlsApp.AllMatchingCertificates(d)) > 0 {
189 + app.logger.Info("skipping automatic certificate management because one or more matching certificates are already loaded",
190 + zap.String("domain", d),
191 + zap.String("server_name", srvName),
192 + )
193 + continue
194 + }
195
184 - // most clients don't accept wildcards like *.tld... we
185 - // can handle that, but as a courtesy, warn the user
186 - if strings.Contains(d, "*") &&
187 - strings.Count(strings.Trim(d, "."), ".") == 1 {
188 - app.logger.Warn("most clients do not trust second-level wildcard certificates (*.tld)",
189 - zap.String("domain", d))
190 - }
196 + // most clients don't accept wildcards like *.tld... we
197 + // can handle that, but as a courtesy, warn the user
198 + if strings.Contains(d, "*") &&
199 + strings.Count(strings.Trim(d, "."), ".") == 1 {
200 + app.logger.Warn("most clients do not trust second-level wildcard certificates (*.tld)",
201 + zap.String("domain", d))
202 + }
203
192 - uniqueDomainsForCerts[d] = struct{}{}
204 + uniqueDomainsForCerts[d] = struct{}{}
205 + }
206 }
207 }
208
@@ -200,12 +213,11 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
213
214 // nothing left to do if auto redirects are disabled
215 if srv.AutoHTTPS.DisableRedir {
216 + app.logger.Warn("automatic HTTP->HTTPS redirects are disabled", zap.String("server_name", srvName))
217 continue
218 }
219
206 - app.logger.Info("enabling automatic HTTP->HTTPS redirects",
207 - zap.String("server_name", srvName),
208 - )
220 + app.logger.Info("enabling automatic HTTP->HTTPS redirects", zap.String("server_name", srvName))
221
222 // create HTTP->HTTPS redirects
223 for _, addr := range srv.Listen {
@@ -459,6 +471,16 @@ func (app *App) createAutomationPolicies(ctx caddy.Context, internalNames []stri
471 }
472 }
473
474 + // if no external managers were configured, enable
475 + // implicit Tailscale support for convenience
476 + if ap.Managers == nil {
477 + ts, err := implicitTailscale(ctx)
478 + if err != nil {
479 + return err
480 + }
481 + ap.Managers = []certmagic.CertificateManager{ts}
482 + }
483 +
484 // while we're here, is this the catch-all/base policy?
485 if !foundBasePolicy && len(ap.Subjects) == 0 {
486 basePolicy = ap
@@ -467,8 +489,14 @@ func (app *App) createAutomationPolicies(ctx caddy.Context, internalNames []stri
489 }
490
491 if basePolicy == nil {
470 - // no base policy found, we will make one!
471 - basePolicy = new(caddytls.AutomationPolicy)
492 + // no base policy found, we will make one! (with implicit Tailscale integration)
493 + ts, err := implicitTailscale(ctx)
494 + if err != nil {
495 + return err
496 + }
497 + basePolicy = &caddytls.AutomationPolicy{
498 + Managers: []certmagic.CertificateManager{ts},
499 + }
500 }
501
502 // if the basePolicy has an existing ACMEIssuer (particularly to
@@ -482,8 +510,8 @@ func (app *App) createAutomationPolicies(ctx caddy.Context, internalNames []stri
510 }
511 }
512 if baseACMEIssuer == nil {
485 - // note that this happens if basePolicy.Issuer is nil
486 - // OR if it is not nil but is not an ACMEIssuer
513 + // note that this happens if basePolicy.Issuers is empty
514 + // OR if it is not empty but does not have not an ACMEIssuer
515 baseACMEIssuer = new(caddytls.ACMEIssuer)
516 }
517
@@ -653,4 +681,11 @@ func (app *App) automaticHTTPSPhase2() error {
681 return nil
682 }
683
684 +// implicitTailscale returns a new and provisioned Tailscale module configured to be optional.
685 +func implicitTailscale(ctx caddy.Context) (caddytls.Tailscale, error) {
686 + ts := caddytls.Tailscale{Optional: true}
687 + err := ts.Provision(ctx)
688 + return ts, err
689 +}
690 +
691 type acmeCapable interface{ GetACMEIssuer() *caddytls.ACMEIssuer }
modules/caddypki/ca.go
+6 -6
@@ -239,7 +239,7 @@ func (ca CA) loadOrGenRoot() (rootCert *x509.Certificate, rootKey interface{}, e
239 if err != nil {
240 return nil, nil, fmt.Errorf("loading root key: %v", err)
241 }
242 - rootKey, err = pemDecodePrivateKey(rootKeyPEM)
242 + rootKey, err = certmagic.PEMDecodePrivateKey(rootKeyPEM)
243 if err != nil {
244 return nil, nil, fmt.Errorf("decoding root key: %v", err)
245 }
@@ -263,7 +263,7 @@ func (ca CA) genRoot() (rootCert *x509.Certificate, rootKey interface{}, err err
263 if err != nil {
264 return nil, nil, fmt.Errorf("saving root certificate: %v", err)
265 }
266 - rootKeyPEM, err := pemEncodePrivateKey(rootKey)
266 + rootKeyPEM, err := certmagic.PEMEncodePrivateKey(rootKey)
267 if err != nil {
268 return nil, nil, fmt.Errorf("encoding root key: %v", err)
269 }
@@ -275,7 +275,7 @@ func (ca CA) genRoot() (rootCert *x509.Certificate, rootKey interface{}, err err
275 return rootCert, rootKey, nil
276 }
277
278 -func (ca CA) loadOrGenIntermediate(rootCert *x509.Certificate, rootKey interface{}) (interCert *x509.Certificate, interKey interface{}, err error) {
278 +func (ca CA) loadOrGenIntermediate(rootCert *x509.Certificate, rootKey crypto.PrivateKey) (interCert *x509.Certificate, interKey crypto.PrivateKey, err error) {
279 interCertPEM, err := ca.storage.Load(ca.storageKeyIntermediateCert())
280 if err != nil {
281 if _, ok := err.(certmagic.ErrNotExist); !ok {
@@ -301,7 +301,7 @@ func (ca CA) loadOrGenIntermediate(rootCert *x509.Certificate, rootKey interface
301 if err != nil {
302 return nil, nil, fmt.Errorf("loading intermediate key: %v", err)
303 }
304 - interKey, err = pemDecodePrivateKey(interKeyPEM)
304 + interKey, err = certmagic.PEMDecodePrivateKey(interKeyPEM)
305 if err != nil {
306 return nil, nil, fmt.Errorf("decoding intermediate key: %v", err)
307 }
@@ -310,7 +310,7 @@ func (ca CA) loadOrGenIntermediate(rootCert *x509.Certificate, rootKey interface
310 return interCert, interKey, nil
311 }
312
313 -func (ca CA) genIntermediate(rootCert *x509.Certificate, rootKey interface{}) (interCert *x509.Certificate, interKey interface{}, err error) {
313 +func (ca CA) genIntermediate(rootCert *x509.Certificate, rootKey crypto.PrivateKey) (interCert *x509.Certificate, interKey crypto.PrivateKey, err error) {
314 repl := ca.newReplacer()
315
316 interCert, interKey, err = generateIntermediate(repl.ReplaceAll(ca.IntermediateCommonName, ""), rootCert, rootKey)
@@ -325,7 +325,7 @@ func (ca CA) genIntermediate(rootCert *x509.Certificate, rootKey interface{}) (i
325 if err != nil {
326 return nil, nil, fmt.Errorf("saving intermediate certificate: %v", err)
327 }
328 - interKeyPEM, err := pemEncodePrivateKey(interKey)
328 + interKeyPEM, err := certmagic.PEMEncodePrivateKey(interKey)
329 if err != nil {
330 return nil, nil, fmt.Errorf("encoding intermediate key: %v", err)
331 }
modules/caddypki/certificates.go
+3 -2
@@ -15,6 +15,7 @@
15 package caddypki
16
17 import (
18 + "crypto"
19 "crypto/x509"
20 "time"
21
@@ -30,7 +31,7 @@ func generateRoot(commonName string) (rootCrt *x509.Certificate, privateKey inte
31 return newCert(rootProfile)
32 }
33
33 -func generateIntermediate(commonName string, rootCrt *x509.Certificate, rootKey interface{}) (cert *x509.Certificate, privateKey interface{}, err error) {
34 +func generateIntermediate(commonName string, rootCrt *x509.Certificate, rootKey crypto.PrivateKey) (cert *x509.Certificate, privateKey crypto.PrivateKey, err error) {
35 interProfile, err := x509util.NewIntermediateProfile(commonName, rootCrt, rootKey)
36 if err != nil {
37 return
@@ -39,7 +40,7 @@ func generateIntermediate(commonName string, rootCrt *x509.Certificate, rootKey
40 return newCert(interProfile)
41 }
42
42 -func newCert(profile x509util.Profile) (cert *x509.Certificate, privateKey interface{}, err error) {
43 +func newCert(profile x509util.Profile) (cert *x509.Certificate, privateKey crypto.PrivateKey, err error) {
44 certBytes, err := profile.CreateCertificate()
45 if err != nil {
46 return
modules/caddypki/crypto.go
+4 -70
@@ -17,14 +17,12 @@ package caddypki
17 import (
18 "bytes"
19 "crypto"
20 - "crypto/ecdsa"
21 - "crypto/ed25519"
22 - "crypto/rsa"
20 "crypto/x509"
21 "encoding/pem"
22 "fmt"
23 "os"
27 - "strings"
24 +
25 + "github.com/caddyserver/certmagic"
26 )
27
28 func pemDecodeSingleCert(pemDER []byte) (*x509.Certificate, error) {
@@ -45,70 +43,6 @@ func pemEncodeCert(der []byte) ([]byte, error) {
43 return pemEncode("CERTIFICATE", der)
44 }
45
48 -// pemEncodePrivateKey marshals a EC or RSA private key into a PEM-encoded array of bytes.
49 -// TODO: this is the same thing as in certmagic. Should we reuse that code somehow? It's unexported.
50 -func pemEncodePrivateKey(key crypto.PrivateKey) ([]byte, error) {
51 - var pemType string
52 - var keyBytes []byte
53 - switch key := key.(type) {
54 - case *ecdsa.PrivateKey:
55 - var err error
56 - pemType = "EC"
57 - keyBytes, err = x509.MarshalECPrivateKey(key)
58 - if err != nil {
59 - return nil, err
60 - }
61 - case *rsa.PrivateKey:
62 - pemType = "RSA"
63 - keyBytes = x509.MarshalPKCS1PrivateKey(key)
64 - case *ed25519.PrivateKey:
65 - var err error
66 - pemType = "ED25519"
67 - keyBytes, err = x509.MarshalPKCS8PrivateKey(key)
68 - if err != nil {
69 - return nil, err
70 - }
71 - default:
72 - return nil, fmt.Errorf("unsupported key type: %T", key)
73 - }
74 - return pemEncode(pemType+" PRIVATE KEY", keyBytes)
75 -}
76 -
77 -// pemDecodePrivateKey loads a PEM-encoded ECC/RSA private key from an array of bytes.
78 -// Borrowed from Go standard library, to handle various private key and PEM block types.
79 -// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L291-L308
80 -// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L238)
81 -// TODO: this is the same thing as in certmagic. Should we reuse that code somehow? It's unexported.
82 -func pemDecodePrivateKey(keyPEMBytes []byte) (crypto.PrivateKey, error) {
83 - keyBlockDER, _ := pem.Decode(keyPEMBytes)
84 - if keyBlockDER == nil {
85 - return nil, fmt.Errorf("no PEM data found")
86 - }
87 -
88 - if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") {
89 - return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type)
90 - }
91 -
92 - if key, err := x509.ParsePKCS1PrivateKey(keyBlockDER.Bytes); err == nil {
93 - return key, nil
94 - }
95 -
96 - if key, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil {
97 - switch key := key.(type) {
98 - case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
99 - return key, nil
100 - default:
101 - return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key)
102 - }
103 - }
104 -
105 - if key, err := x509.ParseECPrivateKey(keyBlockDER.Bytes); err == nil {
106 - return key, nil
107 - }
108 -
109 - return nil, fmt.Errorf("unknown private key type")
110 -}
111 -
46 func pemEncode(blockType string, b []byte) ([]byte, error) {
47 var buf bytes.Buffer
48 err := pem.Encode(&buf, &pem.Block{Type: blockType, Bytes: b})
@@ -137,7 +71,7 @@ type KeyPair struct {
71 }
72
73 // Load loads the certificate and key.
140 -func (kp KeyPair) Load() (*x509.Certificate, interface{}, error) {
74 +func (kp KeyPair) Load() (*x509.Certificate, crypto.Signer, error) {
75 switch kp.Format {
76 case "", "pem_file":
77 certData, err := os.ReadFile(kp.Certificate)
@@ -153,7 +87,7 @@ func (kp KeyPair) Load() (*x509.Certificate, interface{}, error) {
87 if err != nil {
88 return nil, nil, err
89 }
156 - key, err := pemDecodePrivateKey(keyData)
90 + key, err := certmagic.PEMDecodePrivateKey(keyData)
91 if err != nil {
92 return nil, nil, err
93 }
modules/caddytls/automation.go
+36 -8
@@ -89,6 +89,14 @@ type AutomationPolicy struct {
89 // zerossl.
90 IssuersRaw []json.RawMessage `json:"issuers,omitempty" caddy:"namespace=tls.issuance inline_key=module"`
91
92 + // Modules that can get a custom certificate to use for any
93 + // given TLS handshake at handshake-time. Custom certificates
94 + // can be useful if another entity is managing certificates
95 + // and Caddy need only get it and serve it.
96 + //
97 + // TODO: This is an EXPERIMENTAL feature. It is subject to change or removal.
98 + ManagersRaw []json.RawMessage `json:"get_certificate,omitempty" caddy:"namespace=tls.get_certificate inline_key=via"`
99 +
100 // If true, certificates will be requested with MustStaple. Not all
101 // CAs support this, and there are potentially serious consequences
102 // of enabling this feature without proper threat modeling.
@@ -113,7 +121,7 @@ type AutomationPolicy struct {
121
122 // If true, certificates will be managed "on demand"; that is, during
123 // TLS handshakes or when needed, as opposed to at startup or config
116 - // load.
124 + // load. This enables On-Demand TLS for this policy.
125 OnDemand bool `json:"on_demand,omitempty"`
126
127 // Disables OCSP stapling. Disabling OCSP stapling puts clients at
@@ -129,10 +137,12 @@ type AutomationPolicy struct {
137 // EXPERIMENTAL. Subject to change.
138 OCSPOverrides map[string]string `json:"ocsp_overrides,omitempty"`
139
132 - // Issuers stores the decoded issuer parameters. This is only
133 - // used to populate an underlying certmagic.Config's Issuers
134 - // field; it is not referenced thereafter.
135 - Issuers []certmagic.Issuer `json:"-"`
140 + // Issuers and Managers store the decoded issuer and manager modules;
141 + // they are only used to populate an underlying certmagic.Config's
142 + // fields during provisioning so that the modules can survive a
143 + // re-provisioning.
144 + Issuers []certmagic.Issuer `json:"-"`
145 + Managers []certmagic.CertificateManager `json:"-"`
146
147 magic *certmagic.Config
148 storage certmagic.Storage
@@ -153,6 +163,7 @@ func (ap *AutomationPolicy) Provision(tlsApp *TLS) error {
163 ap.storage = cmStorage
164 }
165
166 + // on-demand TLS
167 var ond *certmagic.OnDemandConfig
168 if ap.OnDemand {
169 ond = &certmagic.OnDemandConfig{
@@ -176,6 +187,22 @@ func (ap *AutomationPolicy) Provision(tlsApp *TLS) error {
187 }
188 }
189
190 + // we don't store loaded modules directly in the certmagic config since
191 + // policy provisioning may happen more than once (during auto-HTTPS) and
192 + // loading a module clears its config bytes; thus, load the module and
193 + // store them on the policy before putting it on the config
194 +
195 + // load and provision any cert manager modules
196 + if ap.ManagersRaw != nil {
197 + vals, err := tlsApp.ctx.LoadModule(ap, "ManagersRaw")
198 + if err != nil {
199 + return fmt.Errorf("loading external certificate manager modules: %v", err)
200 + }
201 + for _, getCertVal := range vals.([]interface{}) {
202 + ap.Managers = append(ap.Managers, getCertVal.(certmagic.CertificateManager))
203 + }
204 + }
205 +
206 // load and provision any explicitly-configured issuer modules
207 if ap.IssuersRaw != nil {
208 val, err := tlsApp.ctx.LoadModule(ap, "IssuersRaw")
@@ -225,9 +252,10 @@ func (ap *AutomationPolicy) Provision(tlsApp *TLS) error {
252 DisableStapling: ap.DisableOCSPStapling,
253 ResponderOverrides: ap.OCSPOverrides,
254 },
228 - Storage: storage,
229 - Issuers: issuers,
230 - Logger: tlsApp.logger,
255 + Storage: storage,
256 + Issuers: issuers,
257 + Managers: ap.Managers,
258 + Logger: tlsApp.logger,
259 }
260 ap.magic = certmagic.New(tlsApp.certCache, template)
261
modules/caddytls/certmanagers.go new
+208
@@ -0,0 +1,208 @@
1 +package caddytls
2 +
3 +import (
4 + "context"
5 + "crypto/tls"
6 + "fmt"
7 + "io"
8 + "net/http"
9 + "net/url"
10 + "strings"
11 +
12 + "github.com/caddyserver/caddy/v2"
13 + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
14 + "github.com/caddyserver/certmagic"
15 + "github.com/tailscale/tscert"
16 + "go.uber.org/zap"
17 +)
18 +
19 +func init() {
20 + caddy.RegisterModule(Tailscale{})
21 + caddy.RegisterModule(HTTPCertGetter{})
22 +}
23 +
24 +// Tailscale is a module that can get certificates from the local Tailscale process.
25 +type Tailscale struct {
26 + // If true, this module will operate in "best-effort" mode and
27 + // ignore "soft" errors; i.e. try Tailscale, and if it doesn't connect
28 + // or return a certificate, oh well. Failure to connect to Tailscale
29 + // results in a no-op instead of an error. Intended for the use case
30 + // where this module is added implicitly for convenience, even if
31 + // Tailscale isn't necessarily running.
32 + Optional bool `json:"optional,omitempty"`
33 +
34 + logger *zap.Logger
35 +}
36 +
37 +// CaddyModule returns the Caddy module information.
38 +func (Tailscale) CaddyModule() caddy.ModuleInfo {
39 + return caddy.ModuleInfo{
40 + ID: "tls.get_certificate.tailscale",
41 + New: func() caddy.Module { return new(Tailscale) },
42 + }
43 +}
44 +
45 +func (ts *Tailscale) Provision(ctx caddy.Context) error {
46 + ts.logger = ctx.Logger(ts)
47 + return nil
48 +}
49 +
50 +func (ts Tailscale) GetCertificate(ctx context.Context, hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
51 + canGetCert, err := ts.canHazCertificate(ctx, hello)
52 + if err == nil && !canGetCert {
53 + return nil, nil // pass-thru: Tailscale can't offer a cert for this name
54 + }
55 + if err != nil {
56 + ts.logger.Warn("could not get status; will try to get certificate anyway", zap.Error(err))
57 + }
58 + return tscert.GetCertificate(hello)
59 +}
60 +
61 +// canHazCertificate returns true if Tailscale reports it can get a certificate for the given ClientHello.
62 +func (ts Tailscale) canHazCertificate(ctx context.Context, hello *tls.ClientHelloInfo) (bool, error) {
63 + if ts.Optional && !strings.HasSuffix(strings.ToLower(hello.ServerName), tailscaleDomainAliasEnding) {
64 + return false, nil
65 + }
66 + status, err := tscert.GetStatus(ctx)
67 + if err != nil {
68 + if ts.Optional {
69 + return false, nil // ignore error if we don't expect/require it to work anyway
70 + }
71 + return false, err
72 + }
73 + for _, domain := range status.CertDomains {
74 + if certmagic.MatchWildcard(hello.ServerName, domain) {
75 + return true, nil
76 + }
77 + }
78 + return false, nil
79 +}
80 +
81 +// UnmarshalCaddyfile deserializes Caddyfile tokens into ts.
82 +//
83 +// ... tailscale
84 +//
85 +func (Tailscale) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
86 + for d.Next() {
87 + if d.NextArg() {
88 + return d.ArgErr()
89 + }
90 + }
91 + return nil
92 +}
93 +
94 +// tailscaleDomainAliasEnding is the ending for all Tailscale custom domains.
95 +const tailscaleDomainAliasEnding = ".ts.net"
96 +
97 +// HTTPCertGetter can get a certificate via HTTP(S) request.
98 +type HTTPCertGetter struct {
99 + // The URL from which to download the certificate. Required.
100 + //
101 + // The URL will be augmented with query string parameters taken
102 + // from the TLS handshake:
103 + //
104 + // - server_name: The SNI value
105 + // - signature_schemes: Comma-separated list of hex IDs of signatures
106 + // - cipher_suites: Comma-separated list of hex IDs of cipher suites
107 + //
108 + // To be valid, the response must be HTTP 200 with a PEM body
109 + // consisting of blocks for the certificate chain and the private
110 + // key.
111 + URL string `json:"url,omitempty"`
112 +
113 + ctx context.Context
114 +}
115 +
116 +// CaddyModule returns the Caddy module information.
117 +func (hcg HTTPCertGetter) CaddyModule() caddy.ModuleInfo {
118 + return caddy.ModuleInfo{
119 + ID: "tls.get_certificate.http",
120 + New: func() caddy.Module { return new(HTTPCertGetter) },
121 + }
122 +}
123 +
124 +func (hcg *HTTPCertGetter) Provision(ctx caddy.Context) error {
125 + hcg.ctx = ctx
126 + if hcg.URL == "" {
127 + return fmt.Errorf("URL is required")
128 + }
129 + return nil
130 +}
131 +
132 +func (hcg HTTPCertGetter) GetCertificate(ctx context.Context, hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
133 + sigs := make([]string, len(hello.SignatureSchemes))
134 + for i, sig := range hello.SignatureSchemes {
135 + sigs[i] = fmt.Sprintf("%x", uint16(sig)) // you won't believe what %x uses if the val is a Stringer
136 + }
137 + suites := make([]string, len(hello.CipherSuites))
138 + for i, cs := range hello.CipherSuites {
139 + suites[i] = fmt.Sprintf("%x", cs)
140 + }
141 +
142 + parsed, err := url.Parse(hcg.URL)
143 + if err != nil {
144 + return nil, err
145 + }
146 + qs := parsed.Query()
147 + qs.Set("server_name", hello.ServerName)
148 + qs.Set("signature_schemes", strings.Join(sigs, ","))
149 + qs.Set("cipher_suites", strings.Join(suites, ","))
150 + parsed.RawQuery = qs.Encode()
151 +
152 + req, err := http.NewRequestWithContext(hcg.ctx, http.MethodGet, parsed.String(), nil)
153 + if err != nil {
154 + return nil, err
155 + }
156 +
157 + resp, err := http.DefaultClient.Do(req)
158 + if err != nil {
159 + return nil, err
160 + }
161 + defer resp.Body.Close()
162 + if resp.StatusCode != http.StatusOK {
163 + return nil, fmt.Errorf("got HTTP %d", resp.StatusCode)
164 + }
165 +
166 + bodyBytes, err := io.ReadAll(resp.Body)
167 + if err != nil {
168 + return nil, fmt.Errorf("error reading response body: %v", err)
169 + }
170 +
171 + cert, err := tlsCertFromCertAndKeyPEMBundle(bodyBytes)
172 + if err != nil {
173 + return nil, err
174 + }
175 +
176 + return &cert, nil
177 +}
178 +
179 +// UnmarshalCaddyfile deserializes Caddyfile tokens into ts.
180 +//
181 +// ... http <url>
182 +//
183 +func (hcg *HTTPCertGetter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
184 + for d.Next() {
185 + if !d.NextArg() {
186 + return d.ArgErr()
187 + }
188 + hcg.URL = d.Val()
189 + if d.NextArg() {
190 + return d.ArgErr()
191 + }
192 + for nesting := d.Nesting(); d.NextBlock(nesting); {
193 + return d.Err("block not allowed here")
194 + }
195 + }
196 + return nil
197 +}
198 +
199 +// Interface guards
200 +var (
201 + _ certmagic.CertificateManager = (*Tailscale)(nil)
202 + _ caddy.Provisioner = (*Tailscale)(nil)
203 + _ caddyfile.Unmarshaler = (*Tailscale)(nil)
204 +
205 + _ certmagic.CertificateManager = (*HTTPCertGetter)(nil)
206 + _ caddy.Provisioner = (*HTTPCertGetter)(nil)
207 + _ caddyfile.Unmarshaler = (*HTTPCertGetter)(nil)
208 +)
modules/caddytls/folderloader.go
+15 -20
@@ -61,10 +61,14 @@ func (fl FolderLoader) LoadCertificates() ([]Certificate, error) {
61 return nil
62 }
63
64 - cert, err := x509CertFromCertAndKeyPEMFile(fpath)
64 + bundle, err := os.ReadFile(fpath)
65 if err != nil {
66 return err
67 }
68 + cert, err := tlsCertFromCertAndKeyPEMBundle(bundle)
69 + if err != nil {
70 + return fmt.Errorf("%s: %w", fpath, err)
71 + }
72
73 certs = append(certs, Certificate{Certificate: cert})
74
@@ -77,12 +81,7 @@ func (fl FolderLoader) LoadCertificates() ([]Certificate, error) {
81 return certs, nil
82 }
83
80 -func x509CertFromCertAndKeyPEMFile(fpath string) (tls.Certificate, error) {
81 - bundle, err := os.ReadFile(fpath)
82 - if err != nil {
83 - return tls.Certificate{}, err
84 - }
85 -
84 +func tlsCertFromCertAndKeyPEMBundle(bundle []byte) (tls.Certificate, error) {
85 certBuilder, keyBuilder := new(bytes.Buffer), new(bytes.Buffer)
86 var foundKey bool // use only the first key in the file
87
@@ -96,8 +95,7 @@ func x509CertFromCertAndKeyPEMFile(fpath string) (tls.Certificate, error) {
95
96 if derBlock.Type == "CERTIFICATE" {
97 // Re-encode certificate as PEM, appending to certificate chain
99 - err = pem.Encode(certBuilder, derBlock)
100 - if err != nil {
98 + if err := pem.Encode(certBuilder, derBlock); err != nil {
99 return tls.Certificate{}, err
100 }
101 } else if derBlock.Type == "EC PARAMETERS" {
@@ -105,18 +103,16 @@ func x509CertFromCertAndKeyPEMFile(fpath string) (tls.Certificate, error) {
103 // parameters and key (parameter block should come first)
104 if !foundKey {
105 // Encode parameters
108 - err = pem.Encode(keyBuilder, derBlock)
109 - if err != nil {
106 + if err := pem.Encode(keyBuilder, derBlock); err != nil {
107 return tls.Certificate{}, err
108 }
109
110 // Key must immediately follow
111 derBlock, bundle = pem.Decode(bundle)
112 if derBlock == nil || derBlock.Type != "EC PRIVATE KEY" {
116 - return tls.Certificate{}, fmt.Errorf("%s: expected elliptic private key to immediately follow EC parameters", fpath)
113 + return tls.Certificate{}, fmt.Errorf("expected elliptic private key to immediately follow EC parameters")
114 }
118 - err = pem.Encode(keyBuilder, derBlock)
119 - if err != nil {
115 + if err := pem.Encode(keyBuilder, derBlock); err != nil {
116 return tls.Certificate{}, err
117 }
118 foundKey = true
@@ -124,28 +120,27 @@ func x509CertFromCertAndKeyPEMFile(fpath string) (tls.Certificate, error) {
120 } else if derBlock.Type == "PRIVATE KEY" || strings.HasSuffix(derBlock.Type, " PRIVATE KEY") {
121 // RSA key
122 if !foundKey {
127 - err = pem.Encode(keyBuilder, derBlock)
128 - if err != nil {
123 + if err := pem.Encode(keyBuilder, derBlock); err != nil {
124 return tls.Certificate{}, err
125 }
126 foundKey = true
127 }
128 } else {
134 - return tls.Certificate{}, fmt.Errorf("%s: unrecognized PEM block type: %s", fpath, derBlock.Type)
129 + return tls.Certificate{}, fmt.Errorf("unrecognized PEM block type: %s", derBlock.Type)
130 }
131 }
132
133 certPEMBytes, keyPEMBytes := certBuilder.Bytes(), keyBuilder.Bytes()
134 if len(certPEMBytes) == 0 {
140 - return tls.Certificate{}, fmt.Errorf("%s: failed to parse PEM data", fpath)
135 + return tls.Certificate{}, fmt.Errorf("failed to parse PEM data")
136 }
137 if len(keyPEMBytes) == 0 {
143 - return tls.Certificate{}, fmt.Errorf("%s: no private key block found", fpath)
138 + return tls.Certificate{}, fmt.Errorf("no private key block found")
139 }
140
141 cert, err := tls.X509KeyPair(certPEMBytes, keyPEMBytes)
142 if err != nil {
148 - return tls.Certificate{}, fmt.Errorf("%s: making X509 key pair: %v", fpath, err)
143 + return tls.Certificate{}, fmt.Errorf("making X509 key pair: %v", err)
144 }
145
146 return cert, nil