diff --git a/cmd/derper/cert.go b/cmd/derper/cert.go index d383c82f0..dfd776990 100644 --- a/cmd/derper/cert.go +++ b/cmd/derper/cert.go @@ -44,7 +44,7 @@ type certProvider interface { HTTPHandler(fallback http.Handler) http.Handler } -func certProviderByCertMode(mode, dir, hostname, eabKID, eabKey string) (certProvider, error) { +func certProviderByCertMode(mode, dir, hostname, eabKID, eabKey, email string) (certProvider, error) { if dir == "" { return nil, errors.New("missing required --certdir flag") } @@ -59,6 +59,9 @@ func certProviderByCertMode(mode, dir, hostname, eabKID, eabKey string) (certPro if eabKID == "" || eabKey == "" { return nil, errors.New("--certmode=gcp requires --acme-eab-kid and --acme-eab-key flags") } + if email == "" { + return nil, errors.New("--certmode=gcp requires --acme-email flag") + } keyBytes, err := decodeEABKey(eabKey) if err != nil { return nil, err @@ -73,6 +76,10 @@ func certProviderByCertMode(mode, dir, hostname, eabKID, eabKey string) (certPro } if hostname == "derp.tailscale.com" { certManager.HostPolicy = prodAutocertHostPolicy + } + if email != "" { + certManager.Email = email + } else if hostname == "derp.tailscale.com" { certManager.Email = "security@tailscale.com" } return certManager, nil diff --git a/cmd/derper/cert_test.go b/cmd/derper/cert_test.go index 3a8da4610..b4e18f695 100644 --- a/cmd/derper/cert_test.go +++ b/cmd/derper/cert_test.go @@ -91,7 +91,7 @@ func TestCertIP(t *testing.T) { t.Fatalf("Error closing key.pem: %v", err) } - cp, err := certProviderByCertMode("manual", dir, hostname, "", "") + cp, err := certProviderByCertMode("manual", dir, hostname, "", "", "") if err != nil { t.Fatal(err) } @@ -174,19 +174,25 @@ func TestGCPCertMode(t *testing.T) { dir := t.TempDir() // Missing EAB credentials - _, err := certProviderByCertMode("gcp", dir, "test.example.com", "", "") + _, err := certProviderByCertMode("gcp", dir, "test.example.com", "", "", "test@example.com") if err == nil { t.Fatal("expected error when EAB credentials are missing") } + // Missing email + _, err = certProviderByCertMode("gcp", dir, "test.example.com", "kid", "dGVzdC1rZXk", "") + if err == nil { + t.Fatal("expected error when email is missing") + } + // Invalid base64 - _, err = certProviderByCertMode("gcp", dir, "test.example.com", "kid", "not-valid!") + _, err = certProviderByCertMode("gcp", dir, "test.example.com", "kid", "not-valid!", "test@example.com") if err == nil { t.Fatal("expected error for invalid base64") } // Valid base64url (no padding) - cp, err := certProviderByCertMode("gcp", dir, "test.example.com", "kid", "dGVzdC1rZXk") + cp, err := certProviderByCertMode("gcp", dir, "test.example.com", "kid", "dGVzdC1rZXk", "test@example.com") if err != nil { t.Fatalf("base64url: %v", err) } @@ -195,7 +201,7 @@ func TestGCPCertMode(t *testing.T) { } // Valid standard base64 (with padding, gcloud format) - cp, err = certProviderByCertMode("gcp", dir, "test.example.com", "kid", "dGVzdC1rZXk=") + cp, err = certProviderByCertMode("gcp", dir, "test.example.com", "kid", "dGVzdC1rZXk=", "test@example.com") if err != nil { t.Fatalf("base64: %v", err) } diff --git a/cmd/derper/derper.go b/cmd/derper/derper.go index aeb2adb5d..16f531be0 100644 --- a/cmd/derper/derper.go +++ b/cmd/derper/derper.go @@ -65,6 +65,7 @@ var ( hostname = flag.String("hostname", "derp.tailscale.com", "TLS host name for certs, if addr's port is :443. When --certmode=manual, this can be an IP address to avoid SNI checks") acmeEABKid = flag.String("acme-eab-kid", "", "ACME External Account Binding (EAB) Key ID (required for --certmode=gcp)") acmeEABKey = flag.String("acme-eab-key", "", "ACME External Account Binding (EAB) HMAC key, base64-encoded (required for --certmode=gcp)") + acmeEmail = flag.String("acme-email", "", "ACME account contact email address (required for --certmode=gcp, optional for letsencrypt)") runSTUN = flag.Bool("stun", true, "whether to run a STUN server. It will bind to the same IP (if any) as the --addr flag value.") runDERP = flag.Bool("derp", true, "whether to run a DERP server. The only reason to set this false is if you're decommissioning a server but want to keep its bootstrap DNS functionality still running.") flagHome = flag.String("home", "", "what to serve at the root path. It may be left empty (the default, for a default homepage), \"blank\" for a blank page, or a URL to redirect to") @@ -345,7 +346,7 @@ func main() { if serveTLS { log.Printf("derper: serving on %s with TLS", *addr) var certManager certProvider - certManager, err = certProviderByCertMode(*certMode, *certDir, *hostname, *acmeEABKid, *acmeEABKey) + certManager, err = certProviderByCertMode(*certMode, *certDir, *hostname, *acmeEABKid, *acmeEABKey, *acmeEmail) if err != nil { log.Fatalf("derper: can not start cert provider: %v", err) }