diff --git a/app/cmd/server.go b/app/cmd/server.go index 840613b..d2c9f4c 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -6,12 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/libdns/cloudflare" - "github.com/libdns/duckdns" - "github.com/libdns/gandi" - "github.com/libdns/godaddy" - "github.com/libdns/namedotcom" - "github.com/libdns/vultr" "net" "net/http" "net/http/httputil" @@ -22,6 +16,12 @@ import ( "time" "github.com/caddyserver/certmagic" + "github.com/libdns/cloudflare" + "github.com/libdns/duckdns" + "github.com/libdns/gandi" + "github.com/libdns/godaddy" + "github.com/libdns/namedotcom" + "github.com/libdns/vultr" "github.com/mholt/acmez/acme" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -85,19 +85,36 @@ type serverConfigTLS struct { } type serverConfigACME struct { - Domains []string `mapstructure:"domains"` - Email string `mapstructure:"email"` - CA string `mapstructure:"ca"` - DisableHTTP bool `mapstructure:"disableHTTP"` - DisableTLSALPN bool `mapstructure:"disableTLSALPN"` - ListenHost string `mapstructure:"listenHost"` - AltHTTPPort int `mapstructure:"altHTTPPort"` - AltTLSALPNPort int `mapstructure:"altTLSALPNPort"` - DNSProvider serverConfigACMEDNSProvider `mapstructure:"dnsProvider"` - Dir string `mapstructure:"dir"` + // Common fields + Domains []string `mapstructure:"domains"` + Email string `mapstructure:"email"` + CA string `mapstructure:"ca"` + ListenHost string `mapstructure:"listenHost"` + Dir string `mapstructure:"dir"` + + // Type selection + Type string `mapstructure:"type"` + HTTP serverConfigACMEHTTP `mapstructure:"http"` + TLS serverConfigACMETLS `mapstructure:"tls"` + DNS serverConfigACMEDNS `mapstructure:"dns"` + + // Legacy fields for backwards compatibility + // Only applicable when Type is empty + DisableHTTP bool `mapstructure:"disableHTTP"` + DisableTLSALPN bool `mapstructure:"disableTLSALPN"` + AltHTTPPort int `mapstructure:"altHTTPPort"` + AltTLSALPNPort int `mapstructure:"altTLSALPNPort"` } -type serverConfigACMEDNSProvider struct { +type serverConfigACMEHTTP struct { + AltPort int `mapstructure:"altPort"` +} + +type serverConfigACMETLS struct { + AltPort int `mapstructure:"altPort"` +} + +type serverConfigACMEDNS struct { Name string `mapstructure:"name"` Config map[string]string `mapstructure:"config"` } @@ -304,14 +321,10 @@ func (c *serverConfig) fillTLSConfig(hyConfig *server.Config) error { Logger: logger, } cmIssuer := certmagic.NewACMEIssuer(cmCfg, certmagic.ACMEIssuer{ - Email: c.ACME.Email, - Agreed: true, - DisableHTTPChallenge: c.ACME.DisableHTTP, - DisableTLSALPNChallenge: c.ACME.DisableTLSALPN, - ListenHost: c.ACME.ListenHost, - AltHTTPPort: c.ACME.AltHTTPPort, - AltTLSALPNPort: c.ACME.AltTLSALPNPort, - Logger: logger, + Email: c.ACME.Email, + Agreed: true, + ListenHost: c.ACME.ListenHost, + Logger: logger, }) switch strings.ToLower(c.ACME.CA) { case "letsencrypt", "le", "": @@ -325,51 +338,80 @@ func (c *serverConfig) fillTLSConfig(hyConfig *server.Config) error { } cmIssuer.ExternalAccount = eab default: - return configError{Field: "acme.ca", Err: errors.New("unknown CA")} + return configError{Field: "acme.ca", Err: errors.New("unsupported CA")} } - if c.ACME.DNSProvider.Name != "" && c.ACME.DNSProvider.Config != nil { - switch strings.ToLower(c.ACME.DNSProvider.Name) { + switch strings.ToLower(c.ACME.Type) { + case "http": + cmIssuer.DisableHTTPChallenge = false + cmIssuer.DisableTLSALPNChallenge = true + cmIssuer.DNS01Solver = nil + cmIssuer.AltHTTPPort = c.ACME.HTTP.AltPort + case "tls": + cmIssuer.DisableHTTPChallenge = true + cmIssuer.DisableTLSALPNChallenge = false + cmIssuer.DNS01Solver = nil + cmIssuer.AltTLSALPNPort = c.ACME.TLS.AltPort + case "dns": + cmIssuer.DisableHTTPChallenge = true + cmIssuer.DisableTLSALPNChallenge = true + if c.ACME.DNS.Name == "" { + return configError{Field: "acme.dns.name", Err: errors.New("empty DNS provider name")} + } + if c.ACME.DNS.Config == nil { + return configError{Field: "acme.dns.config", Err: errors.New("empty DNS provider config")} + } + switch strings.ToLower(c.ACME.DNS.Name) { case "cloudflare": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &cloudflare.Provider{ - APIToken: c.ACME.DNSProvider.Config["cloudflare_api_token"], + APIToken: c.ACME.DNS.Config["cloudflare_api_token"], }, } case "duckdns": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &duckdns.Provider{ - APIToken: c.ACME.DNSProvider.Config["duckdns_api_token"], - OverrideDomain: c.ACME.DNSProvider.Config["duckdns_override_domain"], + APIToken: c.ACME.DNS.Config["duckdns_api_token"], + OverrideDomain: c.ACME.DNS.Config["duckdns_override_domain"], }, } case "gandi": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &gandi.Provider{ - BearerToken: c.ACME.DNSProvider.Config["gandi_api_token"], + BearerToken: c.ACME.DNS.Config["gandi_api_token"], }, } case "godaddy": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &godaddy.Provider{ - APIToken: c.ACME.DNSProvider.Config["godaddy_api_token"], + APIToken: c.ACME.DNS.Config["godaddy_api_token"], }, } case "namedotcom": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &namedotcom.Provider{ - Token: c.ACME.DNSProvider.Config["namedotcom_token"], - User: c.ACME.DNSProvider.Config["namedotcom_user"], - Server: c.ACME.DNSProvider.Config["namedotcom_server"], + Token: c.ACME.DNS.Config["namedotcom_token"], + User: c.ACME.DNS.Config["namedotcom_user"], + Server: c.ACME.DNS.Config["namedotcom_server"], }, } case "vultr": cmIssuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: &vultr.Provider{ - APIToken: c.ACME.DNSProvider.Config["vultr_api_token"], + APIToken: c.ACME.DNS.Config["vultr_api_token"], }, } + default: + return configError{Field: "acme.dns.name", Err: errors.New("unsupported DNS provider")} } + case "": + // Legacy compatibility mode + cmIssuer.DisableHTTPChallenge = c.ACME.DisableHTTP + cmIssuer.DisableTLSALPNChallenge = c.ACME.DisableTLSALPN + cmIssuer.AltHTTPPort = c.ACME.AltHTTPPort + cmIssuer.AltTLSALPNPort = c.ACME.AltTLSALPNPort + default: + return configError{Field: "acme.type", Err: errors.New("unsupported ACME type")} } cmCfg.Issuers = []certmagic.Issuer{cmIssuer} diff --git a/app/cmd/server_test.go b/app/cmd/server_test.go index 17a563d..d81a61a 100644 --- a/app/cmd/server_test.go +++ b/app/cmd/server_test.go @@ -34,19 +34,28 @@ func TestServerConfig(t *testing.T) { "sub1.example.com", "sub2.example.com", }, - Email: "haha@cringe.net", - CA: "zero", - DisableHTTP: true, - DisableTLSALPN: true, - AltHTTPPort: 9980, - AltTLSALPNPort: 9443, - Dir: "random_dir", - DNSProvider: serverConfigACMEDNSProvider{ - Name: "cloudflare", + Email: "haha@cringe.net", + CA: "zero", + ListenHost: "127.0.0.9", + Dir: "random_dir", + Type: "dns", + HTTP: serverConfigACMEHTTP{ + AltPort: 8888, + }, + TLS: serverConfigACMETLS{ + AltPort: 44333, + }, + DNS: serverConfigACMEDNS{ + Name: "gomommy", Config: map[string]string{ - "cloudflare_api_token": "xxxxxxxxxx", + "key1": "value1", + "key2": "value2", }, }, + DisableHTTP: true, + DisableTLSALPN: true, + AltHTTPPort: 8080, + AltTLSALPNPort: 4433, }, QUIC: serverConfigQUIC{ InitStreamReceiveWindow: 77881, diff --git a/app/cmd/server_test.yaml b/app/cmd/server_test.yaml index 1e04e80..86a2dcf 100644 --- a/app/cmd/server_test.yaml +++ b/app/cmd/server_test.yaml @@ -15,15 +15,22 @@ acme: - sub2.example.com email: haha@cringe.net ca: zero + listenHost: 127.0.0.9 + dir: random_dir + type: dns + http: + altPort: 8888 + tls: + altPort: 44333 + dns: + name: gomommy + config: + key1: value1 + key2: value2 disableHTTP: true disableTLSALPN: true - altHTTPPort: 9980 - altTLSALPNPort: 9443 - dir: random_dir - dnsProvider: - name: "cloudflare" - config: - cloudflare_api_token: "xxxxxxxxxx" + altHTTPPort: 8080 + altTLSALPNPort: 4433 quic: initStreamReceiveWindow: 77881