Skip to content

Instantly share code, notes, and snippets.

@kennwhite
Last active May 17, 2022 12:32
Show Gist options
  • Save kennwhite/223615b1b34b4f6c7d59 to your computer and use it in GitHub Desktop.
Save kennwhite/223615b1b34b4f6c7d59 to your computer and use it in GitHub Desktop.
Minimalist, bash-only utility script to push a file to S3 bucket. AWS ID & Key can be passed via environment or on command line. Only dependency is openssl tools (usually default). Name is an homage to Joe Stump.
#!/usr/bin/env bash
#
# Upload a file to the Amazon S3 service
# Usage:
# neckbeard-push FILE S3_BUCKET [-a ACL_POLICY] [-i AWS_ACCESS_ID] [-k AWS_SECRET_KEY] [-d debug] [-l write verbose session log]
# Note: If option -l is requested, curl_session.log contains SSL handshake *and* plaintext AWS keys
#
# Ex 1: neckbeard-push foo.log my-bucket -a public-read (with env vars: $AWS_ACCESS_ID & $AWS_SECRET_KEY)
# Ex 2: neckbeard-push foo.log my-bucket -a private -i AKIXXXXX -k aBcDeFgHxxx -d
#
# If credentials not supplied, depends on being set via env (eg: export AWS_ACCESS_ID="AKIXXXXXXX"):
# $AWS_ACCESS_ID
# $AWS_SECRET_KEY
#
# Output: URL of the newly uploaded file on success, HTTP error code (typically 403) on failure
# Returns: 0 on success 1 on failure
# Exit on error
set -e
# See for additional endpoints: http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
s3_endpoint=s3-external-1.amazonaws.com # For geo-aware: s3.amazonaws.com
script="${0##*/}"
# Expects: authorize $string_to_sign $secret_key $access_id
authorize() {
local signature=$( printf "$1" | hmac_sha1 "$2" | base64 )
printf "AWS $3:$signature"
}
# Expects: hmac_sha1 $hmac_key, with string piped
hmac_sha1() {
openssl dgst -binary -sha1 -hmac "$1"
}
base64() {
openssl enc -base64
}
bin_md5() {
openssl dgst -binary -md5
}
# Parse parameters
[ $# -eq 0 ] && printf "ERROR: No arguments passed. Use: $script -h for help. \n" && exit 1
file="$1"
bucket="$2"
usage="USAGE: $script FILE S3_BUCKET \
[-a ACL_POLICY <private>] [-i AWS_ACCESS_ID] [-k AWS_SECRET_KEY] [-d debug] [-l write verbose session log] \n\
Ex: $script kitty.jpg mybucket -a public-read -d \n"
options=$@; args=($options)
index=0; for arg in $options ; do (( index++ ))
case $arg in
-h) printf "$usage" && exit 1 ;;
-d) debug=1 ;;
-l) trace="--trace curl_session.log" && printf "NOTICE: *Plain text* session log written: curl_session.log \n" ;;
-a) acl="${args[index]}" ;;
-i) access_id="${args[index]}" ;;
-k) secret_key="${args[index]}" ;;
-?) printf "WARNING: Unknown parameter '$arg' ${args[index]} \n" ;;
esac
done
# If not provided (or blank), default to constants or env variables
acl=${acl:-"private"}
trace=${trace:-""}
access_id=${access_id:-$AWS_ACCESS_ID}
secret_key=${secret_key:-$AWS_SECRET_KEY}
object_name="${file##*/}"
content_md5="$( bin_md5 < "$file" | base64 )"
content_type='binary/octet-stream'
# Date/time format in POSIX (C) locale and style
utc_date="$( LC_TIME=C && date '+%a, %d %h %Y %T %z' )"
url="https://$bucket.$s3_endpoint/$object_name"
string_to_sign="PUT\n$content_md5\n$content_type\n$utc_date\nx-amz-acl:$acl\n/$bucket/$object_name"
authorization=$( authorize "$string_to_sign" "$secret_key" "$access_id" )
[ $debug ] && printf \
'DEBUG:\n File\t%s\n Bucket\t%s\n Id\t%s\n Key\t%s\n ACL\t%s\n Date\t%s\n Req\t%s\n Auth\t%s\n URL\t%s\n\n' \
"$file" "$bucket" "$access_id" "$secret_key" "$acl" "$utc_date" "$string_to_sign" "$authorization" "$url"
# Ignore .curlrc, silent, show errors only on fail, no progress bar, upload $file with header auth
curl -qsSf -T "$file" $trace \
-H "Authorization: $authorization" \
-H "x-amz-acl: $acl" \
-H "Date: $utc_date" \
-H "Content-MD5: $content_md5" \
-H "Content-Type: $content_type" \
"$url" && printf "$url \n" && exit 0 || \
printf "Error: Failed to PUT: $file on requested URL: $url (use -d to debug)\n" && exit 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment