Repository navigation
[BUG] Portainer AllowInsecureHttp logs no runtime warning while plaintext HTTP is in use #276
Description
Summary
DockerUpdateGuard:DockerInstances:<n>:Portainer:AllowInsecureHttp: true lets the Portainer integration send the API token, or username/password and the returned JWT, over plaintext http. It is only a validation switch today: nothing is logged at runtime while it is active. .squad/project.md requires the transport relaxations SkipCertificateValidation and AllowInsecureHttp to "stay opt-in and keep their runtime warnings", and SkipCertificateValidation already logs one. AllowInsecureHttp needs the same kind of warning, once per Docker instance.
Area
docs/areas/container-actions.md(Transport security)- Security area (
.squad/project.md): TLS and transport relaxations
Current behavior
- Validation accepts an
httpPortainerBaseUrlonly withAllowInsecureHttp: true(src/DockerUpdateGuard/Configuration/DockerUpdateGuardOptionsValidator.cs:337-340). There is no other use of the flag at runtime:PortainerClient.CreateAuthenticatedClientAsync(src/DockerUpdateGuard/Portainer/PortainerClient.cs:134-187) setsBaseAddressto thehttpURL and sendsX-API-Keyor posts the credentials to/api/authwithout a log entry. - For comparison,
SkipCertificateValidationlogs a warning once per instance name per process lifetime:src/DockerUpdateGuard/Docker/DockerInstanceClient.cs:287-292with_certificateValidationWarningInstances(line 34) andDockerInstanceCertificateValidationDisabled(EventId 3106,src/DockerUpdateGuard/Docker/DockerInstanceClientLogging.cs:90-93). docs/areas/container-actions.md:32states: "It is a validation switch: no runtime warning is logged while it is active."
Scenario: an operator sets BaseUrl: http://portainer:9000 plus AllowInsecureHttp: true for a quick test and forgets it. Nothing in the logs reveals that Portainer credentials have been sent unencrypted ever since.
Expected behavior
- When a Portainer request is about to be made for a Docker instance whose Portainer
BaseUrluses thehttpscheme (which validation only allows withAllowInsecureHttp: true), a warning is logged once per Docker instance name per process lifetime, e.g. "Portainer for Docker instance {DockerInstanceName} uses plaintext HTTP (AllowInsecureHttp); API token, credentials and session token are transmitted unencrypted". - The warning names the Docker instance (and may name the Portainer base URL), never the token, username, password or JWT.
- The warning is logged regardless of whether the call is a capability check or an action, and regardless of its outcome.
AllowInsecureHttpstays opt-in, defaultfalse;httpsURLs produce no warning, also whenAllowInsecureHttpistruebut unused.
Acceptance criteria
- Two capability checks / actions for the same instance with an
httpPortainer URL log exactly one warning. - Two different instances with
httpPortainer URLs log one warning each. - An
httpsPortainer URL withAllowInsecureHttp: truelogs no warning. - The warning text contains no secret (asserted in the test with a known token/password value).
- Unit tests (MSTest, naming per
docs/UNIT_TESTS.md) inPortainerClientTests, e.g.PortainerClientGetCapabilityWithInsecureHttpLogsWarningOncePerInstance,PortainerClientExecuteActionWithHttpsDoesNotLogInsecureWarning; follow the pattern ofDockerInstanceClientCreateHttpClientWhenSkipCertificateValidationLogsWarningOncePerInstance(src/Tests/DockerUpdateGuard.Tests/DockerInstanceClientTests.cs:652). - README (
README.md:187,AllowInsecureHttprow) says that a warning is logged at runtime while it is active, like theSkipCertificateValidationrow. - The area document
docs/areas/container-actions.mddescribes the new behavior and its Known gaps entry for this issue (currently Open questions item 4) is removed;docs/ARCHITECTURE.md, Security posture, stays consistent.
Implementation notes
PortainerClientis registered as a singleton (src/DockerUpdateGuard/ServiceCollectionExtensions.cs:104), so an instance-levelConcurrentDictionary<string, byte>(or a static one likeDockerInstanceClient's) keyed by instance name withTryAddis sufficient. Prefer an instance field over a static so tests stay isolated, unless the Lead prefers symmetry withDockerInstanceClient.- Add a source-generated
LoggerMessagetosrc/DockerUpdateGuard/Portainer/PortainerClientLogging.cswith the next free EventId in the 33xx range. - Decide on the scheme check from the parsed
BaseUrl(Uri.Scheme == Uri.UriSchemeHttp), not from the flag alone. - Optional (Lead's call): also log the warning once at startup for every enabled instance with an
httpPortainer URL, so it is visible even if Portainer is never contacted. If chosen, still keep it to once per instance. - No configuration or migration impact. Must not change: validation of
httpwithoutAllowInsecureHttpstill fails startup; secrets never logged (.squad/project.md).
Related
- F-040 (P6) — Critical paths without their own trace spans:
scan.run/Portainer/CVE/persistence never started #83 (Portainer calls without trace spans — same client, independent) - PR [Docs] Describe the current behavior by area #263 (area documents)
Generated by Claude Code
Activity
LarsLaskowski commentedon Oct 24, 2026
Squad working record
Tier
standard. The plan isspecs/issue-276/plan.mdat https://github.com/LarsLaskowski/DockerUpdateGuard/blob/edbd34bef9077330b456fe5eea3d6e9905a8fce7/specs/issue-276/plan.mdSummary: A
LoggerMessage(EventId 3311,Warning) is added toPortainerClient. It is logged once per Docker instance name per process, before the first Portainer request, when the parsedBaseUrlscheme ishttp. It names only the instance. Decision record 0006 weighs the first-request warning against a startup warning, both, and every request. The plan challenge raised one minor objection (the area document must say the warning appears only once a caller ofPortainerClientexists, #280), and Security approved the plan. Acceptance criteria AC1–AC5 are pinned by tests inPortainerClientTests. Pull request: #312.log.md
Squad run metrics
Generated by Claude Code