We run a small asset pipeline that stores user uploads in an S3-compatible bucket (Scaleway Object Storage, Paris region). The bucket had a policy intended to be France-only: deny any request whose aws:RequestedRegion was not fr-par. It worked for a year, then one Tuesday our CI started failing with 403s on PutObject from the runner in eu-west-3. Nothing in the app code had changed. The policy had been edited six weeks earlier by someone who added a second statement and dropped an aws:SourceVpc condition we had been relying on without noticing.
I reproduced it with the CLI, which is the only way I trust for policy debugging:
aws --endpoint-url https://s3.fr-par.scw.cloud s3api put-object \
--bucket uploads-prod \
--key probe.txt --body /dev/null
That succeeded from my laptop because my profile had a session with the right region. From the runner it failed. I dumped the caller identity and the request context:
aws sts get-caller-identity
aws s3api get-bucket-policy --bucket uploads-prod > policy.json
Reading policy.json next to the runner’s env showed the mismatch: the policy allowed s3:* only when aws:RequestedRegion equals fr-par, but the runner was signing requests with eu-west-3 while hitting the fr-par endpoint. The endpoint does not override the signing region in the policy evaluation for this provider, or at least it did not for us. So the request was region-correct at the wire level and region-wrong at the policy level.
The fix was to stop using aws:RequestedRegion as a proxy for “is this request from France” and instead pin the endpoint in the client config, then scope the policy to the bucket ARN and a service account, not a region string:
# ~/.aws/config
[profile uploads-ci]
region = fr-par
endpoint_url = https://s3.fr-par.scw.cloud
s3 =
addressing_style = path
And in the policy, replace the region condition with an explicit deny on anything that is not the bucket’s own ARN, plus an allow for the CI service account. Region conditions look like a clean way to express data residency, but they are evaluated against the signer, not the network path, and every tool signs differently. If you need residency, enforce it at the bucket endpoint and in the network, and treat the policy as identity control, not geography control.
Where this breaks: any client that sets region globally but talks to a different endpoint, and any CI that inherits a region from a parent profile. I have also seen aws:RequestedRegion pass when a request is proxied through a regional gateway, which makes it worse than useless for a hard boundary. The cost of the fix is that the CI profile now has to be distributed with the repo’s bootstrap, and rotated when the service account changes.
What does your team use to enforce data residency when the storage backend is S3-compatible but the policy language is not quite AWS? I am curious whether anyone has a check that fails the build when the signing region and endpoint diverge.
0 条评论
还没有评论,写下第一条。