Signature AWS v4 avec en-têtes de métadonnées personnalisées pour GCS
Problème
Lors de l'ajout d'en-têtes de métadonnées personnalisées (x-amz-meta-*) aux requêtes de l'API compatible S3 de GCS,
les requêtes échouent avec des erreurs de non-correspondance de signature. Les en-têtes doivent être correctement inclus
dans le calcul de la signature AWS v4.
Contexte / Conditions de déclenchement
- Utilisation de l'API XML compatible S3 de GCS (pas l'API JSON native)
- Ajout d'en-têtes
x-amz-meta-*pour les métadonnées d'objet - Erreur : « SignatureDoesNotMatch » ou 403 Forbidden après ajout d'en-têtes
- Les en-têtes fonctionnent avec les requêtes non signées mais échouent avec la signature AWS v4
- Identifiants HMAC configurés pour l'interopérabilité GCS
Solution
Les en-têtes personnalisés doivent être inclus à la fois dans les en-têtes canoniques et dans la liste des en-têtes signés, triés alphabétiquement :
1. En-têtes canoniques (triés alphabétiquement)
let canonical_headers = format!(
"host:{}\nx-amz-content-sha256:{}\nx-amz-date:{}\nx-amz-meta-owner:{}\n",
host, payload_hash, amz_date, owner // Note: ordre alphabétique !
);
2. Liste des en-têtes signés
let signed_headers = "host;x-amz-content-sha256;x-amz-date;x-amz-meta-owner";
3. Exemple complet (Rust)
fn sign_request_with_owner(
req: &mut Request,
config: &GCSConfig,
payload_hash: Option<String>,
owner: &str
) -> Result<()> {
// Définir les en-têtes d'abord
req.set_header("x-amz-date", &amz_date);
req.set_header("x-amz-content-sha256", &payload_hash);
req.set_header("x-amz-meta-owner", owner); // Métadonnée personnalisée
// En-têtes canoniques - DOIVENT être triés alphabétiquement
let signed_headers = "host;x-amz-content-sha256;x-amz-date;x-amz-meta-owner";
let canonical_headers = format!(
"host:{}\nx-amz-content-sha256:{}\nx-amz-date:{}\nx-amz-meta-owner:{}\n",
host, payload_hash, amz_date, owner
);
// Suite de la signature AWS v4...
let canonical_request = format!(
"{}\n{}\n{}\n{}\n{}\n{}",
method, uri, query, canonical_headers, signed_headers, payload_hash
);
// Signer et ajouter l'en-tête Authorization...
}
Vérification
Après le chargement, vérifiez que les métadonnées sont présentes :
gsutil stat gs://bucket/object-name
# Doit afficher :
# Metadata:
# owner: <value>
Ou via l'API S3 :
aws s3api head-object --bucket bucket --key object-name --endpoint-url https://storage.googleapis.com
Exemple
Avant (cassé) - en-tête non inclus dans la signature :
let signed_headers = "host;x-amz-content-sha256;x-amz-date"; // Manque x-amz-meta-owner
req.set_header("x-amz-meta-owner", owner); // En-tête ajouté mais non signé
// Résultat : 403 SignatureDoesNotMatch
Après (fonctionnant) - en-tête correctement signé :
let signed_headers = "host;x-amz-content-sha256;x-amz-date;x-amz-meta-owner"; // Inclus !
req.set_header("x-amz-meta-owner", owner);
// Inclure dans canonical_headers aussi
// Résultat : 200 OK, métadonnées visibles dans GCS
Notes
- Les noms d'en-têtes ne sont pas sensibles à la casse mais sont conventionnellement en minuscules dans la forme canonique
- La liste des en-têtes signés séparés par des points-virgules doit correspondre aux en-têtes canoniques séparés par des retours à la ligne
- Pour GCS, les métadonnées apparaissent comme
Metadata: key: valuedans la sortie de gsutil stat - Lors de l'utilisation de l'API JSON native de GCS, utilisez le champ d'objet
metadataà la place - Plusieurs en-têtes personnalisés peuvent être ajoutés - assurez-vous simplement qu'ils sont tous dans les listes canoniques/signées
Références
- AWS Signature Version 4: https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
- Interopérabilité GCS S3 : https://cloud.google.com/storage/docs/interoperability
- Métadonnées d'objet GCS : https://cloud.google.com/storage/docs/metadata