diff --git a/config.go b/config.go index 90f4df8..55ab718 100644 --- a/config.go +++ b/config.go @@ -209,6 +209,12 @@ func (config Config) validateAcmeConfig() *Config { acmeEmail := len(config.Connection.Downstream.Tls.Acme.Email) > 0 if acmeProvider || acmeDomain || acmeEmail { + if config.Connection.Downstream.Tls.Acme.GracePeriodDays == 0 { + config.Connection.Downstream.Tls.Acme.GracePeriodDays = 30 + } else if config.Connection.Downstream.Tls.Acme.GracePeriodDays > 30 { + config.panic("ACME grace period must be between 1 and 30 days") + } + if len(config.Connection.Downstream.Tls.Cert) > 0 { config.panic("cannot specify TLS cert with ACME configuration, it would be overridden.") } diff --git a/config_test.go b/config_test.go index 726c774..261a35e 100644 --- a/config_test.go +++ b/config_test.go @@ -284,6 +284,112 @@ func TestValidateAcmeEmail(t *testing.T) { config = config.validateAcmeConfig() } +//TestValidateAcmeEmail +func TestValidateAcmeGracePeriod30(t *testing.T) { + config := &Config{ + Connection: Connection{ + Downstream: Downstream{ + Http: Http{Port: 80}, + Tls: Tls{ + Acme: Acme{ + Domains: []string{"adyntest.com"}, + Provider: "letsencrypt", + Email: "noreply@example.org", + GracePeriodDays: 30, + }, + }, + }, + }, + } + + config = config.validateAcmeConfig() + + want := 30 + got := config.Connection.Downstream.Tls.Acme.GracePeriodDays + if want != got { + t.Errorf("want grace period days %v, got %v", want, got) + } +} + +func TestValidateAcmeGracePeriod15(t *testing.T) { + config := &Config{ + Connection: Connection{ + Downstream: Downstream{ + Http: Http{Port: 80}, + Tls: Tls{ + Acme: Acme{ + Domains: []string{"adyntest.com"}, + Provider: "letsencrypt", + Email: "noreply@example.org", + GracePeriodDays: 15, + }, + }, + }, + }, + } + + config = config.validateAcmeConfig() + + want := 15 + got := config.Connection.Downstream.Tls.Acme.GracePeriodDays + if want != got { + t.Errorf("want grace period days %v, got %v", want, got) + } +} + +func TestValidateDefaultAcmeGracePeriod(t *testing.T) { + config := &Config{ + Connection: Connection{ + Downstream: Downstream{ + Http: Http{Port: 80}, + Tls: Tls{ + Acme: Acme{ + Domains: []string{"adyntest.com"}, + Provider: "letsencrypt", + Email: "noreply@example.org", + }, + }, + }, + }, + } + + config = config.validateAcmeConfig() + + want := 30 + got := config.Connection.Downstream.Tls.Acme.GracePeriodDays + if want != got { + t.Errorf("want grace period days %v, got %v", want, got) + } +} + +func TestValidateAcmeGracePeriodFailsGreater30(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("config should have panicked with 31 days acme grace period") + } else { + t.Logf("normal config panic for 31 days acme grace period") + } + }() + + config := &Config{ + Connection: Connection{ + Downstream: Downstream{ + Http: Http{Port: 80}, + Tls: Tls{ + Acme: Acme{ + Domains: []string{"adyntest.com"}, + Provider: "letsencrypt", + Email: "noreply@example.org", + GracePeriodDays: 31, + }, + }, + }, + }, + } + + config = config.validateAcmeConfig() +} + //TestValidateAcmeDomainInvalidLeadingDotFails func TestValidateValidateAcmeDomainInvalidLeadingDotFails(t *testing.T) { defer func() { diff --git a/connection.go b/connection.go index 4a3e241..1849f63 100644 --- a/connection.go +++ b/connection.go @@ -62,6 +62,9 @@ type Acme struct { // Email for registration. Email string + + // Number of days before certificate expiry that triggers first renewal attempt + GracePeriodDays int } // Upstream connection params for remote servers that are being proxied diff --git a/integration/j8a4.yml b/integration/j8a4.yml index 6642655..b779f95 100644 --- a/integration/j8a4.yml +++ b/integration/j8a4.yml @@ -15,6 +15,7 @@ connection: domains: - api.adyntest.com - adyntest.com + gracePeriodDays: 30 upstream: socketTimeoutSeconds: 10 readTimeoutSeconds: 30 diff --git a/proxyhandler_test.go b/proxyhandler_test.go index 59400ec..c7a9807 100644 --- a/proxyhandler_test.go +++ b/proxyhandler_test.go @@ -659,7 +659,8 @@ func mockRuntime() *Runtime { }, Tls: Tls{ Acme: Acme{ - Domains: []string{"localhost"}, + Domains: []string{"localhost"}, + GracePeriodDays: 30, }}, }, }, diff --git a/tls.go b/tls.go index eb40a60..7d17da8 100644 --- a/tls.go +++ b/tls.go @@ -46,7 +46,8 @@ type TlsLink struct { } func (t TlsLink) expiresTooCloseForComfort() bool { - return time.Duration(t.remainingValidity) <= Days30 + gracePeriodDays := time.Hour * 24 * time.Duration(Runner.Connection.Downstream.Tls.Acme.GracePeriodDays) + return time.Duration(t.remainingValidity) <= gracePeriodDays } func (t TlsLink) expiryLongerThanLegalBrowserMaximum() bool { diff --git a/tls_test.go b/tls_test.go index 7dea096..1b10bf9 100644 --- a/tls_test.go +++ b/tls_test.go @@ -19,6 +19,7 @@ func TestPDurationAsString(t *testing.T) { } func TestTlsLinkRemainingValidity29DaysTooCloseForComfort(t *testing.T) { + Runner = mockRuntime() t1 := TlsLink{remainingValidity: PDuration(time.Hour * 24 * 29)} if !t1.expiresTooCloseForComfort() { t.Errorf("did not fire too close for comfort, but 29 days is") @@ -26,6 +27,7 @@ func TestTlsLinkRemainingValidity29DaysTooCloseForComfort(t *testing.T) { } func TestTlsLinkRemainingValidity31DaysNotTooCloseForComfort(t *testing.T) { + Runner = mockRuntime() t1 := TlsLink{remainingValidity: PDuration(time.Hour * 24 * 31)} if t1.expiresTooCloseForComfort() { t.Errorf("did fire too close for comfort but 31 days is not") @@ -33,6 +35,7 @@ func TestTlsLinkRemainingValidity31DaysNotTooCloseForComfort(t *testing.T) { } func TestTlsLinkTotalValidity397DaysWithinLegalBrowserPeriod(t *testing.T) { + Runner = mockRuntime() t1 := TlsLink{ remainingValidity: PDuration(time.Hour * 24 * 397), browserValidity: PDuration(time.Hour * 24 * 398), @@ -43,6 +46,7 @@ func TestTlsLinkTotalValidity397DaysWithinLegalBrowserPeriod(t *testing.T) { } func TestTlsLinkTotalValidity399DaysNotWithinLegalBrowserPeriod(t *testing.T) { + Runner = mockRuntime() t1 := TlsLink{ remainingValidity: PDuration(time.Hour * 24 * 399), browserValidity: PDuration(time.Hour * 24 * 398),