From d819fd2f839c93825ce895cbb4755d3388d4d62e Mon Sep 17 00:00:00 2001 From: Weston Haught Date: Wed, 18 Nov 2020 14:16:42 -0800 Subject: [PATCH] Maintenance mode env var --- cmd/apiserver/main.go | 4 ++-- docs/api.md | 4 +++- pkg/api/api.go | 2 ++ pkg/config/admin_server_config.go | 7 +++++++ pkg/config/issue.go | 1 + pkg/config/server_config.go | 7 +++++++ pkg/controller/issueapi/issue.go | 6 ++++++ 7 files changed, 28 insertions(+), 3 deletions(-) diff --git a/cmd/apiserver/main.go b/cmd/apiserver/main.go index fca60cdf5..feb7dd785 100644 --- a/cmd/apiserver/main.go +++ b/cmd/apiserver/main.go @@ -173,7 +173,7 @@ func realMain(ctx context.Context) error { chaffDet := chaff.HeaderDetector("X-Chaff") // POST /api/verify - verifyChaff, err := chaff.NewTracker(chaff.NewJSONResponder(encodeVerifyReponse), chaff.DefaultCapacity) + verifyChaff, err := chaff.NewTracker(chaff.NewJSONResponder(encodeVerifyResponse), chaff.DefaultCapacity) if err != nil { return fmt.Errorf("error creating chaffer: %v", err) } @@ -212,7 +212,7 @@ func makePadFromChaff(s string) api.Padding { return api.Padding(s) } -func encodeVerifyReponse(s string) interface{} { +func encodeVerifyResponse(s string) interface{} { return api.VerifyCodeResponse{Padding: makePadFromChaff(s)} } diff --git a/docs/api.md b/docs/api.md index 853ea17eb..78148979a 100644 --- a/docs/api.md +++ b/docs/api.md @@ -100,6 +100,8 @@ Possible error code responses. New error codes may be added in future releases. | `code_not_found` | 400 | No | The server has no record of that code. | | `invalid_test_type` | 400 | No | The client sent an accept of an unrecgonized test type | | `missing_date` | 400 | No | The realm requires either a test or symptom date, but none was provided. | +| `uuid_already_exists` | 409 | No | The UUID has already been used for an issued code | +| `maintenance_mode ` | 429 | Yes | The server is temporarily down for maintenance. Wait and retry later. | | `unsupported_test_type` | 412 | No | The code may be valid, but represents a test type the client cannot process. User may need to upgrade software. | | | 500 | Yes | Internal processing error, may be successful on retry. | @@ -152,7 +154,7 @@ Possible error code responses. New error codes may be added in future releases. | `token_invalid` | 400 | No | The provided token is invalid, or already used to generate a certificate | | `token_expired` | 400 | No | Code invalid or used, user may need to obtain a new code. | | `hmac_invalid` | 400 | No | The `ekeyhmac` field, when base64 decoded is not the right size (32 bytes) | -| `uuid_already_exists` | 409 | No | The UUID has already been used for an issued code | +| `maintenance_mode ` | 429 | Yes | The server is temporarily down for maintenance. Wait and retry later. | | | 500 | Yes | Internal processing error, may be successful on retry. | # Admin APIs diff --git a/pkg/api/api.go b/pkg/api/api.go index c2ff6722e..723c0c843 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -64,6 +64,8 @@ const ( ErrHMACInvalid = "hmac_invalid" // ErrUUIDAlreadyExists indicates that the UUID has already been used for an issued code. ErrUUIDAlreadyExists = "uuid_already_exists" + // ErrMaintenanceMode indicates that the server is read-only for maintenance. + ErrMaintenanceMode = "maintenance_mode" ) // ErrorReturn defines the common error type. diff --git a/pkg/config/admin_server_config.go b/pkg/config/admin_server_config.go index de877edd8..f433e7b92 100644 --- a/pkg/config/admin_server_config.go +++ b/pkg/config/admin_server_config.go @@ -40,6 +40,9 @@ type AdminAPIServerConfig struct { // production environments. DevMode bool `env:"DEV_MODE"` + // If MaintenanceMode is true, the server is temporarily read-only and will not issue codes. + MaintenanceMode bool `env:"MAINTENANCE_MODE"` + // Rate limiting configuration RateLimit ratelimit.Config @@ -109,3 +112,7 @@ func (c *AdminAPIServerConfig) GetRateLimitConfig() *ratelimit.Config { func (c *AdminAPIServerConfig) ObservabilityExporterConfig() *observability.Config { return &c.Observability } + +func (c *AdminAPIServerConfig) IsMaintenanceMode() bool { + return c.MaintenanceMode +} diff --git a/pkg/config/issue.go b/pkg/config/issue.go index 6c6e6cc24..0126c2197 100644 --- a/pkg/config/issue.go +++ b/pkg/config/issue.go @@ -28,4 +28,5 @@ type IssueAPIConfig interface { GetEnforceRealmQuotas() bool GetRateLimitConfig() *ratelimit.Config GetENXRedirectDomain() string + IsMaintenanceMode() bool } diff --git a/pkg/config/server_config.go b/pkg/config/server_config.go index 8ecd2ab70..dd956023a 100644 --- a/pkg/config/server_config.go +++ b/pkg/config/server_config.go @@ -94,6 +94,9 @@ type ServerConfig struct { // This includes CSRF protection base cookie. You want this false in production (the default). DevMode bool `env:"DEV_MODE"` + // If MaintenanceMode is true, the server is temporarily read-only and will not issue codes. + MaintenanceMode bool `env:"MAINTENANCE_MODE"` + // Rate limiting configuration RateLimit ratelimit.Config } @@ -154,6 +157,10 @@ func (c *ServerConfig) ObservabilityExporterConfig() *observability.Config { return &c.Observability } +func (c *ServerConfig) IsMaintenanceMode() bool { + return c.MaintenanceMode +} + // FirebaseConfig represents configuration specific to firebase auth. type FirebaseConfig struct { APIKey string `env:"FIREBASE_API_KEY,required"` diff --git a/pkg/controller/issueapi/issue.go b/pkg/controller/issueapi/issue.go index 7382bda0d..6e07bbef4 100644 --- a/pkg/controller/issueapi/issue.go +++ b/pkg/controller/issueapi/issue.go @@ -98,6 +98,12 @@ func validateDate(date, minDate, maxDate time.Time, tzOffset int) (*time.Time, e func (c *Controller) HandleIssue() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if c.config.IsMaintenanceMode() { + c.h.RenderJSON(w, http.StatusTooManyRequests, + api.Errorf("server is read-only for maintenance").WithCode(api.ErrMaintenanceMode)) + return + } + ctx := observability.WithBuildInfo(r.Context()) logger := logging.FromContext(ctx).Named("issueapi.HandleIssue")