MintyOffline - Offline Minting of Shared Access Signatures (SAS) URLs & Tokens for Azure Storage Accounts (e.g. Blob Storage) - Alpha v0.0.3
import os | |
from argparse import ArgumentParser | |
import logging | |
import sys | |
#import os | |
import json | |
import base64 | |
import hmac | |
import hashlib | |
import urllib | |
from datetime import datetime, timedelta | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
__author__ = '@TweekFawkes' | |
__website__ = 'Stage2Sec.com' | |
__blog__ = 'https://Stage2Sec.com/blog/' | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
''' | |
--- MintyOffline - Offline Minting of Shared Access Signatures (SAS) URLs & Tokens for Azure Storage Accounts (e.g. Blob Storage) - Alpha v0.0.3 --- | |
Offline Minting of Shared Access Signatures (SAS) URLs & Tokens for Azure Storage Accounts (e.g. Blob Storage). | |
Useful for persisting access to an Azure Storage Account once the primary or secondary key have been collected. | |
What is a shared access signature (SAS)? | |
To Quote Microsoft: | |
- "A shared access signature (SAS) provides you with a way to grant limited access to objects in your storage account to other clients, without exposing your account key." | |
- "A shared access signature provides delegated access to resources in your storage account. With a SAS, you can grant clients access to resources in your storage account, without sharing your account keys. This is the key point of using shared access signatures in your applications--a SAS is a secure way to share your storage resources without compromising your account keys." | |
See references for more detials. | |
--- Example Usage --- | |
... look at the help ... | |
$ mintyOffline-v0_0_3.py -h | |
... create an SAS URL/Token for the Storage Account named "bcod030" with the container named "container030" | |
$ mintyOffline-v0_0_3.py bcod030 container030 rl 1 "HdBVPwDkMQTdgYnQhOnw38jAcRTjJAnnsVtXdfucVr6P89ysjgqq2VuwtmyA4Oeme/jF/WXv1yncNhXGINNWOA==" | |
... | |
CRITICAL:root:[+] MintyOffline - Alpha v0.0.3 | |
CRITICAL:root:[+] SAS URL: https://bcod030.blob.core.windows.net/container030?sig=RW7EarpHliaORD21iQkTHp1XeOQFHse47IDqiIRY4yk%3D&srt=co&ss=b&spr=https&sp=rwdalcup&sv=2016-05-31&se=2018-05-10T20%3A46%3A50Z&st=2018-05-10T19%3A41%3A50Z | |
CRITICAL:root:[+] SAS Token: sig=RW7EarpHliaORD21iQkTHp1XeOQFHse47IDqiIRY4yk%3D&srt=co&ss=b&spr=https&sp=rwdalcup&sv=2016-05-31&se=2018-05-10T20%3A46%3A50Z&st=2018-05-10T19%3A41%3A50Z | |
... | |
... browse to blob (e.g. blob030.txt) that is protected using the SAS Token ... | |
https://bcod030.blob.core.windows.net/container030/blob030.txt?sig=RW7EarpHliaORD21iQkTHp1XeOQFHse47IDqiIRY4yk%3D&srt=co&ss=b&spr=https&sp=rwdalcup&sv=2016-05-31&se=2018-05-10T20%3A46%3A50Z&st=2018-05-10T19%3A41%3A50Z | |
--- Permissions --- | |
The following values can be used for permissions: | |
"a" (Add), "r" (Read), "w" (Write), "d" (Delete), "l" (List) | |
Concatenate multiple permissions, such as "rwa" = Read, Write, Add | |
Permissions for Storage: | |
"rl" = Read, List -> default | |
"rwa" = Read, Write, Add -> common | |
"rwdalcup" = Read, Write, Delete, Add, List, Create, Update, Process -> all | |
--- Setup on Ubuntu 14.04 x64 --- | |
apt-get update | |
apt-get -y install python | |
... | |
$ python | |
Python 2.7.6 (default, Oct 26 2016, 20:30:19) | |
... | |
>>> exit() | |
--- References --- | |
- Using shared access signatures (SAS) | |
-- https://docs.microsoft.com/en-us/azure/storage/common/storage-dotnet-shared-access-signature-part-1 | |
-- https://docs.microsoft.com/en-us/azure/storage/blobs/storage-dotnet-shared-access-signature-part-2 | |
- How do you generate the signature for an Azure Blob storage SAS token in Python? | |
-- https://stackoverflow.com/questions/41285755/how-do-you-generate-the-signature-for-an-azure-blob-storage-sas-token-in-python?rq=1 | |
- An HTTP trigger Azure Function that returns a SAS token for Azure Storage for the specified container and blob name. | |
-- https://github.com/yokawasa/azure-functions-python-samples/blob/master/blob-sas-token-generator/function/run.py | |
- Generate SAS token | |
-- https://docs.microsoft.com/en-us/rest/api/eventhub/generate-sas-token | |
''' | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
# Automatically Defined Variables | |
sScriptName = os.path.basename(__file__) | |
sAppName = 'MintyOffline' | |
sVersion = 'Alpha v0.0.3' | |
sAzureStorageApiVersion = '2016-05-31' | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
# Get the Arguments | |
parser = ArgumentParser(add_help=True) | |
parser.add_argument('storage_account', | |
action="store", | |
help="[required] storage account to create SAS token for e.g.: bcod030") | |
parser.add_argument('container_name', | |
action="store", | |
help="[required] container to create SAS token for e.g.: container030") | |
parser.add_argument('permission', | |
action="store", | |
help="[required] permission to set for SAS token e.g.: rl") | |
parser.add_argument('token_ttl', | |
action="store", | |
help="[required] ttl to set for SAS token e.g.: 1") | |
parser.add_argument('storage_key', | |
action="store", | |
help="[required] storage key to use to create SAS token e.g.: HdB...WOA==") | |
args = parser.parse_args() | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
# storage_account = "bcod030" | |
sStorageAccountName = str(args.storage_account).strip() | |
logging.critical("[-] sStorageAccountName: " + sStorageAccountName) | |
#container_name = "container030" | |
sContainerName = str(args.container_name).strip() | |
logging.critical("[-] sContainerName: " + sContainerName) | |
#permission = "rwa" # d l | |
sPermission = str(args.permission).strip() | |
logging.critical("[-] sPermission: " + sPermission) | |
#token_ttl = int(1) | |
sTokenTtl = str(args.token_ttl).strip() | |
logging.critical("[-] sTokenTtl: " + sTokenTtl) | |
iTokenTtl = int(sTokenTtl) | |
logging.critical("[-] iTokenTtl: " + str(iTokenTtl)) | |
# storage_key = "HdBVPwDkMQTdgYnQhOnw38jAcRTjJAnnsVtXdfucVr6P89ysjgqq2VuwtmyA4Oeme/jF/WXv1yncNhXGINNWOA==" | |
sStorageKey = str(args.storage_key).strip() | |
logging.critical("[-] sStorageKey: " + sStorageKey) | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
logging.critical(""" | |
__ ____ __ ____ ___________ | |
/ |/ (_)___ / /___ __/ __ \/ __/ __/ (_)___ ___ | |
/ /|_/ / / __ \/ __/ / / / / / / /_/ /_/ / / __ \/ _ \ | |
/ / / / / / / / /_/ /_/ / /_/ / __/ __/ / / / / / __/ | |
/_/ /_/_/_/ /_/\__/\__, /\____/_/ /_/ /_/_/_/ /_/\___/ | |
/____/ | |
""") | |
logging.critical("[+] " + sAppName + " - " + sVersion) | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
def generate_sas_token (storage_account, storage_key, permission, token_ttl, container_name, blob_name = None ): | |
sp = permission | |
# Set start time to five minutes ago to avoid clock skew. | |
st= str((datetime.utcnow() - timedelta(minutes=5) ).strftime("%Y-%m-%dT%H:%M:%SZ")) | |
se= str((datetime.utcnow() + timedelta(hours=token_ttl)).strftime("%Y-%m-%dT%H:%M:%SZ")) | |
srt = 'o' if blob_name else 'co' | |
# Construct input value | |
inputvalue = "{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}\n{8}\n".format( | |
storage_account, # 0. account name | |
sp, # 1. signed permission (sp) | |
'b', # 2. signed service (ss) | |
srt, # 3. signed resource type (srt) | |
st, # 4. signed start time (st) | |
se, # 5. signed expire time (se) | |
'', # 6. signed ip | |
'https', # 7. signed protocol | |
sAzureStorageApiVersion) # 8. signed version | |
# Create base64 encoded signature | |
hash =hmac.new(base64.b64decode(storage_key),inputvalue,hashlib.sha256).digest() | |
sig = base64.b64encode(hash) | |
querystring = { | |
'sv': sAzureStorageApiVersion, | |
'ss': 'b', | |
'srt': srt, | |
'sp': sp, | |
'se': se, | |
'st': st, | |
'spr': 'https', | |
'sig': sig, | |
} | |
sastoken = urllib.urlencode(querystring) | |
sas_url = None | |
if blob_name: | |
sas_url = "https://{0}.blob.core.windows.net/{1}/{2}?{3}".format( | |
storage_account, | |
container_name, | |
blob_name, | |
sastoken) | |
else: | |
sas_url = "https://{0}.blob.core.windows.net/{1}?{2}".format( | |
storage_account, | |
container_name, | |
sastoken) | |
return { | |
'token': sastoken, | |
'url' : sas_url | |
} | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
theGoodStuff = generate_sas_token (sStorageAccountName, sStorageKey, sPermission, iTokenTtl, sContainerName, blob_name = None ) | |
logging.critical("[+] SAS URL: " + theGoodStuff['url'] ) | |
logging.critical("[+] SAS Token: " + theGoodStuff['token'] ) | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment