diff --git a/cmd/server/assets/realmadmin/_form_abuse_prevention.html b/cmd/server/assets/realmadmin/_form_abuse_prevention.html index 735b31f30..1a5c1bfdc 100644 --- a/cmd/server/assets/realmadmin/_form_abuse_prevention.html +++ b/cmd/server/assets/realmadmin/_form_abuse_prevention.html @@ -31,14 +31,16 @@
- {{if gt $quotaRemaining $quotaLimit}} - If your remaining quota exceeds the maximum quota, it means a realm + {{if gt $quotaRemaining $quotaLimit}} + +
+ {{end}}
diff --git a/pkg/config/admin_server_config.go b/pkg/config/admin_server_config.go index 051a4b5c7..8c8a54e54 100644 --- a/pkg/config/admin_server_config.go +++ b/pkg/config/admin_server_config.go @@ -48,7 +48,7 @@ type AdminAPIServerConfig struct { CollisionRetryCount uint `env:"COLLISION_RETRY_COUNT,default=6"` AllowedSymptomAge time.Duration `env:"ALLOWED_PAST_SYMPTOM_DAYS,default=336h"` // 336h is 14 days. - EnforceRealmQuotas bool `env:"ENFORCE_REALM_QUOTAS, default=false"` + EnforceRealmQuotas bool `env:"ENFORCE_REALM_QUOTAS, default=true"` // For EN Express, the link will be // https://[realm-region].[ENX_REDIRECT_DOMAIN]/v?c=[longcode] diff --git a/pkg/config/server_config.go b/pkg/config/server_config.go index 3622250c6..ed54f78e4 100644 --- a/pkg/config/server_config.go +++ b/pkg/config/server_config.go @@ -77,7 +77,7 @@ type ServerConfig struct { ServerName string `env:"SERVER_NAME,default=Diagnosis Verification Server"` CollisionRetryCount uint `env:"COLLISION_RETRY_COUNT,default=6"` AllowedSymptomAge time.Duration `env:"ALLOWED_PAST_SYMPTOM_DAYS,default=336h"` // 336h is 14 days. - EnforceRealmQuotas bool `env:"ENFORCE_REALM_QUOTAS, default=false"` + EnforceRealmQuotas bool `env:"ENFORCE_REALM_QUOTAS, default=true"` AssetsPath string `env:"ASSETS_PATH,default=./cmd/server/assets"` diff --git a/pkg/controller/realmadmin/settings.go b/pkg/controller/realmadmin/settings.go index 708348ccb..2e88fe523 100644 --- a/pkg/controller/realmadmin/settings.go +++ b/pkg/controller/realmadmin/settings.go @@ -199,11 +199,38 @@ func (c *Controller) HandleSettings() http.Handler { } // Abuse prevention + var abusePreventionJustEnabled bool if form.AbusePrevention { + abusePreventionJustEnabled = !realm.AbusePreventionEnabled && form.AbusePreventionEnabled + realm.AbusePreventionEnabled = form.AbusePreventionEnabled realm.AbusePreventionLimitFactor = form.AbusePreventionLimitFactor } + // If abuse prevention was just enabled, create the initial bucket so + // enforcement works. We do this before actually saving the configuration on + // the realm to avoid a race where someone is issuing a code where abuse + // prevention has been enabled, but the quota has not been set. In that + // case, the quota would be the "default" quota for the limiter, which is + // not ideal or correct. + // + // Even if saving the realm fails, there's no harm in doing this early. It's + // an idempotent operation that TTLs out after a week anyway. + if abusePreventionJustEnabled { + dig, err := digest.HMACUint(realm.ID, c.config.RateLimit.HMACKey) + if err != nil { + controller.InternalError(w, r, c.h, err) + return + } + key := fmt.Sprintf("realm:quota:%s", dig) + limit := uint64(realm.AbusePreventionEffectiveLimit()) + ttl := 7 * 24 * time.Hour + if err := c.limiter.Set(ctx, key, limit, ttl); err != nil { + controller.InternalError(w, r, c.h, err) + return + } + } + // Save realm if err := c.db.SaveRealm(realm, currentUser); err != nil { flash.Error("Failed to update realm: %v", err)