Following gives short examples for accessing anonymous S3 buckets with different tools
rclone
Best choice for bulk transfers. Anonymous access simply means omitting access_key_id and secret_access_key.
Add a remote to ~/.config/rclone/rclone.conf:
[public] type = s3 provider = Other endpoint = https://s3.example.org force_path_style = true
Then:
rclone lsf "public:$BUCKET/$PREFIX" # list rclone lsf "public:$BUCKET/" --dirs-only --max-depth 1 # top-level prefixes rclone size "public:$BUCKET/$PREFIX" # object count and volume rclone copy "public:$BUCKET/$KEY" ./download/ --progress # one object rclone copy "public:$BUCKET/$PREFIX" ./download/ --progress --transfers 8 rclone cat "public:$BUCKET/$KEY" --count 8 | od -c # stream, no download
Without a config file, quote the endpoint — rclone splits connection strings on : and would otherwise read it as the bare string https:
rclone lsf ':s3,provider=Other,endpoint="'"$ENDPOINT"'":'"$BUCKET/$PREFIX"
AWS CLI
Use --no-sign-request for anonymous access and --endpoint-url for a non-AWS endpoint. The CLI uses path-style automatically with a custom endpoint.
export AWS_DEFAULT_REGION=us-east-1 # botocore insists on a region export AWS_EC2_METADATA_DISABLED=true # skip the IMDS lookup alias s3anon="aws s3 --no-sign-request --endpoint-url $ENDPOINT"
Then:s3anon ls "s3://$BUCKET/" # top-level prefixes s3anon ls "s3://$BUCKET/$PREFIX" # list a prefix s3anon cp "s3://$BUCKET/$KEY" ./download/ # one object s3anon sync "s3://$BUCKET/$PREFIX" ./download/ --dryrun # bulk, preview first # object metadata / byte range aws s3api head-object --no-sign-request --endpoint-url "$ENDPOINT" --bucket "$BUCKET" --key "$KEY" --query ContentLength aws s3api get-object --no-sign-request --endpoint-url "$ENDPOINT" --bucket "$BUCKET" --key "$KEY" --range bytes=0-7 head.bin
Example
aws s3 ls --no-sign-request --endpoint-url https://s3.r1.cloud.eumetsat.int s3://seviri-meteosat-0-degree.fcdr.level15.netcdf/
Python — boto3
signature_version=UNSIGNED selects anonymous access.
import boto3
from botocore import UNSIGNED
from botocore.config import Config
ENDPOINT = "https://s3.example.org"
BUCKET = "my-bucket"
PREFIX = "some/path/"
KEY = "some/path/object.nc"
s3 = boto3.client(
"s3",
endpoint_url=ENDPOINT,
config=Config(
signature_version=UNSIGNED, # anonymous
s3={"addressing_style": "path"}, # needed for dotted bucket names
),
)
# top-level prefixes
for p in s3.list_objects_v2(Bucket=BUCKET, Delimiter="/").get("CommonPrefixes", []):
print(p["Prefix"])
# every object under a prefix
for page in s3.get_paginator("list_objects_v2").paginate(Bucket=BUCKET, Prefix=PREFIX):
for obj in page.get("Contents", []):
print(obj["Key"], obj["Size"])
s3.head_object(Bucket=BUCKET, Key=KEY)["ContentLength"] # size
s3.get_object(Bucket=BUCKET, Key=KEY, Range="bytes=0-7")["Body"].read()
s3.download_file(BUCKET, KEY, "./local_copy.nc") # multipart-aware
Python — s3fs
anon=True selects anonymous access. Use this when you want a filesystem-like API, or to read part of a remote netCDF/HDF5 file without downloading it.
import s3fs
fs = s3fs.S3FileSystem(
anon=True, # anonymous
client_kwargs={"endpoint_url": "https://s3.example.org"},
config_kwargs={"s3": {"addressing_style": "path"}}, # dotted bucket names
)
fs.ls(BUCKET) # top-level
fs.ls(f"{BUCKET}/{PREFIX}", detail=True) # with sizes
fs.info(f"{BUCKET}/{KEY}")["size"]
fs.get(f"{BUCKET}/{KEY}", "./local_copy.nc") # download
with fs.open(f"{BUCKET}/{KEY}", "rb") as f:
f.read(8)
Reading a remote netCDF lazily (needs xarray, h5netcdf, h5py) — only the chunks you touch are fetched:
import xarray as xr
with fs.open(f"{BUCKET}/{KEY}", "rb", block_size=4 * 1024 * 1024) as f:
with xr.open_dataset(f, engine="h5netcdf", group="measurements") as ds:
tile = ds["counts_hrv"][5000:5004, 2700:2706].values
Example — the snippet above on the SEVIRI bucket reads a 4×6 tile out of an 11136×5568 array in a 245 MiB file in about 2 seconds:[[162 161 163 155 153 155] [168 162 166 156 149 163] [188 159 150 157 149 148] [177 160 151 161 152 145]]
s3cmd — not supported
s3cmd cannot access anonymous buckets (with 2.4.0)
IPv6 note. These instructions assume your VM has IPv4, which EWC VMs do.
Some endpoints — s3.r1.cloud.eumetsat.int among them — publish both A and AAAA records but reject anonymous requests arriving over IPv6 with an empty-message 403 AccessDenied.
On a dual-stack host this looks like random failure (curl/rclone fail ~half the time; boto3/s3fs/AWS CLI fail every time, and AWS CLI v2 crashes with argument of type 'NoneType' is not a container or iterable) — in these cases, force IPv4 with curl -4, rclone --bind 0.0.0.0, or an /etc/hosts entry pinning the endpoint to its IPv4 address.
On an IPv6-only VM there is no workaround: use an IPv4-capable host, or ask the storage provider to fix the IPv6 path.