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