Enviando arquivos para S3 usando script Shell

Resumo Executivo

#!/usr/bin/env bash
# Script para upload em S3/Minio via API REST

access_key="AKIAIOSFODNN7EXAMPLE"
secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
session_token="TokenDeSessaoExemplo"

bucket_origem="meu-bucket"
arquivo_tar="dados.log"

host="play.min.io"
regiao="us-east-1"
servico="s3"

metodo="PUT"
caminho="/${bucket_origem}/${arquivo_tar}"
query_string=""
content_type="application/octet-stream"
content_sha256="UNSIGNED-PAYLOAD"

amz_date=`date -u +%Y%m%dT%H%M%SZ`
signer_date=`echo ${amz_date} | cut -c1-8`
aws4_request="aws4_request"
escopo="$signer_date/$regiao/$servico/$aws4_request"

content_length=`wc -c $arquivo_tar | awk '{print $1}'`
content_md5=`openssl dgst -md5 -binary $arquivo_tar | base64`

date_key=`echo -en ${signer_date} | openssl dgst -sha256 -binary -hmac "AWS4${secret_key}"`
date_region_key=`echo -en $regiao | openssl dgst -sha256 -binary -hmac "${date_key}"`
date_region_service_key=`echo -en $servico | openssl dgst -sha256 -binary -hmac "${date_region_key}"`
signing_key=`echo -en $aws4_request | openssl dgst -sha256 -binary -hmac "${date_region_service_key}"`

headers="content-length:$content_length\ncontent-md5:$content_md5\ncontent-type:$content_type\nhost:$host\nx-amz-content-sha256:$content_sha256\nx-amz-date:$amz_date\nx-amz-security-token:$session_token"
signed_headers="content-length;content-md5;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-security-token"
requisicao_canonica="$metodo\n$caminho\n$query_string\n$headers\n\n$signed_headers\n$content_sha256"
requisicao_canonica_hash=`echo -en "$requisicao_canonica" |openssl sha256`
string_to_sign="AWS4-HMAC-SHA256\n$amz_date\n$escopo\n$requisicao_canonica_hash"

printf "string_to_sign: $string_to_sign"

signature=`echo -en "${string_to_sign}" | openssl dgst -sha256 -hmac "${signing_key}"`

echo
echo $signature

curl -v -X ${metodo} -T "${arquivo_tar}" \
          -H "Host: $host" \
          -H "Accept-Encoding: identity" \
          -H "Content-Length: ${content_length}" \
          -H "Content-MD5: ${content_md5}" \
          -H "x-amz-content-sha256: ${content_sha256}" \
          -H "X-Amz-Security-Token: ${session_token}" \
          -H "x-amz-date: ${amz_date}" \
          -H "Content-Type: ${content_type}" \
          -H "Authorization: AWS4-HMAC-SHA256 Credential=${access_key}/${escopo}, SignedHeaders=$signed_headers, Signature=${signature}" \
          https://$host${caminho}

Contexto

Desenvolvi uma necessidade de implementar upload de logs em um aplicativo Android, inicialmente avaliando o Minio como solução de armazenamento. Após dificuldades com a integração direta no Android Studio, percebi que a solução seria implementar uma requisição HTTP PUT personalizada com os parâmetros de assinatura necessários.

A abordagem consistiu em utilizar a biblioteca okhttp para construir a requisição. No entanto, como minha experiência com Java é limitada, optei por criar um protótipo em Python para analisar o protocolo de comunicação.

regiao = "us-east-1"
access_key = "AKIAIOSFODNN7EXAMPLE"
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
session_token = "TokenDeSessaoExemplo"

client = Minio(
    "play.min.io",
    access_key=access_key,
    secret_key=secret_key,
    session_token=session_token,
    region=regiao,
    secure=True,
    http_client=urllib3.ProxyManager(
        "http://localhost:8080/",
        cert_reqs="CERT_REQUIRED",
        ca_certs='/caminho/para/certificado.pem',
    ),
)

client.fput_object(
    "meu-bucket", "dados.log", "/caminho/local/dados.log",
)

Após interceptar as requisições e analisar o código do signer.py da biblioteca minio-py, consegui compreender os detalhes do protocolo de assinatura AWS Signature Version 4. Com isso, foi possível construir manualmente a requisição curl e posteriormente adaptar para okhttp.

Referências

  • Upload to minio via curl
  • Amazon S3 REST API with curl

Tags: aws-s3 MinIO shell-script Bash aws-signature

Publicado em 6-17 05:06