feat(storage): adjust CloudFront middleware (S06 Step 18)

What

S06 Step 18 — promote the CloudFront CDN storage middleware out of internal/storage/_imported/ into its final buildable location internal/storage/middleware/cloudfront/, adapting it to the AR storage layer.

The middleware is a storage.StorageDriver decorator that wraps an S3-backed driver and rewrites URLFor to return CloudFront signed URLs; every other method passes through to the wrapped driver. It signs URLs with an RSA private key (PEM) + key-pair ID, supports the none/aws/awsregion IP-filtering modes, and refreshes the AWS IP-range list on a background ticker that stops cleanly on shutdown.

Adaptations from the Container Registry source

  • Wired onto AR's narrowed storage.StorageDriver interface, whose URLFor carries only ctx (no *http.Request, no options map).
  • Consumes a middleware-owned, fully-resolved Parameters struct (mapped from config.CloudFrontConfig, RSA key already resolved via the LabKit v2 secret provider). No init-time registry; the composition root constructs it directly via NewCloudFrontMiddleware.
  • S3-only pairing is a construction error (inner must implement s3.S3BucketKeyer) rather than a per-request fallback.
  • slog structured logging in place of the inherited dcontext logger.
  • The promoted source under internal/storage/_imported/driver/middleware/cloudfront/ is deleted, per the plan's "_imported emptied as components are promoted out" intent.

Spec coverage

Spec: docs/specs/S06-storage-layer.md · Plan: docs/plans/2026-05-15-storage-layer.md (Step 18)

Notes for reviewers

  • go.mod core-SDK bump. Adds aws-sdk-go-v2/feature/cloudfront/sign v1.11.6 (required by the inherited signer), which transitively bumps core aws-sdk-go-v2 v1.41.11 → v1.42.0 and smithy-go v1.27.0 → v1.27.1. The sign module's go.mod requires v1.42.0, so there is no pin-to-v1.41.x path. Full suite + S3 driver build and pass against the bump.
  • Client-IP plumbing convention. AR's URLFor(ctx, path) has no *http.Request, so the client IP the IP filter evaluates travels on the context via package-level WithClientIP / clientIPFromContext helpers. Step 19 (Cloud CDN) will need equivalent plumbing — worth deciding then whether to keep per-package helpers or hoist a shared convention. Kept internal/undocumented this step, mirroring the S3/GCS adjusts.
  • Signed-URL expiry fallback (please eyeball). In NewCloudFrontMiddleware, the duration <= 0 fallback for signed-URL expiry currently reuses defaultUpdateFrequency (the 12h IP-range refresh constant). This is a carried-over porting shape; it likely wants its own distinct default.

Related to #163 (closed)

Edited by Pawel Rozlach

Merge request reports

Loading
Loading