EBS, seus snapshots e quanto ocupam de espaço
Tags: aws, security, ebs, cloudops
Published: 2024-02-16 01:22:50

Se você for chato como eu, provavelmente não fica nada satisfeito com a seguinte informação fornecida pelos Snapshots dos seus discos EBS:

```bash
$ aws ec2 describe-snapshots --owner-ids self --query 'Snapshots[0]' | jq '{ "Id" :.SnapshotId, "Tamanho": .VolumeSize } '
{
  "Id": "snap-0ad47bdaaf65a4a33",
  "Tamanho": 300
}
```

Por que eu digo isso? Se você pegar uma lista com os snapshots diários realizados de um disco, todos eles vão ter esse tamanho "300", que é na verdade o tamanho do disco do qual foi tirado o snapshot:

```bash
jq -r  '.Snapshots[] | "\(.StartTime | sub("\\.[:+0-9]*"; "") | strptime("%Y-%m-%dT%H:%M:%S") | strftime("%Y-%m-%d %H:%M:%S") );\(.VolumeId);\(.SnapshotId);\(.VolumeSize)"'  /tmp/snapshots  | sort | fgrep 2024-01 | fgrep volumeAlvo | cut -f1,3,4 -d';'
2024-01-12 06:52:14;snap-1e61c51bff6bccb1c;300
2024-01-13 06:50:26;snap-156ff8793f546c40c;300
2024-01-14 06:50:35;snap-1540ae07ff3f2fafc;300
2024-01-15 06:55:12;snap-1deffec06fff3a76c;300
2024-01-16 06:50:55;snap-1936dd665a700507c;300
2024-01-17 06:48:18;snap-188a940760ef0c94c;300
2024-01-18 06:49:03;snap-1945f27eae3a35e7c;300
2024-01-19 06:51:01;snap-176d9fee762cfb56c;300
2024-01-20 06:50:14;snap-115fe1148762c5f3c;300
2024-01-21 06:51:17;snap-10d4a9af2fb8939fc;300
2024-01-22 06:50:02;snap-1e37bafc822cc7b4c;300
2024-01-23 06:50:51;snap-11ee2feb0c80bf6cc;300
2024-01-24 06:52:55;snap-15c1f5bfd8c973b1c;300
2024-01-25 06:50:05;snap-1236f4771ac4ff4fc;300
2024-01-26 06:52:34;snap-135de7fda2ca0039c;300
2024-01-27 06:54:12;snap-1a2b6f44c3b250fac;300
2024-01-28 06:49:42;snap-15afe58c720ea661c;300
2024-01-29 06:50:24;snap-1c2cb9f83d180b5dc;300
2024-01-30 06:51:31;snap-19292f66969ae91cc;300
2024-01-31 06:49:24;snap-183c70ef3ce184b1c;300
```

E isso não faz sentido, porque vamos relembrar a [definição de snapshot para a AWS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSSnapshots.html):

> You can back up the data on your Amazon EBS volumes by making point-in-time copies, known as Amazon EBS snapshots. A snapshot is **an incremental backup**, which means that we save only the blocks on the device that have changed since your most recent snapshot. 

Ou seja, o primeiro snapshot pode até ter próximo de 300 GiB; o segundo, entretanto, a menos que você reescreva o disco inteiro todos os dias, deverá ter **bem menos blocos**!

E como fazer para saber o tamanho real de um snapshot? A resposta é que não há uma maneira conveniente.

Então vamos de maneira inconveniente mesmo!

A partir da saída acima, podemos gerar um arquivo:

```bash
$  jq -r  '.Snapshots[] | "\(.StartTime | sub("\\.[:+0-9]*"; "") | strptime("%Y-%m-%dT%H:%M:%S") | strftime("%Y-%m-%d %H:%M:%S") );\(.VolumeId);\(.SnapshotId);\(.VolumeSize)"'  /tmp/snapshots  | sort | fgrep 2024-01 | fgrep <volume> | cut -f1,3,4 -d';' > /tmp/snaps
``` 

Vamos comparar a diferença dos blocos do primeiro snapshot para o último:

```bash
$ aws ebs list-changed-blocks  --first-snapshot-id snap-1e61c51bff6bccb1c --second-snapshot-id snap-183c70ef3ce184b1c  --max-results 100
{
    "ChangedBlocks": [
        {
            "BlockIndex": 2,
            "FirstBlockToken": "ADEBAWTl1T2vIApHeiKgKcuwBAiqZ3klr66mE47BZomkZvBk1xa7l6Se3EVy",
            "SecondBlockToken": "ADEBAZC7wx3B//8SZ8AO8Ik4nO3drO69ZHydPGfiFdQPf7+z0/yeTvhtpdPk"
        },
        {
            "BlockIndex": 3,
            "FirstBlockToken": "ADEBAUFwRyPWnxXmE9tuUiGA0/IZNMT6QeArgY6LEUPzqfc1kla8X1RE682F",
            "SecondBlockToken": "ADEBAXZa8N6xIhaeQmNpB4Ryg0c71c3hfJggUam5aRWzXtsOMIM+o7B8gTMz"
        },
        {
            "BlockIndex": 9,
            "FirstBlockToken": "ADEBAY/0eak3ujr8ZeaKojwpqpDdDgkEEoJbwm1qQq3ZD8OGgZJWWawJ3gAP",
            "SecondBlockToken": "ADEBAZMWcWKeWoOVSmSoddWfqIpt4MJb7jC8pczPc2Zb5bzDhaXdOcy5igEx"
        },
        {
            "BlockIndex": 54,
            "FirstBlockToken": "ADEBAWVhyEKJPSo3dngCxFvu+thS+Wxb6bIsya3oW/7BhdXgKwXAEd1pf8le",
            "SecondBlockToken": "ADEBAZ/wRcFMm0dps8ORFkiSutXRI+UBS+Zx5sQ0nUonUTwBP6oQg0XcsTTI"
        },
        {
            "BlockIndex": 63,
            "FirstBlockToken": "ADEBAbG6AUCqy8i8iTha8TsuS/KJjkR6m2orwTiWu6x1TRnhF9nZQeNryPNG",
            "SecondBlockToken": "ADEBAa1CD96xB6tGbtqNlh2L0oRVcsQTWB5Zt2oFaYnF9r3Nl0BkdTXoMrgU"
        }
    ],
    "ExpiryTime": "2024-02-22T23:27:36.911000-03:00",
    "VolumeSize": 300,
    "BlockSize": 524288,
    "NextToken": "ADEDAfg20tH5S6MgIQvwejESz9oJt6wTkV9E7uLE/AWOE3YldGqcD87vayC4iR409U/UlvchfJ31"
}
```

## "Temos 800 TiB de snapshots!"

Em um outro caso, alguém ficou horrorizado porque tínhamos quase 800TiB de snapshots de um backup de um disco que não estava sendo usado!

Mas... Se o disco não está sendo usado, os snapshots subsequentes possivelmente estão vazios, pois não há diferença entre os blocos!

Vamos olhar?

```bash
$ jq -r  '.Snapshots[] | "\(.StartTime | sub("\\.[:+0-9]*"; "") | strptime("%Y-%m-%dT%H:%M:%S") | strftime("%Y-%m-%d %H:%M:%S") );\(.VolumeId);\(.SnapshotId);\(.VolumeSize)"'  /tmp/snapshots  | sort | fgrep 2024-01 | fgrep <outro volume> | cut -f1,3,4 -d';' > /tmp/snaps

$ head /tmp/snaps
2024-01-12 05:08:57;snap-021dd9ce209e29e04;8000
2024-01-13 05:04:54;snap-0bb0b0a4ba1aa52a4;8000
2024-01-14 05:05:34;snap-0235d8d888956aabc;8000
2024-01-15 05:05:45;snap-0b438036484680c4d;8000
2024-01-16 05:02:47;snap-0224676f259ea425c;8000
```

Vamos separar o primeiro snapshot (que tem o disco inteiro) e comparar com todos os demais:

```bash
FIRST=$( head -n1 /tmp/snaps | cut -f2 -d';' ) ; for i in $( tail -n+2 /tmp/snaps | cut -f2 -d';' ) ; do  echo -n $i: ; aws ebs list-changed-blocks --first-snapshot-id $FIRST --second-snapshot-id $i | jq '.ChangedBlocks | length' ; done
snap-02b0b0a4ba1aa52a4:0
snap-0b35d8d888956aabc:0
snap-02438036484680c4d:0
snap-0b24676f259ea425c:0
snap-02356588643dade8c:0
snap-05391e8ce8e97ac09:0
snap-022654ea634922940:0
snap-0c559252bb1323192:0
snap-0197cfa3b57dd112a:0
snap-0baf04b1bc66d9fbe:0
snap-0c8139bca34c3fa2b:0
snap-01a13015e939ec730:0
snap-03c616d28c4c2f8e2:0
snap-00d3d9a860deebf38:0
snap-0e98d71bec134ca26:0
snap-0374d3f4ce87bad9e:0
snap-0f797d4836e05681d:0
snap-0b305c3b79e375f36:0
snap-06338105ef6571bee:0
```

Mostrando sem o jq:

```bash
...
# Saída sem o jq:
snap-02b0b0a4ba1aa52a4:{
    "ChangedBlocks": [],
    "ExpiryTime": "2024-02-15T22:16:59.026000-03:00",
    "VolumeSize": 8000,
    "BlockSize": 524288,
    "NextToken": "ADEDAao20vaMS6MgIahZ7GoSz9o7FPruoV9E7tDE4qoDE3YldGqcD84P8/h4vpdT5TY/6I/kb8Ud"
}
```

Ou seja, esses snapshots definitivamente não estão ocupando 800TiB, ou o custo seria na casa de 55 mil dólares!

## EBS Direct API

[Este recurso](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-accessing-snapshot.html) não tão conhecido é a API pela qual podemos disparar a criação de snapshots.

Só que vai muito além! Com ela, é possível **ler e escrever dados em snapshots**, além de analisar as diferenças entre snapshots da mesma origem!


## Nota de rodapé pensando em segurança

Eu naturalmente estava interessado apenas na funcionlaidade de listar as diferenças entre snapshots. Mas vai aqui algumas informações importantes sobre este serviço sob a ótica da segurança.

[**Na documentação**](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ebs/index.html) é possível consultar os comandos da EBS Direct API:

* start-snapshot
* complete-snapshot
* get-snapshot-block
* list-changed-blocks
* list-snapshot-blocks
* put-snapshot-block

Se você olhar o site, eu mudei a ordem propositalmente.

Os dois primeiros são considerados **Management Events** pela API da AWS, e portanto são logados por padrão pelo CloudTrail.

Os outros quatro, entretanto, são considerados **Data Events**. E como você bem sabe (provavelmente porque já precisou ir atrás de quem deletou algo de um bucket S3), este tipo de evento **não é logado por padrão no CloudTrail**!

Ou seja, alguém pode usar **get-snapshot-block** para baixar pedaços dos seus discos e você **nunca vai saber** a menos que tenha data events ativado em uma Trail! Maneira extremamente maliciosa de surrupiar dados dos outros.