Cart

    Sorry, we could not find any results for your search querry.

    Creating an Object Store bucket (container) via the S3 API on Linux

    In this article we show you how to create an empty bucket (container) in an S3-compatible Object Store on Linux using curl and openssl (and xxd), without additional tools such as AWS CLI or s3cmd. We use AWS Signature v4 for authentication; the required signature is generated for you by a shell script.

    For the steps in this guide you need: 

    • An Object Store project with an S3 token. From the S3 token you need the ‘Access Key’, ‘Secret Key’ and ‘Project ID’. If you have not yet created an S3 token (default option during the ordering process) or want to retrieve your S3 token details, take a look at our article Managing S3 tokens.
    • Available tooling: bash, curl, openssl and xxd (usually present by default on modern Linux distributions).

     

    Creating a bucket/container

     

    Optionally, you can use an env.sh file to define your values centrally:

    # env.sh
    export ACCESS_KEY="YOUR_ACCESS_KEY"
    export SECRET_KEY="YOUR_SECRET_KEY"
    
    export REGION="eu-west-1"               # e.g. eu-west-1
    export ENDPOINT="project-ID.objectstore.eu"  # without https:// (host name only)
    export BUCKET="my-test-bucket"          # lowercase letters/digits/hyphen only

    Make the file readable only for yourself and load the variables into your shell:

    chmod 600 env.sh
    source env.sh

    With the script below you create an (empty) bucket via the S3 API. The script automatically builds the required AWS Signature v4 signature using openssl and sends the PUT request with curl. If the bucket already exists, the provider may, depending on the implementation, return either a 2xx or a 409 (see “Response and status codes”).

    With env.sh (recommended)

    Step 1

    Make sure env.sh has been created (see the “Requirements” section) and that you have loaded the variables into your shell:

    cd ~/s3-sigv4-demo
    source env.sh

    Step 2

    Create the script create_bucket.sh with the contents below. The script uses the variables from env.sh:

    #!/usr/bin/env bash
    set -euo pipefail
    
    # Expects ACCESS_KEY, SECRET_KEY, REGION, ENDPOINT and BUCKET
    # to be set 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) for region (non-us-east-1)
    # Note: for us-east-1 you usually need to omit LocationConstraint.
    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 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) Derive signing key (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) Send request
    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}' has been created."
      rm -f "${TMP_BODY}"
      exit 0
    fi
    
    echo "ERROR: bucket not created. Response body:"
    cat "${TMP_BODY}"
    rm -f "${TMP_BODY}"
    exit 1

    Make the script executable:

    chmod +x create_bucket.sh

    Step 3

    Run the script to create the bucket:

    source env.sh
    ./create_bucket.sh

    Explanation:
    source env.sh loads your access key, secret key, region, endpoint and bucket name into the shell;
    create_bucket.sh automatically builds the AWS Signature v4 signature and sends a PUT request to https://ENDPOINT/BUCKET (path style).

     
     

    Without env.sh (values in the script)

    Step 1

    If you don’t want to use a separate env.sh file, you can put the values directly at the top of the script:

    #!/usr/bin/env bash
    set -euo pipefail
    
    ACCESS_KEY="YOUR_ACCESS_KEY"
    SECRET_KEY="YOUR_SECRET_KEY"
    REGION="eu-west-1"
    ENDPOINT="project-ID.objectstore.eu"
    BUCKET="my-test-bucket"
    
    # <rest of create_bucket.sh remains the same>

    Then run the script directly:

    chmod +x create_bucket.sh
    ./create_bucket.sh
     
     

     

    Response and status codes

     

    • 200 OK (sometimes 204 No Content): the bucket has been created.
    • 409 Conflict: the bucket already exists. Depending on the implementation this can mean “already yours” or “name already in use”.
    • 403 Forbidden / AccessDenied / SignatureDoesNotMatch: check access key, secret key, endpoint, region and system time (UTC/NTP).
    • 400 Bad Request / InvalidBucketName: the bucket name does not comply with the naming rules (lowercase letters, digits and hyphens only).
    • 400 Bad Request / InvalidLocationConstraint (or similar): the region/LocationConstraint does not match the endpoint (or the provider expects a different region).

     

    Use the same access key and secret key for other S3 requests as well. The signature follows the same pattern: the payload is hashed, a canonical request and string-to-sign are created, and they are signed with AWS Signature v4 using your secret key.

    Need help?

    Receive personal support from our supporters

    Contact us