Skip to content

Instantly share code, notes, and snippets.

@ZyqGitHub1
Created March 9, 2023 02:19
Show Gist options
  • Save ZyqGitHub1/9d26c70e34d295b51b0506afd31e7d91 to your computer and use it in GitHub Desktop.
Save ZyqGitHub1/9d26c70e34d295b51b0506afd31e7d91 to your computer and use it in GitHub Desktop.

Revisions

  1. @manics manics revised this gist Apr 14, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions list-or-get-object.py
    Original file line number Diff line number Diff line change
    @@ -9,6 +9,8 @@
    s3path = sys.argv[1]
    s = sys.stdin.read()
    connection_args = json.loads(s)
    connection_args.pop('expiration', None)

    session = boto3.client('s3', **connection_args)

    bucket, prefix = s3path.split('/', 1)
  2. @manics manics created this gist Apr 7, 2020.
    84 changes: 84 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,84 @@
    # MinIO Security Token Service (STS)

    This is an example of setting up a Minio server with the [Security Token Service (STS)](https://github.com/minio/minio/blob/RELEASE.2020-04-04T05-39-31Z/docs/sts/README.md) and [AssumeRole](https://github.com/minio/minio/blob/RELEASE.2020-04-04T05-39-31Z/docs/sts/assume-role.md) for temporary session tokens.

    You must [create a new Minio user](https://docs.min.io/docs/minio-multi-user-quickstart-guide.html) to use STS, the default Minio access/secret won't work.
    The new user must have access to the objects you will be creating sessions for, the permissions of the created session are the intersection of the permissions of the STS user and the inline permissions requested when the session is created

    Tested on `2020-04-06` with the current version of Minio.
    This is probably `RELEASE.2020-04-04T05-39-31Z`, though if using Homebrew on Mac OSX `minio --version` outputs `DEVELOPMENT.GOGET` so who knows.


    ## Example: start the Minio server and configure the client

    This will serve your home directory:

    export MINIO_ACCESS_KEY=minio
    export MINIO_SECRET_KEY=minio123
    minio server ~/

    Create a `mc` client config called `local`:

    mc config host add local http://localhost:9000 minio minio123


    ## Example: create a user `stsadmin` with full read-only access to all buckets and objects

    mc admin user add local stsadmin stsadmin-secret
    mc admin policy add local readall s3-policy-readall.json
    mc admin policy set local readall user=stsadmin


    ## Example: create a token

    Run [`create-token.py`](create-token.py) to create a session token valid for 15 minutes for a bucket and optional prefix.
    The token along with connection credentials will be printed to stdout.
    This example will give access to keys matching the prefix `media/*` in bucket `tmp`:

    TOKEN=$(./create-token.py --endpoint http://localhost:9000 --accesskey stsadmin --secretkey stsadmin-secret --bucket tmp --prefix 'media/*')
    echo "$TOKEN"


    ## Example: test the token

    Test the token by using [`list-or-get-object.py`](list-or-get-object.py).
    This script reads the connection credentials output by the previous script on stdin.

    List objects (trailing `/`):

    $ echo "$TOKEN" | ./list-or-get-object.py tmp/media/

    Listing tmp/media/
    - ETag: '"00000000000000000000000000000000-1"'
    Key: media/hello.txt
    LastModified: 2020-04-06 18:29:55.394000+00:00
    Owner:
    DisplayName: ''
    ID: 02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4
    Size: 6
    StorageClass: STANDARD

    Get an object (no trailing `/`):

    $ echo "$TOKEN" | ./list-or-get-object.py tmp/media/hello.txt

    Getting tmp/media/hello.txt
    b'hello\n'

    List a disallowed path:

    $ echo "$TOKEN" | ./list-or-get-object.py tmp/other/

    Listing tmp/other/
    Traceback (most recent call last):
    ...
    botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the ListObjects operation: Access Denied.

    Expired token:

    $ echo "$TOKEN" | ./list-or-get-object.py tmp/media/

    Listing tmp/media/
    Traceback (most recent call last):
    ...
    botocore.exceptions.ClientError: An error occurred (InvalidAccessKeyId) when calling the ListObjects operation: The access key ID you provided does not exist in our records.
    81 changes: 81 additions & 0 deletions create-token.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,81 @@
    #!/usr/bin/env python

    # https://github.com/minio/minio/blob/master/docs/sts/assume-role.md
    # https://docs.minio.io/docs/how-to-use-aws-sdk-for-python-with-minio-server
    # https://docs.min.io/docs/minio-select-api-quickstart-guide.html

    import argparse
    import boto3
    import json

    parser = argparse.ArgumentParser(
    'create-token.py', description=(
    'STS token creator. Create read-only temporary access tokens for S3'))
    parser.add_argument('--endpoint', help='S3 server endpoint', required=True)
    parser.add_argument('--region', help='S3 region', default="")
    parser.add_argument(
    '--accesskey', help='Access key ID for the STS admin user', required=True)
    parser.add_argument(
    '--secretkey', help='Secret access key ID for STS admin user',
    required=True)
    parser.add_argument('--bucket', help='S3 bucket', required=True)
    parser.add_argument(
    '--prefix', default='*', help=(
    'Prefix inside bucket, for example * (default), '
    'prefix/*, prefix/file.name'))

    args = parser.parse_args()

    sts_admin = boto3.client(
    'sts',
    endpoint_url=args.endpoint,
    aws_access_key_id=args.accesskey,
    aws_secret_access_key=args.secretkey,
    region_name=args.region)

    bucket_name = args.bucket
    prefix = args.prefix

    # Access policies
    # https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session
    # https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html
    # https://aws.amazon.com/premiumsupport/knowledge-center/s3-folder-user-access/
    policy = {
    "Version": "2012-10-17",
    "Statement": [
    {
    "Sid": "ListObjectsInBucket",
    "Effect": "Allow",
    "Action": "s3:ListBucket",
    "Resource": ["arn:aws:s3:::{}".format(bucket_name)],
    "Condition": {
    "StringLike": { "s3:prefix": [prefix]}
    },
    },
    {
    "Sid": "GetObjectsInBucket",
    "Effect": "Allow",
    "Action": "s3:GetObject",
    "Resource": ["arn:aws:s3:::{}/{}".format(bucket_name, prefix)],
    },
    ],
    }

    # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sts.html#STS.Client.assume_role
    response = sts_admin.assume_role(
    RoleArn='arn:x:ignored:by:minio:',
    RoleSessionName='ignored-by-minio',
    # PolicyArns=[{'arn': 'string'}],
    Policy=json.dumps(policy),
    DurationSeconds=900,
    )

    boto3_client_args = dict(
    endpoint_url=args.endpoint,
    aws_access_key_id=response['Credentials']['AccessKeyId'],
    aws_secret_access_key=response['Credentials']['SecretAccessKey'],
    aws_session_token=response['Credentials']['SessionToken'],
    region_name=args.region,
    )
    # E.g. s3 = boto3.client('s3', **boto3_client_args)
    print(json.dumps(boto3_client_args))
    22 changes: 22 additions & 0 deletions list-or-get-object.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    #!/usr/bin/env python

    import boto3
    import json
    import sys
    import yaml


    s3path = sys.argv[1]
    s = sys.stdin.read()
    connection_args = json.loads(s)
    session = boto3.client('s3', **connection_args)

    bucket, prefix = s3path.split('/', 1)
    if s3path.endswith('/'):
    print('Listing {}'.format(s3path))
    r = session.list_objects(Bucket=bucket, Prefix=prefix)
    print(yaml.dump(r['Contents']))
    else:
    print('Getting {}'.format(s3path))
    r = session.get_object(Bucket=bucket, Key=prefix)
    print(r['Body'].read())
    16 changes: 16 additions & 0 deletions s3-policy-readall.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,16 @@
    {
    "Version": "2012-10-17",
    "Statement": [
    {
    "Action": [
    "s3:GetObject",
    "s3:ListBucket"
    ],
    "Effect": "Allow",
    "Resource": [
    "arn:aws:s3:::*"
    ],
    "Sid": ""
    }
    ]
    }