First let's setup our environment, and create a couple of s3 users: laura, and randy.
[ramartin@tower-osd4 ~]$ radosgw-admin user create --tenant laura --uid laura --display-name laura --access_key laura_access --secret laura_secret
[ramartin@tower-osd4 ~]$ radosgw-admin user create --tenant randy --uid randy --display-name randy --access_key randy_access --secret randy_secret
Remember with using clients like s3cmd, that the RGW will use an implicit tenant name based on the requestor, unless explicitly specified otherwise. Here we're going to create a bucket called 'share-bucket' and upload an object:
[ramartin@tower-osd4 ~]$ s3cmd ls -c laura.cfg
[ramartin@tower-osd4 ~]$ s3cmd mb s3://share-bucket -c laura.cfg
Bucket 's3://share-bucket/' created
[ramartin@tower-osd4 ~]$ s3cmd ls -c laura.cfg
2020-03-24 01:59 s3://share-bucket
[ramartin@tower-osd4 ~]$ s3cmd info s3://share-bucket -c laura.cfg
s3://share-bucket/ (bucket):
Location: default
Payer: BucketOwner
Expiration Rule: none
Policy: none
CORS: none
ACL: laura: FULL_CONTROL
[ramartin@tower-osd4 ~]$ s3cmd put example.txt s3://share-bucket -c laura.cfg
upload: 'example.txt' -> 's3://share-bucket/example.txt' [1 of 1]
Next we'll use my example script to have laura's user create a bucket policy granting user randy access to 'share-bucket'. In addition, the script will also list contents of 'share-bucket' and download a predefined file to the current users home_directory/Downloads folder:
#!/usr/bin/python3
import boto3
import botocore
import json
import os
from botocore.handlers import validate_bucket_name
tenant = 'randy' # --tenant
tenant_user = 'randy' # --uid
target_tenant = 'laura' # --tenant
target_user = 'laura' # --uid
target_bucket = 'share-bucket' # bucket to be shared with tenant$tenant_user
# set access/secret ids from environment variables
randy_access = os.environ.get('randy_access')
randy_secret = os.environ.get('randy_secret')
laura_access = os.environ.get('laura_access')
laura_secret = os.environ.get('laura_secret')
# configure endpoint url
endpoint = 'https://tower-osd4.cephtips.com'
# file to download
file_to_download = 'example.txt'
# download file destination
dest_file = os.path.expanduser('~')+'/Downloads/'+f'{file_to_download}'
# create s3 client
randy = boto3.client('s3', endpoint_url=endpoint, use_ssl=True, aws_access_key_id=randy_access, aws_secret_access_key=randy_secret)
# unregister bucket name validation
randy.meta.events.unregister('before-parameter-build.s3', validate_bucket_name)
# create s3 client
laura = boto3.client('s3', endpoint_url=endpoint, use_ssl=True, aws_access_key_id=laura_access, aws_secret_access_key=laura_secret)
# unregister bucket name validation
laura.meta.events.unregister('before-parameter-build.s3', validate_bucket_name)
try:
# Grant tenanted user access to: target_tenant, target_bucket
bucket_policy = {
'Version': '2012-10-17',
'Statement': [{
'Sid': 'ListBucket',
'Effect': 'Allow',
'Principal': {
"AWS": f"arn:aws:iam::{tenant}:user/{tenant_user}"
},
'Action': [
's3:ListBucket'
],
'Resource': f"arn:aws:s3::{target_tenant}:{target_bucket}"
}, {
'Sid': 'GetObject',
'Effect': 'Allow',
'Principal': {
"AWS": f"arn:aws:iam::{tenant}:user/{tenant_user}"
},
'Action': [
's3:GetObject'
],
'Resource': f"arn:aws:s3::{target_tenant}:{target_bucket}/*"
}]
}
# Convert the policy to a JSON string
bucket_policy = json.dumps(bucket_policy)
# Set the new policy on the given bucket as laura
laura.put_bucket_policy(Bucket=target_bucket, Policy=bucket_policy)
print( "\r\n" + "Bucket policy configured.")
except botocore.exceptions.ClientError as e:
print(e)
try:
# Get a list of objects in target_bucket as tenant_user
objects = randy.list_objects(Bucket=f'{target_tenant}:{target_bucket}')
print("\r\n" + "The following objects are in " + f'{target_tenant}' + "'s" + " [bucket=" + f'{target_bucket}' + "] :")
print(objects)
except botocore.exceptions.ClientError as e:
print(e)
try:
# Download f'{dest_file}' from share-bucket as randy
randy.download_file(f'{target_tenant}:{target_bucket}', f'{file_to_download}', f'{dest_file}')
print( "\r\n" + f'{dest_file}' + " File Download Complete. ")
except botocore.exceptions.ClientError as e:
print(e)
stdout Reference:
[ramartin@tower-osd4 ~]$ python randy.py
Bucket policy configured.
The following objects are in laura's [bucket=share-bucket] :
{'ResponseMetadata': {'RequestId': 'tx000000000000000000114-005e798920-8589-default', 'HostId': '', 'HTTPStatusCode': 200, 'HTTPHeaders': {'transfer-encoding': 'chunked', 'x-amz-request-id': 'tx000000000000000000114-005e798920-8589-default', 'content-type': 'application/xml', 'date': 'Tue, 24 Mar 2020 04:14:24 GMT', 'connection': 'Keep-Alive'}, 'RetryAttempts': 0}, 'IsTruncated': False, 'Marker': '', 'Contents': [{'Key': 'example.txt', 'LastModified': datetime.datetime(2020, 3, 24, 3, 25, 37, 682000, tzinfo=tzutc()), 'ETag': '"a80903d14ce564dd34939eecce1fd0d4"', 'Size': 2088, 'StorageClass': 'STANDARD', 'Owner': {'DisplayName': 'laura', 'ID': 'laura$laura'}}], 'Name': 'share-bucket', 'Prefix': '', 'MaxKeys': 1000, 'EncodingType': 'url'}
/home/ramartin/Downloads/example.txt File Download Complete.
[ramartin@tower-osd4 ~]$ s3cmd info s3://share-bucket -c laura.cfg
s3://share-bucket/ (bucket):
Location: default
Payer: BucketOwner
Expiration Rule: none
Policy: {"Version": "2012-10-17", "Statement": [{"Sid": "ListBucket", "Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::randy:user/randy"}, "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3::laura:share-bucket"}, {"Sid": "GetObject", "Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::randy:user/randy"}, "Action": ["s3:GetObject"], "Resource": "arn:aws:s3::laura:share-bucket/*"}]}
CORS: none
ACL: laura: FULL_CONTROL
Comments