Access other storage backends via the S3 API
S3Proxy implements the S3 API and proxies requests, enabling several use cases:
Docker Hub hosts a Docker image and has instructions on how to run it.
Reference manifests show how to wire the Docker image into Kubernetes, including health probes, graceful shutdown, and Secret-based credentials. Helm users may prefer the third-party s3proxy-chart.
Users can download releases
from GitHub. Developers can build the project by running mvn package which
produces a binary at target/s3proxy. S3Proxy requires Java 17 or newer to
run.
Configure S3Proxy via a properties file. An example using the local file system as the storage backend with anonymous access:
s3proxy.authorization=none
s3proxy.endpoint=http://127.0.0.1:8080
jclouds.provider=filesystem
jclouds.filesystem.basedir=/tmp/s3proxyFirst create the filesystem basedir:
mkdir /tmp/s3proxyNext run S3Proxy. Linux and Mac OS X users can run the executable jar:
chmod +x s3proxy
s3proxy --properties s3proxy.confWindows users must explicitly invoke java:
java -jar s3proxy --properties s3proxy.confFinally test by creating a bucket then listing all the buckets:
$ curl --request PUT http://localhost:8080/testbucket
$ curl http://localhost:8080/
<?xml version="1.0" ?><ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Owner><ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID><DisplayName>[email protected]</DisplayName></Owner><Buckets><Bucket><Name>testbucket</Name><CreationDate>2015-08-05T22:16:24.000Z</CreationDate></Bucket></Buckets></ListAllMyBucketsResult>Maven Central hosts S3Proxy artifacts and the wiki has instructions on Java use.
See the wiki for examples of configurations.
jclouds.identity and jclouds.credential name the enduring half of a backend
credential. Credentials that expire carry a third part, which
jclouds.session-token names: an AWS session token, an Azure shared access
signature, a Google OAuth 2.0 access token, or a Keystone token.
jclouds.provider=aws-s3
jclouds.identity=ASIAIOSFODNN7EXAMPLE
jclouds.credential=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
jclouds.session-token=IQoJb3JpZ2luX2VjEExampleA token written into the properties file serves no longer than the token
itself does. A deployment that rotates them embeds S3Proxy and hands
BlobStores.create a Supplier<Credentials> instead: a store asks it again
for every request it signs rather than keeping the first answer, so an
assumed role, a reissued signature, or a refreshed access token takes effect
without the store being rebuilt.
Naming no credentials at all defers to the chain each backend's own tools
read -- environment, profile, container, and instance role for aws-s3,
DefaultAzureCredential for azureblob, and application default credentials
for google-cloud-storage -- every one of which renews what it finds.
Three credentials are read only when the store is built, because nothing
about them expires: an Azure account key, which the client copies out when it
is created; a Google service account key, which mints and renews access
tokens of its own; and the sftp password, presented once when the
connection opens. openstack-swift re-reads the supplier whenever the
Keystone token it holds nears its expiry.
S3Proxy can be configured to assign buckets to different backends with the same credentials. The configuration in the properties file is as follows:
s3proxy.bucket-locator.1=bucket
s3proxy.bucket-locator.2=another-bucketIn addition to the explicit names, glob syntax can be used to configure many buckets for a given backend.
A bucket (or a glob) cannot be assigned to multiple backends.
S3Proxy can modify its behavior based on middlewares:
S3Proxy can listen on HTTPS by setting the secure-endpoint and configuring a keystore. You can read more about how configure S3Proxy for SSL Support in the dedicated wiki page with Docker, Kubernetes or simply Java.
S3Proxy has broad compatibility with the S3 API, however, it does not support:
x-amz-grant-* headers and any grant naming a specific granteepart-number-markerpartNumber, unless the object has only one partx-amz-expected-bucket-ownerS3Proxy emulates the following operations:
filesystem and transient backends, which store a stub object to carry the metadataSome limitations depend on the storage backend:
| limitation | backends |
|---|---|
| no per-object ACLs, including public-read | azureblob, openstack-swift |
| ETag is not the object MD5 | azureblob, google-cloud-storage |
Cache-Control not preserved |
google-cloud-storage, openstack-swift |
Content-Encoding not preserved |
google-cloud-storage |
Content-Language not preserved |
openstack-swift |
Expires not preserved |
azureblob |
max-keys=0 not honored |
azureblob |
UploadPartCopy streams the data through S3Proxy instead of copying it on the backend |
filesystem, transient, openstack-swift, sftp |
conditional PUT refuses If-Match, honoring only If-None-Match: * |
openstack-swift |
| no object versioning, see #1137 | azureblob |
| no object versioning, see #1138 | filesystem |
| no object versioning, for want of a service analog | openstack-swift, sftp |
| no server-side encryption, see #1134 | google-cloud-storage |
| no server-side encryption, by design or for want of a service analog | filesystem, openstack-swift, sftp |
| SSE-S3 only: AES256 relayed from Azure's own at-rest encryption; SSE-C (#1135) and SSE-KMS refused | azureblob |
The backends that do version objects come by it differently. aws-s3
passes the requests through to a service that already versions, while
google-cloud-storage translates S3 versioning onto GCS object generations,
emulating delete markers as zero-byte generations; because GCS versioning is
simply on or off, it refuses the Suspended state. transient implements
versioning itself, keeping every version but the current one as a hidden
object. filesystem shares that implementation and declines it: its versions
would outlive the process, settling a layout on disk that S3Proxy would owe
its users from then on.
Server-side encryption divides aws-s3 and transient from most of the
rest, and divides the two nio2 stores on the same line. aws-s3 forwards the header families --
SSE-S3, SSE-KMS and SSE-C alike -- to a service that encrypts, while
transient answers them itself: what it holds rests in a filesystem that
dies with the process, so an object's encryption is the headers and nothing
besides, which is all S3 lets a caller observe of SSE-S3 anyway. SSE-C has
an observable half beyond the headers -- an object stored under a customer's
key answers only to that key -- and transient enforces it, keeping the
key's MD5 and never the key. Unlike S3, S3Proxy accepts SSE-C over plain
HTTP, its TLS commonly terminating elsewhere. filesystem writes to a real
disk, where reporting AES256 over plaintext someone could go and read would
be a claim S3Proxy has no business making, so it refuses every one of these
headers instead. azureblob stands halfway in: Azure encrypts everything
at rest and says so on each response, which the store relays as SSE-S3 --
an object's AES256 is Azure's answer, not S3Proxy's assertion -- while
SSE-C and SSE-KMS are refused rather than silently accepted.
Bucket default encryption, the ?encryption subresource, rides the same
divide. aws-s3 passes the configuration through, transient keeps it and
rests unadorned writes under it -- a write naming its own encryption still
wins -- and every other backend answers NotImplemented. A bucket never
given a configuration answers the 404 S3 spells
ServerSideEncryptionConfigurationNotFoundError rather than the account-wide
AES256 default modern AWS invents, which is also what the Ceph s3-tests
expect of it.
The filesystem backend stores a key as a path, so on Windows a key
holding a backslash is refused with 400 InvalidArgument. Windows reads
that character as a directory separator and no file name can hold one, so
the key back\slash.txt would come back from a listing as
back/slash.txt and answer for whoever wrote that key instead --
two S3 keys on one object. Keys are spelled with forward slashes on every
host, whatever the filesystem underneath.
aws-s3, azureblob and google-cloud-storage copy a part on the server,
in one request and w
No open issues yet, or sync has not completed.