NASDEC: Kernel, Cifragem e Alguns Testes de Desempenho

No episódio de hoje da saga do NASDEC, vou detalhar a instalação do sistema operacional básico e da pilha de software relacionada ao gerenciamento básico do espaço em disco e a criptografia em tempo real, sobretudo os "truques" que eu tive de usar para que tudo isso funcione com bom desempenho. Esse artigo também serve como um tutorial sobre cifragem de disco com o dm-crypt, que creio ser útil até para quem não está interessado no NASDEC.

Talvez não pareça, mas tentei tornar o texto curto e acessível, mas mesmo assim o artigo saiu longo e tecnicamente denso. Há alguns atalhos:

  • Se você quer simplesmente reproduzir meus resultados sem entender em detalhe a razão das escolhas técnicas subjacentes, vá direto para a seção "Resumo da Ópera" mais adiante;

  • Se você está fazendo o seu próprio NASDEC e não vai querer usar cifragem, você pode simplesmente pular este artigo e partir para o próximo da série.

Instalação Básica

A instalação incial foi bem simples e quase sem sustos. Eu comecei baixando a versão "Live/Standard" do Debian 6. Com 240MB de tamanho, é relativamente pequena se comparada às demais porque vem sem ambiente gráfico; é uma versão para quem quer instalar sistemas minimalistas pela linha de comando, como é o caso do NASDEC. Além disso, já vem prontinha para ser jogada em um pendrive USB (em todos os exemplos, em vermelho-negrito está o que você digita; em preto estão os prompts e as respostas que o computador emite):

# dd if=debian-live-6.0.1-amd64-standard.img of=/dev/sdb

Espetei o pendrive em uma das portas USB do NASDEC e dei partida no sistema. Sendo relativamente moderna, a BIOS tem uma opção pela tecla F12 que permite escolher a partir de qual dispositivo ela deverá buscar o bootloader. Escolhi a porta USB e sem mais delongas me descobri no instalador modo texto padrão do Debian.

Quando eu fiz originalmente a instalação, eu ainda não tinha instalado os HDs de 3TB – lembre que no episódio anterior eu disse que o sistema operacional ia ficar instalado no cartão MicroSD que eu espetei em uma das interfaces USB internas, que aparecia como /dev/sda.

A instalação foi quase totalmente dentro do padrão. A única coisa que eu fiz foi aderir a um antigo hábito de criar uma partição separada com uns 200MB para o /boot e colocar a raiz em uma outra partição com o restante do espaço disponível. Nos dois eu usei o sistema de arquivos ext3 mesmo.

Não me dei ao trabalho de criar uma partição dedicada para swap, não só porque os 2GB de memória da máquina são mais do que suficientes para que tudo que pretendo rodar nela caiba inteiramente em memória, mas também porque swap em uma mídia degradável como a memória flash do pendrive não é uma boa idéia – tende a reduzir a vida útil do dispositivo. E, se algum dia no futuro eu precisar mesmo de área de swap, eu posso criá-la dinamicamente em um arquivo comum no filesystem.

Continuando com a instalação: um pequeno susto veio quando reiniciei o computador para testar o sistema recém instalado: engatou na tela do "Welcome to GRUB", antes de aparecer o menu propriamente dito. O problema foi resolvido instalando esse upgrade de BIOS, o que foi até bem fácil porque a BIOS tem um recurso de upgrade fácil pela tecla F7, que lê a nova imagem direto de um pendrive. Feito isso, reinicie o sistema e o GRUB funcionou normalmente, carregando o kernel e o resto do Debian sem incidentes.

Depois que eu instalei os HDs de 3TB, houve uma mudança nos nomes dos dispositivos: o HD passou a ser /dev/sda e o cartão MicroSD passou a ser /dev/sdb; não deu problema porque a linha de boot do kernel no GRUB identifica o dispositivo pelo UUID e não por nome.

Antes mesmo de instalar qualquer coisa nos discos, fiz um teste rápido da velocidade de gravação deles, usando o comando abaixo para preencher os primeiros 10GB com zeros:

# dd if=/dev/zero of=/dev/sda bs=1M count=10000

Obtive uma velocidade de 120MB/s para o WD Caviar Green e 160MB/s para o Seagate Barracuda, valores compatíveis com outros benchamarks que vi publicado.

Na realidade, o que isso mede é o desempenho de escrita sequencial para blocos de dados grandes. Essa é a métrica que me baseei nos testes que vou descrever mais adiante, até porque o principal uso que vou fazer com o NASDEC é armazenar um monte de arquivos grandes, da ordem de centenas de megabytes até dezenas de gigabytes. Mas vale lembrar que existem várias outras métricas para avaliar o desempenho de discos rígidos e sistemas de armazenamento: a velocidade de leitura, também muito importante, mas quase sempre ela é superior à velocidade de escrita, o que torna esta última ponto principal a ser otimizado; e a latência, por exemplo, que se torna particularmente importante no caso de sistemas de arquivos tipo "journalling" e acessos a muitos arquivos pequenos.

Uma Solução de Criptografia de Disco com o AES

Vamos começar fazendo uma experiência pra ver a velocidade da cifragem com o dm_crypt. Comecei criando um arquivo temporário com 900MB de zeros:

# cd /dev/shm
# dd if=/dev/zero of=test.img bs=1M count=900
900+0 records in
900+0 records out
943718400 bytes (944 MB) copied, 2.31607 s, 407 MB/s

O arquivo foi criado em 2,32 segundos, o que deu uma velocidade de escrita direto para a memória de 407 MB/s.

Note que eu criei esse arquivo no diretório /dev/shm, que, no Debian (e em várias outras distros), é montado usando tmpfs, um sistema de arquivos simulado em RAM. Assim, esse teste vai medir só o impacto da criptografia na CPU e memória, sem envolver o disco rígido. Observe também que, por padrão, o /dev/shm é limitado a um tamanho máximo de metade da sua RAM total; assim, o arquivo de 900MB só vai caber se você tiver pelo menos 2GB de RAM.

Em seguida, usei o comando losetup para "transformar" o arquivo test.img em um dispositvo de bloco chamado /dev/loop0.

# losetup /dev/loop0 test.img

Pronto, agora temos um dispostivo de bloco com 900MB de zeros em RAM para fazer nossos testes. Em seguida, vamos criar uma versão cifrada dele:

# echo senha | cryptsetup create test /dev/loop0

A partir desse momento, agora existe um dispositivo chamado /dev/mapper/test que é a "versão às claras" do /dev/loop0, que estará cifrado.

O comando acima só monta o circo e cria os dispositivos, mas ele não realmente escreve nada. Tanto é que, se formos ver o comecinho do arquivo test.img, veremos que ele ainda está apenas com zeros, tal como no momento que o criamos:

# xxd test.img | head -5
0000000: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................

Por estranho que possa parecer, essa é a versão "cifrada". Ela está "zerada" porque nada foi escrito nela ainda. A versão "às claras" aparece assim:

# xxd /dev/mapper/test | head -5
0000000: 2efe 9fad 6292 f3d4 1aec 3197 f52d b5e2  ....b.....1..-..
0000010: 4b67 37e3 9e3e d602 c96a 6f2b 723b 9241  Kg7..>...jo+r;.A
0000020: 4b67 37e3 9e3e d602 c96a 6f2b 723b 9241  Kg7..>...jo+r;.A
0000030: 4b67 37e3 9e3e d602 c96a 6f2b 723b 9241  Kg7..>...jo+r;.A
0000040: 4b67 37e3 9e3e d602 c96a 6f2b 723b 9241  Kg7..>...jo+r;.A

Isso é como um bloco cheio de zeros fica após ser "decifrado" usando a frase-senha senha. As repetições acontecem porque o AES, o algoritmo de criptografia usado por padrão pelo dm_crypt, opera em blocos de 16 bytes. Não sei explicar ao certo por que o primeiro bloco de cada setor (512 bytes/setor) fica diferente dos demais.

Vamos agora escrever um único byte de valor 0x01 no dispositivo e ver como ele aparece na versão cifrada:

# printf '\x1' | dd of=/dev/mapper/test bs=1 count=1
# xxd test.img | head | more
0000000: e598 aa51 2a61 3f82 695a a19f 1563 f03b  ...Q*a?.iZ...c.;
0000010: eb91 884c 3e65 f5e3 0871 6f9e a188 e489  ...L>e...qo.....
0000020: 24e9 67d8 0a7f 8151 1e02 24d2 b6ff fad3  $.g....Q..$.....
0000030: 5b14 9447 1cd0 7386 bb11 4b1c f168 fc12  [..G..s...K..h..
0000040: aeed c649 2929 df2f 7709 f65d 5fb9 4775  ...I))./w..]_.Gu

   ( ... um monte de outras linhas deletadas por brevidade ...)

00001e0: 0170 1b2e 995b a914 a2f0 28a6 9d44 e783  .p...[....(..D..
00001f0: 190f 797f 3a00 cdfb 78b8 44ca 9fcc 6c6d  ..y.:...x.D...lm
0000200: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000210: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000220: 0000 0000 0000 0000 0000 0000 0000 0000  ................

   ( ... )

Note que o setor inteiro de 512 bytes mudou por completo (os bytes a partir de 0x200, ou 512 em decimal, continuam zerados). Do ponto de vista criptográfico, isso é considerado bom: uma mudança pequena, de um único byte, na versão "às claras" faz com que quase todos os 512 bytes do setor mudem. Isso tende a dificultar sobremaneira vários tipos de ataques criptanalíticos.

Mas isso é ruim do ponto de vista da confiabilidade: imagine que um defeitinho momentâneo no disco ou na memória RAM (tanto a do computador quanto a do cache do controlador do disco) faça que um único bit seja lido invertido. A decifragem "ampliará" esse "errinho", fazendo que ele se torne um setor inteiro corrompido. Um erro de um byte às vezes se manifesta como um caractere estranho no nome de um arquivo, ou uma permissão ou tamanho incorreto; mas uma corrupção de 512 bytes certamente é bem mais séria e pode tornar os arquivos inacessíveis.

Finalmente, vamos fazer um teste de velocidade: vamos preencher o dispositivo test com zeros. Isso fará com que o sistema cifre esses zeros e salve a versão cifrada no test.img:

# dd if=/dev/zero of=/dev/mapper/test bs=1M count=900
900+0 records in
900+0 records out
943718400 bytes (944 MB) copied, 20.4841 s, 46.1 MB/s

Se você estiver pretendendo montar um NASDEC usando uma rede Fast Ethernet, que no máximo só dá 12,5MB/s, esses 46MB/s são mais do que o suficientes. Mas para o projeto do meu NASDEC, eu considerei esse resultado aquém do esperado, pois é menos da metade dos 110MB/s que uma rede Gigabit Ethernet tipicamente pode dar. Além disso, o problema da ampliação dos erros me deixa muito desconfortável.

Nas seções seguintes, vou detalhar como fiz para obter um desempenho bem melhor e características de propagação de erros bem mais atraentes para nossa aplicação específica. Em rigor, se você está satisfeito com o desempenho obtido e não se preocupa tanto assim com o problema da ampliação dos erros, poderia parar por aqui. Mas se você quer mais desempenho e nenhuma ampliação de erros, continue lendo, pois vou mostrar um esquema mais sofisticado que nos permitirá atingir esses objetivos.

A propósito: você pode fazer todos esses testes no seu próprio computador, sem precisar montar um NASDEC. Basta que ele tenha 2GB de memória ou mais ou que você reduza o tamanho do arquivo test.img de 900MB para algo que caiba com sobra no seu /dev/shm.

Uma Solução Melhor com o Salsa20

O kernel do Linux inclui uma biblioteca de algortimos criptográficos chamada CryptoAPI – algoritmos de cifragem/decifragem simétrica, algoritmos de hash, geradores de números aleatórios e códigos detectores de erros. Cada algortimo vem em um módulo separado do kernel e você pode ver quais algoritmos estão carregados através do comando cat /proc/crypto.

Entre os algoritmos que o kernel suporta, está um dos meus preferidos: o Salsa20, inventado pelo lendário matemático norte-americano Daniel J. Bernstein, também conhecido como "DJB". O Salsa20 foi explicitamente projetado para ser muito eficiente nas CPUs modernas, dando o dobro do desempenho do AES ao mesmo tempo que usa uma chave criptográfica duas vezes maior.

Além disso, o Salsa20 é um algoritmo tipo "stream": cifra a mensagem byte a byte fazendo XOR com a saída do seu gerador de números pseudo-aleatórios controlados pela chave. O resultado disso é que se um bit mudar na mensagem cifrada, um único bit mudará na mensagem decifrada e vice versa. Ele não "amplia" erros, como fazem os algoritmos de bloco em geral e o AES em particular.

Para usar o Salsa20 com o dm_crypt/cryptsetup, deveria ser tão simples quanto especificá-lo na linha de comando:

# echo senha | cryptsetup -c salsa20-plain-plain64 create test /dev/loop0

As três partes do nome do esquema salsa20-plain-plain64 são, separadas por traço:

  • Primeiro o nome do algoritmo, salsa20 no nosso caso;
  • Em segundo lugar, nome do modo de encadeamento. Usamos plain porque o Salsa20, sendo um algoritmo de "stream", dispensa encadeamento. Se você estivesse usando um algoritmo de bloco, poderíamos colocar CBC, XTS, LRW, etc.
  • Por último, qual o gerador de vetores iniciais. Aqui, usamos o plain64, que nada mais é que um contador de 64 bits com a posição do setor relativa ao início do disco.

Se você tentar rodar o comando acima no seu Linux, quase certamente ele dará o seguinte erro:

device-mapper: reload ioctl failed: No such file or directory

Normalmente isso quer dizer que o kernel não tem o algoritmo de criptografia que você pediu. Mas, nesse caso, é uma limitação no dm-crypt: ele não suporta o segundo argumento do esquema de cifragem como sendo plain. Fiz um patch pro kernel (na seção "Resumo da Ópera" mais adiante há um link de onde você pode baixar o meu kernel já prontinho na forma de um pacote Debian, sem precisar compilar nada) pra corrigir esse erro e eis o resultado que obtive:

# cryptsetup remove test >/dev/null
# echo senha | cryptsetup -c salsa20-plain-plain64 create test /dev/loop0
# dd if=/dev/zero of=/dev/mapper/test bs=1M count=900
900+0 records in
900+0 records out
943718400 bytes (944 MB) copied, 10.7086 s, 88.1 MB/s

Com 88,1 MB/s eu já poderia até me dar por satisfeito. Mas dá pra fazer algo ainda melhor.

Uma Solução Melhor Ainda com o Salsa20/12

O algortimo Salsa20 tem diversas variantes. A variante padrão chama-se "Salsa20/20": o número após a barra diz o número de rodadas realizadas pelo algoritmo. Quando o DJB concebeu o Salsa20, ele estipulou o número de rodadas em 20 para dar uma margem de segurança absurdamente ampla. Estipulou, ainda, que os usuários poderiam diminuir o número de rodadas para ganhar mais velocidade, às custas da margem de segurança. Ele estipulou duas principais variantes: Salsa20/8, com 8 rodadas; e Salsa20/12, com 12 rodadas. O comitê do projeto eSTREAM referendou a versão com 12 rodadas como a que oferece melhor relação desempenho versus segurança.

Fiz outro pequeno patch (que, para simplificar, empacotei neste mesmo arquivo) no kernel para implementar o Salsa20/12. Recompilei e reinstalei o kernel e fui pro tão esperado teste:

# cryptsetup remove test >/dev/null
# echo senha | cryptsetup -c salsa20_12-plain-plain64 create test /dev/loop0
# dd if=/dev/zero of=/dev/mapper/test bs=1M count=900
900+0 records in
900+0 records out
943718400 bytes (944 MB) copied, 8.58697 s, 110 MB/s

Sucesso!1 Pelo menos em princípio, temos agora um algortimo de criptografia rápido o bastante para aguentar a velocidade de rede gigabit.

Na prática, a vida não vai ser tão bela assim. Aqui, medimos tudo direto para a RAM; os demais componentes da solução final – disco, processos, rede – vão impor seus custos.

Eu testei também usar o Salsa20/8 ao invés do Salsa20/12. Todavia, a diferença é pequena2 (só uns 10MB/s a mais) e a margem de segurança extra do Salsa20/12 me deixa mais confortável.

Resumo da Ópera

Receita 1: Se você está montando o seu próprio NASDEC e se contenta em usar o AES com o kernel padrão do Debian, precisaremos seguir um procedimento em três grandes passos: instalar os utilitários necessários, depois a inicialização do disco, depois a criação do volume cifrado em si.

A instalação dos utilitários é trivial:

# apt-get install cryptsetup pv

Em seguida, vamos à preparação do disco: antes de ativar a cifragem do disco em si, vamos primeiro "inicializá-lo", sobrescrevendo todo o disco com números aleatórios – isso impedirá um eventual atacante de descobrir quais áreas estão ocupadas e quais estão vazias (o que às vezes pode ser uma pista importante).

Você só precisará fazer essa inicialização na primeira vez em que for montar o sistema. Tenha consciência que essas operações destroem todo o conteúdo anterior do disco, portanto, não as refaça após ter guardado dados importantes no disco. Se você já tem algo importante gravado no HD que quer usar, faça um backup antes.

Feitas essas salvaguardas, execute os seguintes comandos (troque todas as ocorrências de /dev/sda nos comandos a seguir pelo nome do dispostivo que corresponde ao disco que quer usar – tome cuidado nisso, porque qualquer erro ou distração pode levá-lo a inadvertidamente destruir dados que queria preservar).

# size=$(blockdev --getsize64 /dev/sda)
# (openssl rand $[size/2] & openssl rand $[size/2]) | pv -s $size > /dev/sda

O primeiro comando obtém a capacidade exata em bytes do disco rígido e o seguindo é que realmente preenche todo o disco rígido com números aleatórios. Esse segundo comando3 leva horas para terminar – no meu NASDEC, com disco de 3TB, esse comando demoraria quase dois dias.

Feita a inicialização, crie o dispositivo cifrado usando o comando cryptsetup:

# cryptsetup --verify-passphrase esda /dev/sda
Enter passphrase: (digite sua frase-senha aqui)
Verify passphrase: (redigite a senha para confirmar)

Você precisará usar esse comando sempre que quiser "abrir" o disco cifrado, tanto para criar o dispositivo inicialmente quanto para acessá-lo posteriormente (nesse caso, você poderá omitir o --verify-passphrase).

Ao usá-lo pela primeira vez, invente uma boa frase-senha (eu recomendo 16 caracteres, pelo menos) e NÃO A ESQUEÇA. Se você esquecê-la, seus dados estarão perdidos para sempre e não há nada que eu, nem o DJB, nem o CEPESC, nem a DPF, nem o FBI e nem mesmo a NSA possamos fazer para reavê-los4. Se você achar que há risco de esquecê-la, anote-a e guarde o papel em um cofre ou algum lugar que considere seguro.

Uma fez feito isso, você pode criar um sistema de arquivos no /dev/mapper/esda ou usar o comando pvcreate para marcá-lo como volume físico do LVM.

Receita 2: Se você pretende usar o Salsa20/12 com o meu kernel personalizado, teremos três principais passos a cumprir: primeiro, baixar e instalar o kernel e os utilitários; depois, inicializar o disco; e, por fim, criar o dispositivo cifrado propriamente dito. As duas primeiras etapas só precisam ser feitas uma única vez.

Inicialmente, baixe+instale a chave pública do repositório da Tempest (caso queira verificar, o fingerprint é 6360 014B 880C 2027 7B59 8B30 BC2B 52D2 BC66 940D) e acrescente o localizador do repositório à configuração do seu apt:

# wget -O- http://debian.tempest.com.br/tempest-deb-key.asc | apt-key add -
# echo deb http://debian.tempest.com.br/ packages main > \
  /etc/sources.list.d/tempest.list
# apt-get update

Feito isso, é só instalar o kernel5 (vamos aproveitar e instalar logo também os utilitários pv e cryptsetup):

# apt-get install pv cryptsetup linux-image-2.6.38.8-vs2.3.0.37-rc17

Reinicie o sistema, confira que tudo esteja funcionando bem e dê entrada novamente como root.

A segunda etapa de preparação consiste em preencher o disco com números aleatórios. Isso impedirá um eventual atacante de discernir entre espaço ocupado e espaço vazio (o que, às vezes, já pode ser uma pista importante). Em princípio, poderíamos usar o mesmo método mostrado na "Receita 1" acima, usando o gerador de números aleatórios do openssl. Mas há um meio mais rápido, apesar de mais rebuscado:

# size=$(blockdev --getsize64 /dev/sda)
# echo 0 $[size/512] zero | dmsetup create bigzero
# openssl rand -base64 32 | \
  cryptsetup -c salsa20_12-plain-plain64 create bigrand /dev/mapper/bigzero
# pv -s $size < /dev/mapper/bigrand > /dev/sda

Os comandos acima criam um dispositivo de bloco virtual (ele não existe de verdade, é emulado em software pelo target "zero" do device-mapper) chamado /dev/mapper/bigzero. Ele é como o /dev/null: se você tentar ler qualquer posição dele, ele retornará apenas zeros.

Em seguida, usamos o cryptsetup para criar um dispositivo chamado /dev/mapper/bigrand usando uma chave escolhida aleatoriamente (e que é posteriormente descartada). Isso equivale a criar um dispositivo cheio de números aleatórios.

Por fim, usamos o utilitário pv para copiar todos esses números aleatórios do /dev/mapper/bigrand para por sobre o /dev/sda, com uma barra de progresso bonitinha. Aqui no meu NASDEC, o processo se completou em pouco mais de dez horas – bem mais rápido do que os dois dias que seriam necessários se tivéssemos usado o openssl rand descrito na "Receita 1".

Lembre-se: você só precisará fazer essa inicialização uma única vez. Esse processo é destrutivo, inutilizando tudo que havia antes no seu disco. Portanto, se há nele algo que você quer manter, faça um backup antes. Lembre-se de trocar todas as ocorrências de /dev/sda pelo nome correto do dispositivo que representa o disco no seu sistema. Muita atenção e cuidado para não errar: qualquer deslize poderá lhe fazer perder dados.

Isso feito, basta agora criar o dispositivo cifrado em si. O procedimento é quase o mesmo do da "Receita 1", exceto que aqui vamos explicitar o esquema de criptografia através da opção -c do cryptsetup.

# cryptsetup --verify-passphrase -c salsa20_12-plain-plain64 esda /dev/sda
Enter passphrase: (digite sua frase-senha aqui)
Verify passphrase: (redigite a senha para confirmar)

Ao usá-lo pela primeira vez, invente uma boa frase-senha e não a esqueça: você a reusará toda vez que remontar o circo para acessar os dados cifrados (nesse caso, você poderá omitir o --verify-passphrase). Em um artigo futuro, vou passar um script que arma todo o circo automaticamente.

Outro detalhe de extrema importância para quem estiver tentando criar seu próprio NASDEC: se você estiver usando o Salsa20 (qualquer das variantes), use frases-senha diferentes em cada máquina. Se você usar a mesma frase-senha, poderá ficar vulnerável a um ataque clássico.

Modo Cru vs Modo LUKS

Em tudo que fizemos acima, usamos o chamado "modo cru" do dm_crypt, onde há uma correspondência biunívoca exata entre os blocos do dispositivo cifrado e do às claras. Outra característica é que a frase-senha é usada para gerar a chave criptográfica. Isso tem as seguintes consequências:

  • O cryptsetup não tem como saber se você errou a frase-senha. Se você tiver errado a frase-senha, o cryptsetup criará o dispositivo cifrado normalmente, mas seu conteúdo parecerá ser "lixo". O conteúdo correto só aparecerá se você digitar a frase-senha correta.

  • Aliás, o cryptsetup não guarda em canto nenhum qual foi o algoritmo de cifragem que você usou. Se você, por exemplo, criou o dispositivo usando a frase-senha "senha" e o algoritmo Salsa20/12 e depois tentar recriá-lo com o AES e a mesma senha "senha", ele aparentemente vai funcionar, mas, novamente, o conteúdo lhe parecerá corrompido.

  • Não há como mudar a frase-senha, a não ser criando um novo dispositivo e copiando tudo do antigo pro novo.

O dm_crypt oferece um outro modo, chamado LUKS, que remedia esses problemas. Resumidamente, ele reserva alguns dos blocos de disco para armazenar um "descritor" (chamado "partition header" ou "phdr" na documentação), que contém a chave criptográfica (escolhida aleatoriamente e que a gente nem vê), identificador do algoritmo de cifragem e alguns outros "dados burocráticos". A chave criptográfica é cifrada usando a frase-senha (há suporte para várias frases-senha) quando o dispositivo é inicializado (com o subcomando luksFormat do cryptsetup). Quando você "abre" o dispostivo (com o subcomando luksOpen), ele usa a frase-senha que você digitou para decifrar a chave criptográfica. Se o cabeçalho do descritor não bater com o esperado, ele sabe que você errou a frase-senha.

Isso também viabiliza trocar a frase-senha sem ter de recriar todo o dispositivo: tudo que o cryptsetup precisa fazer é te pedir a frase-senha atual, decifrar a chave criptográfica principal usando a frase-senha que você deu, e recifrá-la usando a nova frase-senha. O grosso dos dados continuam cifrados com a mesma chave; só o que precisa mudar é o descritor.

A razão pela qual eu não usei o modo LUKS é que, se exatamente o setor do disco que armazena o descritor for danificado, perde-se a chave de cifragem principal e com isso todo o conteúdo do disco se tornará inacessível (tal como se tivéssemos esquecido a frase-senha). Então, por questões de confiabilidade, preferi optar pelo modo cru6.

Conclusões e Próximos Passos

A escolha de usar o modo cru, ao invés do mais sofisticado modo LUKS, bem como a adoção do algoritmo Salsa20/12, podem causar estranheza a muita gente, pois divergem do "padrão". Todavia, creio que representam um equilíbrio bem balanceado entre três critérios conflitantes: sigilo versus confiabilidade versus desempenho. Creio que a solução que escolhi provê ótimo sigilo, mas, precisamos lembrar, o objetivo do NASDEC é durar dez anos ou mais. Por isso, precisamos priorizar a confiabilidade, pois sabemos que falhas de hardware fatalmente irão ocorrer, cedo ou tarde. Bacana é conseguir fazer tudo isso sacrificando relativamente pouco desempenho.

Claro, esse equilíbrio deixou a solução mais complicada, requerendo planejamento e sutilezas fora do "padrão" e às quais muita gente não se atém. Espero que este artigo tenha lhe recompensado com um entendimento mais refinado e profundo sobre o funcionamento do dm-crypt, os prós e contras que as soluções de cifragem de disco impõem e como encaixá-las dentro do "plano geral" de uma solução de armazenamento durável.

No próximo artigo desta série, vou mostrar como o restante da pilha de armazenamento é fácil: a criação do LVM é brincadeira de criança e o GlusterFS provavelmente é o software de clustering mais fácil de usar que eu já vi. O que vai dar um certo trabalhinho serão uns truques sujos para obter mais desempenho e o esquema de compartimentalização usando vservers.



1 Não se assuste se você não obtiver exatamente 110MB/s; a minha média foi, na realidade uns 105MB/s. É normal o valor exato variar mais ou menos 10MB/s em relação à média. Lembre-se também que o desempenho exato depende do seu disco, da sua placa-mãe, da sua memória, etc. [ voltar ]

2 Esse efeito parece ser causado pelo próprio kernel, pois uma implementação userspace do Salsa20 em memória dá velocidades mais de dez vezes maiores. [ voltar ]

3 Note que eu uso duas instâncias do openssl rand para aproveitar o fato que as máquinas do NASDEC são "dual-core" e, com isso, duplicar a velocidade. Se sua máquina for single-core e/ou você usar uma única instância, vai demorar duas vezes mais tempo. [ voltar ]

4 Pra ser tecnicamente preciso, isso só é verdade se você não lembrar de nenhum caractere da frase-senha; se você lembrar da maior parte dela e só tiver esquecido alguns poucos caracteres, pode ser possível reavê-la em tempo/custo razoável. [ voltar ]

5 No repositório da Tempest você também poderá encontrar os pacotes com os fontes e os cabeçalhos. [ voltar ]

6 Uma outra alternativa seria tirar um backup do descritor da partição LUKS. Nem seria complicado de fazer, mas, mesmo assim, eu achei o modo cru mais simples de usar. [ voltar ]

Partes anteriores

Próximas partes

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.
Adriano | 2011-09-20 08:26:27 | permalink | topo

pra quem disse que nao ia se importar com o desempenho... gastou um post enorme nisso.. mas eh importante mesmo...