Skip to content

chore(secrets): extract IMDS-blocking transport into shared pkg/httpclient #25

Description

@cristim

Severity / Confidence

P3, high confidence. Chore. Duplicated SSRF-defense helper across two modules; no behavior change pending refactor.

Affected files

  • internal/secrets/azure_resolver.go:17-60 (duplicate imdsAddresses, blockIMDSDialer, imdsBlockingTransport)
  • providers/azure/internal/httpclient/httpclient.go:14-53 (original imdsAddresses, blockIMDSDialer, New)

Evidence

PR LeanerCloud/cloud-commitments-cli#1224 added an IMDS-blocking transport to NewAzureResolver (imdsBlockingTransport() at internal/secrets/azure_resolver.go:46). The implementation is bit-for-bit equivalent to httpclient.New() at providers/azure/internal/httpclient/httpclient.go:38: same imdsAddresses map (169.254.169.254, fd00:ec2::254), same blockIMDSDialer struct + DialContext, same 10s/30s/30s timeouts, same http.Transport fields.

The duplication is currently forced by Go's internal/ import rule plus the module boundary (providers/azure/go.mod is a separate module from the root). The root-module internal/secrets package cannot import github.com/LeanerCloud/CUDly/providers/azure/internal/httpclient.

Memory feedback_azure_use_httpclient_new.md explicitly says: "Every Azure service client must use httpclient.New(), not &http.Client{Timeout: ...}. Any new Azure service client that omits this step loses the SSRF defense-in-depth without any compile-time signal." Two parallel implementations defeat the grep-based regression check the memory recommends (grep '&http.Client{' in providers/azure/).

Recommendation

Extract imdsAddresses, blockIMDSDialer, and the New() constructor into a shared, non-internal package importable by both modules:

  • pkg/httpclient/httpclient.go (the pkg/ module already has its own go.mod, so it can be required by both root and providers/azure/).
  • Replace the contents of providers/azure/internal/httpclient/httpclient.go and internal/secrets/azure_resolver.go IMDS helpers with a re-export / direct call into the new pkg/httpclient.New().
  • Keep the regression test pattern: each consuming module retains a small test that calls pkg/httpclient.New() and asserts a request to http://169.254.169.254/... errors with "blocked".

This collapses the duplication and gives the grep guard a single canonical answer.

Triage labels

type/chore, priority/p3, severity/low, urgency/eventually, impact/internal, effort/s, triaged.

Source: PR LeanerCloud/cloud-commitments-cli#1224 adversarial review.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions