NILFS2: Segurança contra Arquivos Deletados via Snapshots Contínuos

Nesses últimos dias tenho estudado e testado um monte de coisa nova (pelo menos pra mim). Entre elas, descobri o NILFS2, um sistema de arquivos para Linux que implementa o conceito de "snapshots contínuos": cada alteração elementar no sistema de arquivos (criação, remoção, escrita de bloco) cria um novo "snapshot" que pode ser montado posteriormente (em modo "somente leitura"), efetivamente criando o efeito de "voltar no tempo" ou "undo infinito". Neste post, vou fazer uma demonstração rápida dos seus recursos só para dar água na boca e vou terminar com um comentário filosófico sobre a velha questão "seguro contra o que".

Nota: Embora os conceitos básicos sejam universais, esse post é meio técnico e tende a ser melhor apreciado por usuários do Linux (especialmente quem roda o Ubuntu 10.04, pois poderão seguir o passo-a-passo) que não tenham medo da linha de comando.

Preparação

No Ubuntu 10.04 ou superior, o driver do kernel já vem instalado e se chama – adivinhe! – nilfs2. A primeira coisa que você precisa instalar são os utilitários de criação e controle, o seguinte comando (nos exemplos abaixo, em vermelho está o que você digita; em preto, estão os prompts e as respostas):

$ sudo apt-get install nilfs2-tools

Nessa demonstração, vou assumir que você vai usar um pen-drive como dispositivo de bloco subjacente. Nota: os testes que vamos fazer vão destruir o conteúdo preexistente do seu pen-drive.

Espete seu pen-drive na porta USB. Após alguns segundos, o shell do seu ambiente gráfico deve detectá-lo e montá-lo automaticamente, fazendo aparecer um ícone na área de trabalho e talvez uma janela do seu gerenciador de arquivos. Na realidade, nesse caso essa super-automação nos atrapalha, e a primeira coisa que teremos de fazer é desmontar o sistema de arquivos sem remover o pen-drive.

Para isso, abra um terminal e promova-se para root (sudo -i no Ubuntu e afins, /bin/su - nos demais) e digite esses dois comandos:

# p=($(dmesg | tail -50 | perl -ne 'print "$_\n" for m{sd[a-z]\d}g'))
# echo ${p[*]}
sdb1

O primeiro comando cata nas últimas 50 linhas do log de atividades do kernel (acessível através do comando dmesg) os nomes dos devices atribuídos às partições do pen-drive, salvando-os no vetor p, porque vamos precisar dele depois.

O segundo comando imprime os resultados, para que possamos conferir que funcionou direito – neste ponto, é extremamente importante conferir que o dispositivo detectado não seja o do seu disco rígido (normalmente chamados /dev/sdaX, sendo "X" um número qualquer), pois logo adiante vamos realizar uma operação destrutiva e certamente não queremos destruir seu sistema. Na maioria dos casos, deverá aparecer sdb1, como no exemplo acima, referindo-se à primeira (e normalmente única) partição do seu pen-drive.

Certos de que estamos fazendo a coisa certa, vamos desmontar os sistemas de arquivos usando o comando abaixo:

# for a in ${p[*]} ; do umount /dev/$a ; done

Esse comando itera por todos os elementos do vetor p e sai desmontando os filesystems de cada partição. Se por alguma razão, alguma das partições não estiver montada, dará uma mensagem de erro inócua. Aqui no meu GNOME, isso também faz com que quaisquer janelas do gerenciador de arquivos que estejam mostrando o conteúdo do pen-drive sejam automaticamente fechadas.

Agora vamos criar o sistema de arquivos NILFS2 na primeira dessas partições – essa é a operação destrutiva:

# mkfs.nilfs2 /dev/${p[0]}
mkfs.nilfs2 ver 2.0
Start writing file system initial data to the device
       Blocksize:4096  Device:/dev/sdb1  Device Size:16011231232
File system initialization succeeded !!

Ativando o Sistema de Arquivos

Agora podemos montar o sistema e arquivos:

# mkdir -p /mnt/nilfs
# mount -t nilfs2 /dev/${p[0]} /mnt/nilfs
mount.nilfs2: WARNING! - The NILFS on-disk format may change at any time.
mount.nilfs2: WARNING! - Do not place critical data on a NILFS filesystem.

Como o NILFS2 ainda é considerado "experimental", ele dá esse aviso dizendo que o formato em disco pode mudar. O isso significa é que uma futura versão do NILFS pode não ser diretamente compatível com a atual. Mesmo assim, ele já é bem estável para uso diário em uma grande gama de aplicações.

Testando os "Snapshots Contínuos"

Para testar o recurso-chave do NILFS2, vamos fazer o seguinte: vou criar um arquivo, depois sobrescrever seu conteúdo e em seguida reaver o conteúdo original.

Primeiro passo: vamos criar um arquivo-texto de nome teste.txt com o conteúdo "isso eh um teste".

# echo isso eh um teste > /mnt/nilfs/teste.txt
# cat /mnt/nilfs/teste.txt
isso eh um teste

A quase cada operação de escrita ou após certo intervalo de tempo (da ordem de alguns segundos), o NILFS2 cria um novo "checkpoint" do sistema de arquivos. Podemos vê-los com o comando lscp ("list checkpoints").

# lscp
CNO        DATE     TIME  MODE  FLG   NBLKINC       ICNT
  1  2011-05-31 09:03:30   cp    -         11          3
  2  2011-05-31 09:06:09   cp    -         11          4
  3  2011-05-31 09:12:27   cp    -         14          5

Ou seja, após a criação do arquivo, o contador de checkpoints ("CNO") estava em 3. Agora vamos sobrescrever o conteúdo daquele arquivo:

# echo o conteudo anterior jah era > /mnt/nilfs/teste.txt
# cat /mnt/nilfs/teste.txt
o conteudo anterior jah era

Rodando novamente o lscp, podemos ver que um novo checkpoint, de número 4, foi criado:

# lscp
CNO        DATE     TIME  MODE  FLG   NBLKINC       ICNT
  1  2011-05-31 09:03:30   cp    -         11          3
  2  2011-05-31 09:06:09   cp    -         11          4
  3  2011-05-31 09:12:27   cp    -         14          5
  4  2011-05-31 09:16:30   cp    -         11          5

Reavendo um Estado Anterior do Sistema de Arquivos

Para usarmos o recurso de "voltar no tempo" que o NILFS oferece, precisamos entender alguns detalhes de como ele funciona. Primeiro, existem dois tipos de checkpoints: os normais e os "snapshots".

Os checkpoints normais podem ser destruídos no futuro quando o NILFS precisar de espaço em disco para guardar coisas novas. O NILFS evita ao máximo sobrescrever o dispositivo: ele vai salvando os blocos de dados em sequência. Só quando o dispositivo fica cheio é que o NILFS sai procurando espaço nos checkpoints mais antigos para recliclar. Por isso, um "checkpoint" não é garantido, no sentido que em algum momento (que pode demorar muito ou ser bem rápido, dependendo do quanto de espaço em disco suas outras atividades consumam) ele poderá ser reciclado.

Ao converter um "checkpoint" para um "snapshot", você informa ao NILFS que tem interesse que ele seja preservado. Por isso, "snapshots" são garantidos, no sentido que eles não serão reciclados.

Então, para podermos voltar no tempo, precisamos registrar o nosso interesse em um checkpoint específico, convertendo-o em um snapshot. Isso pode ser feito facilmente através do comando chcp ("change checkpoint"):

# chcp ss 3

O comando acima transformou o checkpoint número 3 em um snapshot. Feito isso, podemos montá-lo em modo somente-leitura passando a opção cp (específica do NILFS) para o comando mount:

# mkdir -p /mnt/antigo
# mount -t nilfs2 -r -o cp=3 /dev/${p[0]} /mnt/antigo
# cat /mnt/antigo/teste.txt
isso eh um teste

Quando você monta um snapshot anterior, todo o estado do sistema de arquivos volta para aquele momento no tempo, nos mínimos detalhes, inclusive os nomes e permissões de arquivos. Portanto, naturalmente, esse lance também funciona para "desapagar" arquivos.

Quando não tivermos mais interesse em um snapshot, podemos usar novamente o comando chcp para convertê-lo novamente para checkpoint, de forma que seu espaço em disco será reciclado.

Considerações

O conceito geral de snapshots não é uma coisa nova; o LVM já suporta isso para dispositivos de bloco genéricos (e, por isso, para filesystems quaisquer) há mais de uma década; toda boa solução de virtualização (VMWare, VirtualBox, QEMU, User Mode Linux) oferece recursos semelhantes. Os usuários do MacOS já têm há um tempão um sistema de snapshots periódicos chamado Time Machine. Alguns filesystems recentes do Linux, como o btrfs, suportam snapshots ultra-leves nativamente, permitindo, juntamente com um script no cron, obter o mesmo efeito do Time Machine. Os Windows mais recentes têm um recurso parecido chamado Volume Shadow Copy (também apelidado de "Volume Snapshot Service") que é usado por algumas ferramentas de backup e pela aba "Versões Anteriores" no gerenciador de arquivos.

Todas essas soluções, porém, requerem intervenção manual para criar os snapshots. O que eu achei bacana no NILFS2 é o fato dos snapshots serem cirados continuamente, sem necessidade de intervenção do usuário e a cada operação elementar de alteranção no sistema de arquivos. Assim, é possível recuperar praticamente qualquer estado anterior, e não apenas aqueles que você (ou seu script no cron ou o agendamento do Time Machine) teve o cuidado de salvaguardar.

Esse treco poderia ter salvado a minha vida naquelas vezes em que eu inavertidamente apaguei um arquivo (sou cuidadoso; isso só me aconteceu umas cinco ou seis vezes em mais de dez anos... mas nas vezes que aconteceu, fiquei muito, muito, muito zangado e me atrapalhou bastante).

Outra coisa interessante do NILFS é o fato de ser "log strucutred", o que basicamente significa que ele vai ocupando os blocos sequencialmente. Isso é interessante para mídias que têm limitações na quantidade de vezes que se pode salvar um bloco, como é o caso das memórias FLASH. Verdade que as controladoras dos SSDs e pen-drive modernos implementam recursos de "wear leveling" que permitem seu uso com sistemas de arquivos convencionais que sobrescrevem várias vezes no mesmo lugar.

Outra consequência do estilo "append-only" de tratar o dispositivo de bloco como se fosse uma grande FIFO (também chamado de "ring buffer") é a facilidade de recuperação para um estado consistente em caso a máquina trave ou falte luz.

Uma ressalva – a versão que vem no Ubuntu, apesar de funcional, não tem um recurso que já está disponível na versão mais recente: a possibilidade de crescer o tamanho do sistema de arquivos, importantíssimo para quem vislumbra usá-lo sob o LVM.

Outra limitação besta é que os snapshots anteriores do NILFS2 são sempre somente leitura – ele não suporta branching, ou seja, ramificar-se em múltiplas "versões atuais". (Muito embora o LVM2 permita isso com qualquer filesystem; ainda não testei, mas deve ser interessante misturar os snapshots do LVM com os do NILFS2). Imagino que no futuro essa limitação será removida, pois se trata de um recurso muito útil.

Comentários Filosóficos Finais

Não resisto a um comentário filosófico: o NILFS ilustra bem como é importante definirmos o "seguro contra o que" e quais, exatamente, são nossas prioridades, o nosso "modelo de ameaça".

Explico: se nossa preocupação maior é perdermos dados por erro do operador, bug em algum programa ou "acidentes" desse tipo, o NILFS é uma solução muito mais segura (sob esse ponto de vista) do que qualquer outro esquema de backup ou filesystem que eu conheça.

Mas se nosso medo é termos nossos segredos caindo em mãos erradas se nosso computador for roubado ou apreendido, o NILFS sozinho é extremamente inseguro, pois, nele, é deliberadamente difícil apagar algo. Nele, a técnica padrão de sobrescrever um arquivo para obliterar seu conteúdo só funciona se preenchermos todo o conteúdo do dispositivo de armazenamento com lixo, de forma a reciclar toda a área disponível, e isso tudo tendo o cuidado de marcar como "normais" os checkpoints que têm as versões dos dados que querermos "destruir". É inconveniente, para dizer o mínimo.

Outra maneira de colocar essa questão é perceber que, do ponto de vista de um perito em forense computacional, o NILFS é um verdadeiro sonho de consumo, pois não dá trabalho quase nenhum para reaver seus estados anteriores – um verdadeiro palimpsesto digital.

No nosso exemplo, uma maneira relativamente fácil de harmonizar o "undo infinito" do NILFS com sigilo forte seria usar cifragem do dispositivo de bloco, tal como oferecido pelo TrueCrypt ou o dm-crypt.

Esse caso do NILFS é um exemplo de uma coisa que acontece muito na área de segurança: quando você torna uma coisa muito segura sob um certo ponto de vista, tende a torná-la fraca sob outro critério. Atingir o balanceamento correto, como no exemplo do TrueCrypt/dm-crypt, requer uma "visão de conjunto", uma "abordagem holística", que tem tanto de arte quanto de ciência – especialmente quando o quesito "custo" não pode ser ignorado.

Comentários
Aceita-se formatação à la TWiki. HTML e scripts são filtrados. Máximo 15KiB.

 
Enviando... por favor aguarde...
Comentário enviado com suceso -- obrigado.
Ele aparecerá quando os moderadores o aprovarem.
Houve uma falha no envio do formulário!
Deixei uma nota para os admins verificarem o problema.
Perdoe-nos o transtorno. Por favor tente novamente mais tarde.
Marco Carnut | 2011-06-13 11:15:49 | permalink | topo

Renato,

Não acho absolutamente necessário que o NILFS2 implemente uma camada de criptografia embutida. As soluções de cifragem em nível do dispositivo de bloco, tais como o TrueCrypt e o dm-crypt, parecem-me perfeitamente razoáveis.

Ou talvez você consiga o que quer usando um sistema de arquivos criptográfico empilhado, como é o caso do eCryptfs. Aliás, seria interessante investigar como as garantias criptográficas do eCryptfs se comportam quando o substato é o NILFS. Não sei dizer, a priori, se não há alguma "interação medicamentosa" ruim entre os dois. Só testando. Por que você não escreve um artigo sobre isso? :)

Quanto ao Google ou a Microsoft não se esquecerem de você, isso me parece outro problema. Se você está preocupado com isso, a coisa mais simples é deixar de usar os serviços gratuitos deles. Não sei se alguém teve sucesso em pedir explicitamente pra eles que os esqueçam. Eis aí outra coisa que eu gostaria de saber.

Quando ao caso das empresas, aqui em aquela velha ambiguidade: o que, exatamente, significa "guarda segura da informação"? Seguro contra o que, exatamente? Pelo resto da sua frase, parece que a preocupação é que o funcionário apague os arquivos – que é apenas uma dentre as várias possíveis definições de "guarda segura da informação".

Ainda que tomemos como desejável ter alguma proteção contra isso, o NILFS2 só oferece essa garantia se o funcionário for incauto o suficiente para apenas tentar apagar o arquivo. Se ele "reformatar" o disco ou passar algum programa apagador como o DBAN, os dados já eram, com o NILFS ou qualquer outro sistema de arquivos.

É por essas e outras que muitas empresas adotam um modelo de trabalho em que tudo é salvo em um servidor de arquivos centralizado, sob o controle físico da empresa.

Renato Campachi | 2011-06-08 09:42:45 | permalink | topo

Muito interessante. Mas fico com receio, como comentado pelo próprio autor, se seu disco cair em “mão erradas” vai ser uma festa. O NILFS deveria ter uma camada de segurança própria, a cifragem da partição, por opção.

Eu fico com receio, pois o Google/MS têm informações digitais aos montes minhas (até aquelas que já esqueci), quanto mais um sistema de arquivos com cópia continua por padrão. Sei lá, tem coisas que é melhor esquecer. ;-)

Creio que o NILFS faça mais sentido nas empresas que não sabem lidar com a guarda segura da informação produzida por seus funcionários. Os documentos ficam guardados em seus próprios discos, e ao fim do contrato de trabalho, aquele funcionário apaga TUDO! Um tremendo prejuízo para empresa (tempo é dinheiro, e esforço compreendido também).

Rodrigo Santos Silva | 2011-05-31 22:44:02 | permalink | topo

Muito legal essa estrutura de imagem contínua.. porém o que pena é ter que criar snapshots e montar todas os checkpoints criados até encontrar a informação desejada.. Existe um preview dos checkpoints? Isso facilitaria bastante a vida, aumentando a semelhança com o "Time Machine".

[]s Rodrigo