In dit artikel laten we zien hoe je in Linux een lege bucket (container) aanmaakt in een S3-compatibele Object Store met behulp van curl en openssl (en xxd), zonder extra tools zoals AWS CLI of s3cmd. We gebruiken AWS Signature v4 voor authenticatie; de benodigde handtekening wordt door een shellscript voor je opgebouwd.
Voor de stappen in deze handleiding heb je nodig:
- Een Object Store-project met een S3-token. Van de S3-token heb je de ‘Access Key’, ‘Secret Key’ en ‘Project ID’ nodig. Heb je nog geen S3-token aangemaakt (default optie bij het bestelproces), of wil je je S3-token gegevens achterhalen? Neem dan een kijkje in ons artikel S3-tokens beheren.
- Beschikbare tooling: bash, curl, openssl en xxd (doorgaans standaard aanwezig in moderne Linux-distributies).
Een Bucket/Container aanmaken
Optioneel kun je een env.sh-bestand gebruiken om je waarden centraal te definiëren:
# env.sh
export ACCESS_KEY="JOUW_ACCESS_KEY"
export SECRET_KEY="JOUW_SECRET_KEY"
export REGION="eu-west-1" # bijv. eu-west-1
export ENDPOINT="project-ID.objectstore.eu" # zonder https:// (alleen hostnaam)
export BUCKET="mijn-test-bucket" # alleen kleine letters/cijfers/koppeltekenMaak het bestand alleen voor jezelf leesbaar en laad de variabelen in je shell:
chmod 600 env.sh
source env.shMet onderstaand script maak je een (lege) bucket aan via de S3 API. Het script bouwt automatisch de vereiste AWS Signature v4-handtekening met behulp van openssl en verstuurt de PUT-request met curl. Als de bucket al bestaat, kan de provider afhankelijk van implementatie een 2xx of een 409 teruggeven (zie “Response en statuscodes”).
Met env.sh (aanbevolen)
Stap 1
Zorg dat je env.sh is aangemaakt (zie hoofdstuk “Benodigdheden”) en dat je de variabelen in je shell hebt geladen:
cd ~/s3-sigv4-demo
source env.sh
Stap 2
Maak het script create_bucket.sh met onderstaande inhoud. Het script gebruikt de variabelen uit env.sh:
#!/usr/bin/env bash
set -euo pipefail
# Verwacht dat ACCESS_KEY, SECRET_KEY, REGION, ENDPOINT en BUCKET zijn gezet
# via: source env.sh
: "${ACCESS_KEY:?Missing ACCESS_KEY}"
: "${SECRET_KEY:?Missing SECRET_KEY}"
: "${REGION:?Missing REGION}"
: "${ENDPOINT:?Missing ENDPOINT}"
: "${BUCKET:?Missing BUCKET}"
SERVICE="s3"
METHOD="PUT"
HOST="${ENDPOINT}"
CANONICAL_URI="/${BUCKET}"
REQUEST_URL="https://${HOST}/${BUCKET}"
# Timestamps
AMZ_DATE="$(date -u +"%Y%m%dT%H%M%SZ")"
DATE_STAMP="$(date -u +"%Y%m%d")"
# 1) Payload (XML) voor regio (niet-us-east-1)
# Let op: bij us-east-1 moet je LocationConstraint doorgaans weglaten.
if [[ "${REGION}" == "us-east-1" ]]; then
PAYLOAD=""
else
PAYLOAD="$(cat <<EOF
<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<LocationConstraint>${REGION}</LocationConstraint>
</CreateBucketConfiguration>
EOF
)"
fi
# 2) Hash van payload
PAYLOAD_HASH="$(printf "%s" "${PAYLOAD}" | openssl dgst -sha256 -binary | xxd -p -c 256)"
# 3) Canonical headers + signed headers
if [[ -n "${PAYLOAD}" ]]; then
CANONICAL_HEADERS="content-type:application/xml
host:${HOST}
x-amz-content-sha256:${PAYLOAD_HASH}
x-amz-date:${AMZ_DATE}
"
SIGNED_HEADERS="content-type;host;x-amz-content-sha256;x-amz-date"
else
CANONICAL_HEADERS="host:${HOST}
x-amz-content-sha256:${PAYLOAD_HASH}
x-amz-date:${AMZ_DATE}
"
SIGNED_HEADERS="host;x-amz-content-sha256;x-amz-date"
fi
CANONICAL_REQUEST="${METHOD}
${CANONICAL_URI}
${CANONICAL_HEADERS}
${SIGNED_HEADERS}
${PAYLOAD_HASH}"
CANONICAL_REQUEST_HASH="$(printf "%s" "${CANONICAL_REQUEST}" | openssl dgst -sha256 -binary | xxd -p -c 256)"
CREDENTIAL_SCOPE="${DATE_STAMP}/${REGION}/${SERVICE}/aws4_request"
STRING_TO_SIGN="AWS4-HMAC-SHA256
${AMZ_DATE}
${CREDENTIAL_SCOPE}
${CANONICAL_REQUEST_HASH}"
# Helpers: HMAC-SHA256 -> hex
hmac_sha256_hex_key() {
local key_ascii="$1"
local data="$2"
printf "%s" "${data}" | openssl dgst -sha256 -mac HMAC -macopt "key:${key_ascii}" -binary | xxd -p -c 256
}
hmac_sha256_hex_hexkey() {
local key_hex="$1"
local data="$2"
printf "%s" "${data}" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:${key_hex}" -binary | xxd -p -c 256
}
# 4) Signing key afleiden (AWS4 + secret)
K_SECRET="AWS4${SECRET_KEY}"
K_DATE="$(hmac_sha256_hex_key "${K_SECRET}" "${DATE_STAMP}")"
K_REGION="$(hmac_sha256_hex_hexkey "${K_DATE}" "${REGION}")"
K_SERVICE="$(hmac_sha256_hex_hexkey "${K_REGION}" "${SERVICE}")"
K_SIGNING="$(hmac_sha256_hex_hexkey "${K_SERVICE}" "aws4_request")"
SIGNATURE="$(hmac_sha256_hex_hexkey "${K_SIGNING}" "${STRING_TO_SIGN}")"
AUTH_HEADER="AWS4-HMAC-SHA256 Credential=${ACCESS_KEY}/${CREDENTIAL_SCOPE}, SignedHeaders=${SIGNED_HEADERS}, Signature=${SIGNATURE}"
# 5) Request doen
TMP_BODY="$(mktemp)"
if [[ -n "${PAYLOAD}" ]]; then
HTTP_CODE="$(curl -sS -o "${TMP_BODY}" -w "%{http_code}" \
-X "${METHOD}" "${REQUEST_URL}" \
-H "Host: ${HOST}" \
-H "x-amz-date: ${AMZ_DATE}" \
-H "x-amz-content-sha256: ${PAYLOAD_HASH}" \
-H "Content-Type: application/xml" \
-H "Authorization: ${AUTH_HEADER}" \
--data-binary "${PAYLOAD}")"
else
HTTP_CODE="$(curl -sS -o "${TMP_BODY}" -w "%{http_code}" \
-X "${METHOD}" "${REQUEST_URL}" \
-H "Host: ${HOST}" \
-H "x-amz-date: ${AMZ_DATE}" \
-H "x-amz-content-sha256: ${PAYLOAD_HASH}" \
-H "Authorization: ${AUTH_HEADER}" \
--data-binary "")"
fi
echo "HTTP ${HTTP_CODE}"
if [[ "${HTTP_CODE}" =~ ^2 ]]; then
echo "OK: bucket '${BUCKET}' is aangemaakt."
rm -f "${TMP_BODY}"
exit 0
fi
echo "FOUT: bucket niet aangemaakt. Response body:"
cat "${TMP_BODY}"
rm -f "${TMP_BODY}"
exit 1
Maak het script uitvoerbaar:
chmod +x create_bucket.sh
Stap 3
Voer het script uit om de bucket aan te maken:
source env.sh
./create_bucket.sh
Toelichting:
source env.sh laadt je access key, secret key, regio, endpoint en bucketnaam in de shell;
create_bucket.sh bouwt automatisch de AWS Signature v4-handtekening en stuurt een PUT-request naar https://ENDPOINT/BUCKET (path-style).
Zonder env.sh (waarden in script)
Stap 1
Wil je geen apart env.sh-bestand gebruiken, dan kun je de waarden direct bovenin het script zetten:
#!/usr/bin/env bash
set -euo pipefail
ACCESS_KEY="JOUW_ACCESS_KEY"
SECRET_KEY="JOUW_SECRET_KEY"
REGION="eu-west-1"
ENDPOINT="project-ID.objectstore.eu"
BUCKET="mijn-test-bucket"
# <rest van create_bucket.sh blijft gelijk>
Voer het script daarna direct uit:
chmod +x create_bucket.sh
./create_bucket.sh
Response en statuscodes
- 200 OK (soms 204 No Content): de bucket is aangemaakt.
- 409 Conflict: de bucket bestaat al. Afhankelijk van implementatie kan dit “al van jou” of “naam al in gebruik” betekenen.
- 403 Forbidden / AccessDenied / SignatureDoesNotMatch: controleer access key, secret key, endpoint, regio en systeemtijd (UTC/NTP).
- 400 Bad Request / InvalidBucketName: de bucketnaam voldoet niet aan de naamregels (alleen kleine letters, cijfers en koppeltekens).
- 400 Bad Request / InvalidLocationConstraint (of vergelijkbaar): de regio/LocationConstraint past niet bij het endpoint (of de provider verwacht een andere regio).
Gebruik dezelfde access key en secret key ook in andere S3-requests. De handtekening volgt hetzelfde patroon: de payload wordt gehasht, er wordt een Canonical Request en String-To-Sign gemaakt, en die worden met AWS Signature v4 ondertekend met je secret key.