v1.8.0
Bucket-scoped write tokens, so a credential can be limited to a single bucket instead of the whole service.
Note:
v1.6.61was the previous tag, so this release also carries the untagged 1.7.0, 1.7.1 and 1.7.2 work. Read the upgrade notes at the bottom before deploying: those releases contain breaking changes.
Bucket-scoped write tokens
A token can now authorize writes to exactly one bucket. Wire format is Authorization: Bearer <bucket>:<token>.
- Accepted on
/upload,/batch/upload,/upload-url,/batch/delete,DELETE /{bucket}/{path}and/resize. Thebucketform/body field becomes optional for these callers because the token's bucket is authoritative; naming a different bucket returns 403. - Rejected on the operator endpoints (
/aws/*,/minio/*,/monitor,/metrics,/ws), which act on arbitrary buckets or expose service-wide data and still require the generalTOKEN. - Configured in
config/tokens.json(template:config/tokens.template.json, path overridable viaTOKENS_FILE) and read once at boot. The file is git-ignored and docker-ignored, mounted read-only at runtime, and never baked into an image layer. - Secrets are SHA-256 hashed in memory after load and compared in constant time. The bucket prefix is attacker-controlled and grants nothing on its own: the secret is only ever compared against the entry for that exact bucket.
- A missing token file is not an error. Deployments that use only the general
TOKENbehave exactly as before. A file that exists but is invalid stops boot rather than silently serving with missing credentials.
Read isolation is out of scope: GET /{bucket}/{path} stays public, so this covers writes only.
Security
- Rate-limit bypass fixed. The rate-limit key was built from the raw, unverified
Authorizationheader, so a client sending a different random token on every request minted a fresh counter each time and was never rate limited. The key is now derived from the verified identity of the credential (<ip>|general,<ip>|bucket:<name>, or the plain<ip>for anything that does not authenticate). This also removes token material from the Redis keyspace. - Operator endpoints separated.
AuthMiddlewarewas split into a general-token-only gate and a bucket-aware one. Without this a bucket-scoped token could have calledDELETE /minio/{bucket}/deleteon any bucket or started cost-bearing Glacier retrievals. - Bucket names validated on creation. Upload paths that implicitly create a missing bucket now require 3-63 characters of lowercase letters, digits or
-. Previously any string could create an arbitrarily named bucket. Existing buckets are not re-validated.
Also included since v1.6.61
From 1.7.2:
- ImageMagick RCE surface hardened.
POST /resizewas unauthenticated and fed request bytes straight into ImageMagick; it is now auth-gated and runs the same magic-number content check as the upload endpoints. A strictpolicy.xmldisables the Ghostscript delegate chain (PS/EPS/PDF/XPS), the ImageTragick coder class, indirect file reads and all delegates. Raster decode and SVG upload/serving are unaffected.
From 1.7.1:
- Upload to a non-existent bucket now creates it instead of failing.
DELETE /batch/deletereachesBatchDeleteagain; it was shadowed by theDELETE /{bucket}/*wildcard.
From 1.7.0:
- Auth bypass fixed: boot fails fast when
TOKENis empty, and token comparison is constant-time. Previously an empty server token let an empty client token authenticate. - SSRF fixed:
/upload-urlrejects non-http(s) schemes and private/loopback/link-local (including cloud-metadata) targets, validated at request time and at dial time, so DNS rebinding does not get through. Opt out withUPLOAD_URL_ALLOW_PRIVATE=true. - Path traversal fixed in the Glacier async local-download target.
- Resize DoS hardened: on-the-fly resize dimensions are clamped to
MAX_RESIZE_DIMENSION(default 4096), closing a uint-wraparound giant-allocation path on the public GET route. - MIME hardening:
X-Content-Type-Options: nosniffon all responses and a sandbox CSP on served SVG objects. /metricsnow requires a Bearer token and/wsrequires atokenquery parameter; both were previously open.- Uploads with an image extension must actually be valid images. Opt-in
optimize=truestores a visually-lossless, size-reduced version. Process-wide ImageMagick resource limits bound decode cost against decompression bombs. - Global panic recovery, a rate limiter that fails open instead of panicking on a Redis outage, batch size caps, a bounded header buffer, and
..rejection in object keys.
Upgrade notes
Coming from v1.8.0's own changes, nothing to do. With no config/tokens.json the service behaves exactly as before, using the general TOKEN alone. Rate-limit counters in flight at deploy time are orphaned by the new key format and expire on their own within the rate-limit window.
Coming from v1.6.61, three things can break an existing deployment:
TOKENmust be set. The service now refuses to boot with an empty token./metricsand/wsnow require authentication. Prometheus scrapers need the token as a Bearer header; WebSocket clients need?token=.MINIO_USE_SSLis now honored. If your.envsets it, confirm the value matches your MinIO deployment.
To start using bucket-scoped tokens:
cp config/tokens.template.json config/tokens.json
openssl rand -hex 32 # one token per bucket, 32 characters minimum
$EDITOR config/tokens.json
docker compose up -d --build # the file is read once at bootFull changelog: v1.6.61...v1.8.0