Skip to content

Commit d451cd5

Browse files
authored
cmd/derper: add --acme-email flag for GCP cert mode (tailscale#18278)
GCP Certificate Manager requires an email contact on ACME accounts. Add --acme-email flag that is required for --certmode=gcp and optional for --certmode=letsencrypt. Fixes tailscale#18277 Signed-off-by: Raj Singh <raj@tailscale.com>
1 parent 2917ea8 commit d451cd5

3 files changed

Lines changed: 21 additions & 7 deletions

File tree

cmd/derper/cert.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ type certProvider interface {
4444
HTTPHandler(fallback http.Handler) http.Handler
4545
}
4646

47-
func certProviderByCertMode(mode, dir, hostname, eabKID, eabKey string) (certProvider, error) {
47+
func certProviderByCertMode(mode, dir, hostname, eabKID, eabKey, email string) (certProvider, error) {
4848
if dir == "" {
4949
return nil, errors.New("missing required --certdir flag")
5050
}
@@ -59,6 +59,9 @@ func certProviderByCertMode(mode, dir, hostname, eabKID, eabKey string) (certPro
5959
if eabKID == "" || eabKey == "" {
6060
return nil, errors.New("--certmode=gcp requires --acme-eab-kid and --acme-eab-key flags")
6161
}
62+
if email == "" {
63+
return nil, errors.New("--certmode=gcp requires --acme-email flag")
64+
}
6265
keyBytes, err := decodeEABKey(eabKey)
6366
if err != nil {
6467
return nil, err
@@ -73,6 +76,10 @@ func certProviderByCertMode(mode, dir, hostname, eabKID, eabKey string) (certPro
7376
}
7477
if hostname == "derp.tailscale.com" {
7578
certManager.HostPolicy = prodAutocertHostPolicy
79+
}
80+
if email != "" {
81+
certManager.Email = email
82+
} else if hostname == "derp.tailscale.com" {
7683
certManager.Email = "security@tailscale.com"
7784
}
7885
return certManager, nil

cmd/derper/cert_test.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func TestCertIP(t *testing.T) {
9191
t.Fatalf("Error closing key.pem: %v", err)
9292
}
9393

94-
cp, err := certProviderByCertMode("manual", dir, hostname, "", "")
94+
cp, err := certProviderByCertMode("manual", dir, hostname, "", "", "")
9595
if err != nil {
9696
t.Fatal(err)
9797
}
@@ -174,19 +174,25 @@ func TestGCPCertMode(t *testing.T) {
174174
dir := t.TempDir()
175175

176176
// Missing EAB credentials
177-
_, err := certProviderByCertMode("gcp", dir, "test.example.com", "", "")
177+
_, err := certProviderByCertMode("gcp", dir, "test.example.com", "", "", "test@example.com")
178178
if err == nil {
179179
t.Fatal("expected error when EAB credentials are missing")
180180
}
181181

182+
// Missing email
183+
_, err = certProviderByCertMode("gcp", dir, "test.example.com", "kid", "dGVzdC1rZXk", "")
184+
if err == nil {
185+
t.Fatal("expected error when email is missing")
186+
}
187+
182188
// Invalid base64
183-
_, err = certProviderByCertMode("gcp", dir, "test.example.com", "kid", "not-valid!")
189+
_, err = certProviderByCertMode("gcp", dir, "test.example.com", "kid", "not-valid!", "test@example.com")
184190
if err == nil {
185191
t.Fatal("expected error for invalid base64")
186192
}
187193

188194
// Valid base64url (no padding)
189-
cp, err := certProviderByCertMode("gcp", dir, "test.example.com", "kid", "dGVzdC1rZXk")
195+
cp, err := certProviderByCertMode("gcp", dir, "test.example.com", "kid", "dGVzdC1rZXk", "test@example.com")
190196
if err != nil {
191197
t.Fatalf("base64url: %v", err)
192198
}
@@ -195,7 +201,7 @@ func TestGCPCertMode(t *testing.T) {
195201
}
196202

197203
// Valid standard base64 (with padding, gcloud format)
198-
cp, err = certProviderByCertMode("gcp", dir, "test.example.com", "kid", "dGVzdC1rZXk=")
204+
cp, err = certProviderByCertMode("gcp", dir, "test.example.com", "kid", "dGVzdC1rZXk=", "test@example.com")
199205
if err != nil {
200206
t.Fatalf("base64: %v", err)
201207
}

cmd/derper/derper.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ var (
6565
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")
6666
acmeEABKid = flag.String("acme-eab-kid", "", "ACME External Account Binding (EAB) Key ID (required for --certmode=gcp)")
6767
acmeEABKey = flag.String("acme-eab-key", "", "ACME External Account Binding (EAB) HMAC key, base64-encoded (required for --certmode=gcp)")
68+
acmeEmail = flag.String("acme-email", "", "ACME account contact email address (required for --certmode=gcp, optional for letsencrypt)")
6869
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.")
6970
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.")
7071
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() {
345346
if serveTLS {
346347
log.Printf("derper: serving on %s with TLS", *addr)
347348
var certManager certProvider
348-
certManager, err = certProviderByCertMode(*certMode, *certDir, *hostname, *acmeEABKid, *acmeEABKey)
349+
certManager, err = certProviderByCertMode(*certMode, *certDir, *hostname, *acmeEABKid, *acmeEABKey, *acmeEmail)
349350
if err != nil {
350351
log.Fatalf("derper: can not start cert provider: %v", err)
351352
}

0 commit comments

Comments
 (0)