Vai al contenuto

Bucket Policies e segregazione degli accessi

Per impostazione predefinita, ogni credential EC2 di un progetto ha accesso completo a ogni bucket di quel progetto. È comodo: un nuovo bucket funziona con i credential esistenti senza che nessuno debba intervenire sui permessi.

Per alcuni bucket questo comportamento predefinito è troppo ampio, ad esempio un bucket in cui esattamente un'applicazione può scrivere e da cui esattamente un consumatore può leggere. Invece di spostare un tale bucket in un progetto dedicato, potete invertire il comportamento predefinito con una bucket policy: negare tutto a tutti, tranne ai credential che portano uno dei due ruoli denominati. Tutti i bucket e tutti i credential restano nello stesso progetto.

Quali credential una bucket policy può indirizzare

Una bucket policy indirizza ruoli, non singole chiavi di accesso. Il ruolo di un credential viene impostato nel portale al momento della creazione del credential (Credential role), vedere Create EC2 Credentials.

Funzionamento dell'accesso predefinito

Argomento Comportamento
Ambito Un progetto è il contenitore: tutti i bucket e tutti i credential EC2 al suo interno.
Predefinito Senza una bucket policy, ogni credential del progetto può leggere e scrivere in ogni bucket del progetto, sia legato al progetto sia legato all'utente.
Restrizione Poiché l'accesso è consentito per impostazione predefinita, la concessione di un diritto non cambia nulla. Solo un Deny esplicito in una bucket policy restringe l'accesso.
Granularità Una policy per bucket, per regione. Lo stesso bucket nell'altra regione necessita della propria policy, vedere Multi Region.
Visibilità La segregazione blocca l'accesso, non la visibilità: aws s3 ls senza bucket elenca comunque il nome del bucket.

Prerequisiti

  • Un progetto con il profilo di quota object-storage e almeno un bucket, vedere Configuration.
  • Un'AWS CLI configurata per l'endpoint e la regione S3, vedere AWS CLI & IAM Features.
  • Due credential EC2 nel progetto che portano un ruolo, vedere il passo 1.
  • L'ID del progetto, vedere il passo 2.

Passo 1: creare credential con un ruolo

Nel portale: Object Storage → S3 → selezionate il progetto → Credentials → + Create Credentials, poi compilate Credential role:

Credential Credential role
Accesso in scrittura <bucket>-readwrite
Accesso in lettura <bucket>-readonly

Il nome del ruolo costituisce il vostro ambito di autorizzazione

  • Il nome del ruolo è testo libero e appartiene al credential. Un nome diverso significa un nuovo credential.
  • Per la policy, ogni credential del progetto che porta lo stesso nome di ruolo è intercambiabile, compresi i credential legati all'utente appartenenti ad altri utenti.
  • Non usate nomi generici come readwrite se bucket diversi richiedono accessi diversi: un solo credential aprirebbe allora ogni bucket la cui policy elenca quel ruolo.
  • Annotate i nomi esatti. Vi serviranno di nuovo per modificare o revocare la policy.

Passo 2: determinare l'ID del progetto

La policy fa riferimento ai credential come ARN:

arn:aws:iam::<PROJECT_ID>:role/<ROLE>

<PROJECT_ID> è l'ID del vostro progetto, non il suo nome. Potete leggerlo con il client OpenStack (openstack ec2 credentials list lo mostra nella colonna Project ID, vedere EC2 Credentials), oppure ricavarlo dalla panoramica del progetto nel portale.

Passo 3: scrivere la policy

Definite i vostri valori come variabili shell e lasciate che sia la shell a inserirli. Non modificate gli ARN a mano: un solo carattere errato in un ARN nega l'accesso a tutti, compreso il portale.

export bucket=my-bucket
export project=<PROJECT_ID>

# Note the unquoted EOF: ${bucket} and ${project} must be expanded.
cat <<EOF > bucket-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyAllWithoutRole",
      "Effect": "Deny",
      "NotPrincipal": {
        "AWS": [
          "arn:aws:iam::${project}:role/${bucket}-readonly",
          "arn:aws:iam::${project}:role/${bucket}-readwrite"
        ]
      },
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::${bucket}",
        "arn:aws:s3:::${bucket}/*"
      ]
    },
    {
      "Sid": "DenyNonReadActionsForReadonlyRole",
      "Effect": "Deny",
      "Principal": {
        "AWS": "arn:aws:iam::${project}:role/${bucket}-readonly"
      },
      "NotAction": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:ListBucket",
        "s3:ListBucketVersions",
        "s3:GetBucketLocation"
      ],
      "Resource": [
        "arn:aws:s3:::${bucket}",
        "arn:aws:s3:::${bucket}/*"
      ]
    }
  ]
}
EOF

# Control: no ${...} may be left in the file
grep -n '\$' bucket-policy.json || echo "OK: all variables expanded"

Cosa fanno le due istruzioni:

  1. DenyAllWithoutRole: a chiunque non sia uno dei due ruoli viene negato tutto su questo bucket e sui suoi oggetti. Ciò include tutti gli altri credential del progetto, il portale e le richieste anonime.
  2. DenyNonReadActionsForReadonlyRole: il ruolo di sola lettura può solo leggere (GetObject, ListBucket, più le ricerche di versione e posizione necessarie ad alcuni client). Scrittura ed eliminazione gli sono negate.

Un Deny esplicito prevale sempre su qualsiasi Allow. Se non avete bisogno dell'accesso in sola lettura, eliminate la seconda istruzione e la voce -readonly della prima.

Passo 4: fare una prova su un bucket di test

Una mancata corrispondenza tra policy e credential blocca il bucket per tutti. Fate prima una prova su un bucket usa e getta nello stesso progetto e nella stessa regione, richiede due minuti.

export prod_bucket=my-bucket
export bucket=${prod_bucket}-fence-test

aws s3 mb s3://${bucket}

# Run the heredoc from step 3 again (it uses ${bucket} and ${project}),
# then apply the policy to the scratch bucket:
aws s3api put-bucket-policy --bucket="${bucket}" --policy=file://bucket-policy.json

# 1) Read-write credential: export its keys first
export AWS_ACCESS_KEY_ID=<readwrite-access-key>
export AWS_SECRET_ACCESS_KEY=<readwrite-secret-key>
AWS_MAX_ATTEMPTS=1 aws s3 cp - s3://${bucket}/hello.txt <<<"hello"

# 2) Read-only credential: switch to its keys
export AWS_ACCESS_KEY_ID=<readonly-access-key>
export AWS_SECRET_ACCESS_KEY=<readonly-secret-key>
AWS_MAX_ATTEMPTS=1 aws s3 cp s3://${bucket}/hello.txt -

# 3) A credential without a role: switch to its keys (expect 403)
export AWS_ACCESS_KEY_ID=<roleless-access-key>
export AWS_SECRET_ACCESS_KEY=<roleless-secret-key>
AWS_MAX_ATTEMPTS=1 aws s3api head-bucket --bucket "${bucket}"

Il controllo 3 è la vera prova. Se un credential senza ruolo riesce ancora, fermatevi e contattate il supporto: non applicate la policy a un bucket di produzione.

Pulizia:

aws s3api delete-bucket-policy --bucket="${bucket}"
aws s3 rm s3://${bucket} --recursive
aws s3 rb s3://${bucket}

Se la prova blocca se stessa

Se il controllo 1 fallisce con Access Denied, il principal della policy non corrisponde ai credential: ID del progetto errato, un altro progetto oppure un errore di battitura nel nome del ruolo. Lasciate stare il bucket di test e non applicate la policy al bucket di produzione. Senza un credential corrispondente né voi né il portale potete rimuovere la policy, vedere Revoke and lockout.

Passo 5: applicare al bucket

export bucket=${prod_bucket}

# Run the heredoc from step 3 again, then:
aws s3api put-bucket-policy --bucket="${bucket}" --policy=file://bucket-policy.json

# Control: the stored document should match your file
aws s3api get-bucket-policy --bucket="${bucket}" --query Policy --output text | jq .

Passo 6: verificare

Eseguite i tre controlli del passo 4 sul bucket di produzione, ciascuno con il proprio credential come mostrato. Aspettatevi: scrittura e lettura con il credential in scrittura, sola lettura con il credential in sola lettura e 403 per ogni altro credential del progetto.

Comportamento quotidiano

  • Portale: il pannello S3 funziona con il credential selezionato sotto Select Credentials. Se quel credential non porta alcun ruolo, la navigazione, il caricamento e l'editor della policy falliscono con Access Denied. Assegnate al credential usato dai vostri amministratori il ruolo <bucket>-readwrite, oppure lavorate con la CLI.
  • Altra regione: ripetete i passi da 3 a 5 per il bucket nella seconda regione.
  • Nuovi bucket: non c'è ereditarietà. Un nuovo bucket è aperto all'intero progetto finché non riceve la propria policy.
  • Versioning: il ruolo di sola lettura può leggere le versioni attuali e precedenti degli oggetti, nient'altro.
  • Modifica di un ruolo: create il nuovo credential, aggiungete il nuovo ruolo alla policy, migrate le applicazioni, poi rimuovete il vecchio ruolo dalla policy. Mai nell'ordine inverso.

Revoca e blocco

Per revocare, rimuovete la policy. Serve un credential che porti <bucket>-readwrite:

aws s3api delete-bucket-policy --bucket="${bucket}"

Successivamente il bucket torna al comportamento predefinito del progetto: ogni credential del progetto ha di nuovo accesso completo.

Blocco: nessuno ha più accesso

Sintomo: ogni credential del progetto, compreso il portale, riceve Access Denied sul bucket. Causa: i nomi dei ruoli o l'ID del progetto nella policy non corrispondono ad alcun credential esistente, oppure l'ultimo credential che portava il ruolo è stato eliminato.

Il recupero senza supporto funziona finché conoscete i nomi dei ruoli della policy. Create un nuovo credential nello stesso progetto con esattamente quel nome di ruolo (<bucket>-readwrite). L'istruzione di negazione esclude questo principal, quindi il nuovo credential ha di nuovo accesso. Usatelo per eliminare la policy, poi ricreate il credential perso.

Recupero con supporto: se i nomi dei ruoli non sono più noti, aprite un ticket con l'ID del progetto, il nome del bucket, la regione e l'indicazione che una bucket policy DenyAllWithoutRole blocca il bucket. Il supporto può rimuovere la policy.

Prevenzione: create entrambi i credential prima di applicare la policy, fate una prova su un bucket di test e non eliminate mai il credential in scrittura finché la policy è attiva.

Risoluzione dei problemi

Sintomo Causa e soluzione
Le richieste si bloccano per circa un minuto, poi restituiscono 403 La CLI riprova le richieste fallite. export AWS_MAX_ATTEMPTS=1 fallisce rapidamente, utile per le negazioni attese sopra.
MalformedPolicy al momento dell'applicazione ${...} non è stato espanso: l'heredoc deve iniziare con <<EOF, non con <<'EOF'.
aws s3 rb fallisce Il bucket non è vuoto. Eliminate prima gli oggetti (aws s3 rm --recursive, richiede il ruolo).
Il bucket compare ancora in aws s3 ls Normale. La segregazione nega l'accesso al contenuto, non al nome del bucket.
Un secondo credential con lo stesso ruolo funziona Normale. Il nome del ruolo è l'ambito, non la chiave.