Component: keepercommander — Service Mode
Files:
keepercommander/service/decorators/security.py — get_rate_limit()
- Service Mode config pipeline:
handle_streamlined_config() → ServiceConfig.create_record()/save_config() → encrypt_config_file() → ConfigReader.read_config('rate_limiting')
Severity: Medium — a documented CLI flag for raising Service Mode's local rate limit does not reliably take effect in streamlined (-p <port>) mode, causing unexpected HTTP 429s even after the flag is set and causing the terraform provider to error out.
Summary
Commander Service Mode's local rate limiter (flask_limiter, default 60/minute) is documented as configurable via service-create -rl <limit>. In streamlined config mode (-p <port>), this value does not reliably reach the running process: get_rate_limit() — called fresh on every request via @limiter.limit(get_rate_limit, ...) — continues to return the old default even after -rl is passed with a higher value, causing HTTP 429 responses at a much lower request volume than the operator configured.
Root Cause
Tracing the flag's path from CLI to runtime reveals several lossy hops between the -rl value and what get_rate_limit() actually returns at request time:
handle_streamlined_config() writes the value via ServiceConfig.create_record()/save_config().
save_config() performs a plaintext save immediately followed by AES re-encryption keyed off the device's private key.
- At request time,
get_rate_limit() calls ConfigReader.read_config("rate_limiting"), which contains a broad try/except with a silent fallback to the hardcoded default on any read/decrypt issue.
In streamlined mode this chain does not reliably deliver the -rl value to the running process — live testing confirmed 429s still occurring at the effective default (60/minute) even after explicitly passing -rl 300/minute at service-create time.
Reproduction
- Start Service Mode in streamlined mode:
service-create -p <port> -rl 300/minute ....
- Issue requests at a rate above 60/minute but below 300/minute.
- Observe
429: rate limit exceeded responses despite the configured limit being 300/minute.
Proposed Fix
- Make
get_rate_limit()'s config read path fail loudly (or log a warning) rather than silently falling back to the default on any read/decrypt failure, so operators can detect the propagation gap.
- Alternatively, support setting the rate limit via an environment variable read directly by the running process at request time, bypassing the config-file round-trip entirely for streamlined-mode deployments.
References
keepercommander/service/decorators/security.py — get_rate_limit()
keepercommander/service/README.md — documented service-create -rl behavior and default limits (60/minute, 600/hour, 6000/day)
Component:
keepercommander— Service ModeFiles:
keepercommander/service/decorators/security.py—get_rate_limit()handle_streamlined_config()→ServiceConfig.create_record()/save_config()→encrypt_config_file()→ConfigReader.read_config('rate_limiting')Severity: Medium — a documented CLI flag for raising Service Mode's local rate limit does not reliably take effect in streamlined (
-p <port>) mode, causing unexpected HTTP 429s even after the flag is set and causing the terraform provider to error out.Summary
Commander Service Mode's local rate limiter (
flask_limiter, default60/minute) is documented as configurable viaservice-create -rl <limit>. In streamlined config mode (-p <port>), this value does not reliably reach the running process:get_rate_limit()— called fresh on every request via@limiter.limit(get_rate_limit, ...)— continues to return the old default even after-rlis passed with a higher value, causing HTTP 429 responses at a much lower request volume than the operator configured.Root Cause
Tracing the flag's path from CLI to runtime reveals several lossy hops between the
-rlvalue and whatget_rate_limit()actually returns at request time:handle_streamlined_config()writes the value viaServiceConfig.create_record()/save_config().save_config()performs a plaintext save immediately followed by AES re-encryption keyed off the device's private key.get_rate_limit()callsConfigReader.read_config("rate_limiting"), which contains a broadtry/exceptwith a silent fallback to the hardcoded default on any read/decrypt issue.In streamlined mode this chain does not reliably deliver the
-rlvalue to the running process — live testing confirmed 429s still occurring at the effective default (60/minute) even after explicitly passing-rl 300/minuteatservice-createtime.Reproduction
service-create -p <port> -rl 300/minute ....429: rate limit exceededresponses despite the configured limit being 300/minute.Proposed Fix
get_rate_limit()'s config read path fail loudly (or log a warning) rather than silently falling back to the default on any read/decrypt failure, so operators can detect the propagation gap.References
keepercommander/service/decorators/security.py—get_rate_limit()keepercommander/service/README.md— documentedservice-create -rlbehavior and default limits (60/minute, 600/hour, 6000/day)