Estou usando o Almalinux, caso isso seja relevante aqui.
Usamos LUKS2 no disco local, com LVM no topo - então /dev/sda1 e 2 não são criptografados, mas /dev/sda3 é criptografado e usado para o sistema operacional.
Também usamos clevis/tang para fazer a descriptografia automática (e isso está funcionando bem).
Quando construímos via kickstart, definimos uma senha temporária ao criptografar e construir - e então, quando terminamos a construção inicial, usamos o ansible para definir senhas de boa qualidade, de acordo com nossas práticas de proteção/segurança.
Então isso envolve um script que faz o equivalente a:
cryptsetup luksOpen -S 0 --test-passphrase /dev/sda3 && cryptsetup luksChangeKey -S0 --force-password --batch-mode
(E, claro, a senha de compilação 'padrão' é testada e substituída por uma do nosso sistema de gerenciamento de chaves).
Isso começou a falhar recentemente e estou um pouco perplexo sobre o que pode estar dando errado.
Definitivamente funciona em nossa 'compilação' inicial, que usa uma imagem de compilação um pouco desatualizada, mas depois mudamos dnf update
para a revisão atual e agora não conseguimos mais usar luksChangeKey ou luksAddkey.
# Adding new keyslot 2 by passphrase, volume key provided by passphrase (-1).
# Selected keyslot 2.
# Keyslot 0 priority 1 != 2 (required), skipped.
# Keyslot 1 priority 1 != 2 (required), skipped.
# Trying to open LUKS2 keyslot 0.
# Running keyslot key derivation.
# Reading keyslot area [0x8000].
# Acquiring read lock for device /dev/sda3.
# Opening lock resource file /run/cryptsetup/L_8:3
# Verifying lock handle for /dev/sda3.
# Device /dev/sda3 READ lock taken.
# Reusing open ro fd on device /dev/sda3
# Device /dev/sda3 READ lock released.
# Verifying key from keyslot 0, digest 0.
# Keyslot 2 assigned to digest 0.
# Trying to allocate LUKS2 keyslot 2.
# Found area 548864 -> 806912
# Running argon2id() benchmark.
# PBKDF benchmark: memory cost = 65536, iterations = 4, threads = 4 (took 72 ms)
# PBKDF benchmark: memory cost = 227555, iterations = 4, threads = 4 (took 264 ms)
# PBKDF benchmark: memory cost = 1048576, iterations = 6, threads = 4 (took 1982 ms)
# Benchmark returns argon2id() 6 iterations, 1048576 memory, 4 threads (for 512-bits key).
# JSON does not fit in the designated area.
# Not enough space in header json area for new keyslot.
# Rolling back in-memory LUKS2 json metadata.
# Releasing crypt device /dev/sda3 context.
# Releasing device-mapper backend.
# Closing read only fd for /dev/sda3.
Command failed with code -1 (wrong or missing parameters).
Gostaria de saber se alguém pode me ajudar a entender o que está errado aqui e o que preciso fazer para remediar.
Existe uma opção de 'tamanho do cabeçalho inicial' que eu possa especificar em nossas compilações? Ou um parâmetro para cryptsetup? Ou estou "apenas" encontrando um bug? (Mas não estou convencido de que algo tão amplamente usado como cryptsetup
vai ter um bug do tipo 'ninguém pode mudar suas senhas')
É assim que o cabeçalho LUKs aparece em um host de exemplo.
LUKS header information
Version: 2
Epoch: 5
Metadata area: 16384 [bytes]
Keyslots area: 16744448 [bytes]
UUID:
Label: (no label)
Subsystem: (no subsystem)
Flags: (no flags)
Data segments:
0: crypt
offset: 16777216 [bytes]
length: (whole device)
cipher: aes-xts-plain64
sector: 512 [bytes]
Keyslots:
0: luks2
Key: 512 bits
Priority: normal
Cipher: aes-xts-plain64
Cipher key: 512 bits
PBKDF: argon2id
Time cost: 9
Memory: 1048576
Threads: 4
Salt:
AF stripes: 4000
AF hash: sha256
Area offset:32768 [bytes]
Area length:258048 [bytes]
Digest ID: 0
1: luks2
Key: 512 bits
Priority: normal
Cipher: aes-xts-plain64
Cipher key: 512 bits
PBKDF: pbkdf2
Hash: sha256
Iterations: 1000
Salt:
AF stripes: 4000
AF hash: sha256
Area offset:290816 [bytes]
Area length:258048 [bytes]
Digest ID: 0
Tokens:
0: clevis
Keyslot: 1
Digests:
0: pbkdf2
Hash: sha256
Iterations: 105025
Salt:
Digest:
Estou esquecendo de algo profundo? Como dito, tenho certeza de que isso funciona se eu não "atualizar" a caixa para um kernel mais novo, então minha solução alternativa atual é construí-la, recodificar manualmente e então continuar a atualizá-la, mas isso parece... abaixo do ideal.
cryptsetup versões 2.6.0 e 2.7.2
head -c 1M /dev/sda3 | strings -n 128
reformatado:
{
"keyslots": {
"0": {
"type": "luks2",
"key_size": 64,
"af": {
"type": "luks1",
"stripes": 4000,
"hash": "sha256"
},
"area": {
"type": "raw",
"offset": "32768",
"size": "258048",
"encryption": "aes-xts-plain64",
"key_size": 64
},
"kdf": {
"type": "argon2id",
"time": 7,
"memory": 1048576,
"cpus": 4,
"salt": "(removed)"
}
},
"1": {
"type": "luks2",
"key_size": 64,
"af": {
"type": "luks1",
"stripes": 4000,
"hash": "sha256"
},
"area": {
"type": "raw",
"offset": "290816",
"size": "258048",
"encryption": "aes-xts-plain64",
"key_size": 64
},
"kdf": {
"type": "pbkdf2",
"hash": "sha256",
"iterations": 1000,
"salt": "(removed)"
}
}
},
"tokens": {
"0": {
"type": "clevis",
"keyslots": [
"1"
],
"jwe": {
"ciphertext": "(removed)",
"encrypted_key": "",
"iv": "(removed)",
"protected": "",
"tag": "(removed)"
}
}
},
"segments": {
"0": {
"type": "crypt",
"offset": "16777216",
"size": "dynamic",
"iv_tweak": "0",
"encryption": "aes-xts-plain64",
"sector_size": 512
}
},
"digests": {
"0": {
"type": "pbkdf2",
"keyslots": [
"0",
"1"
],
"segments": [
"0"
],
"hash": "sha256",
"iterations": 88086,
"salt": "(removed)",
"digest": "(removed)"
}
},
"config": {
"json_size": "12288",
"keyslots_size": "16744448"
}
}
Edição 2: A história se complica: luksHeaderBackup e depois uma restauração falha.
Mas matar o 'slot 0' e adicionar uma chave ainda funciona. (Supondo que o 'slot 1' você pode extrair, mas nos meus casos clevis-luks-pass
funciona bem o suficiente)
Resumo do meu relatório de 'problema' contra o cryptsetup que pode ser encontrado aqui: https://gitlab.com/cryptsetup/cryptsetup/-/issues/924
A raiz desse problema está relacionada ao dimensionamento padrão do cabeçalho LUKS ao criar via Kickstart e ao tamanho da pegada que o clevis/tang ocupa.
Especificamente, quando construí essas caixas, usei a diretiva kickstart:
Isso aplica os padrões para luksFormat (no almalinux, não tenho certeza se esse é sempre o padrão) de 16 MB de cabeçalho LUKS, mas 16k - 4k para a área de metadados JSON.
Normalmente isso é bom, porque um slot de chave luks não tem muitos metadados, então é um padrão sensato no geral.
No entanto, usamos clevis/tang para desbloquear e configurar - durante o kickstart - um
clevis luks bind
Usamos 6 servidores tang — 3 por site — por motivos de resiliência.
Nosso script de pós-instalação do kickstart inclui:
No entanto, temos 6 servidores tang definidos para o
sss
desbloqueio, o que pode ser anormalmente grande. (Mas era para resiliência de site duplo - realmente não queríamos estar em um lugar onde toda a nossa empresa não conseguisse inicializar sem múltiplas falhas)Os metadados da manilha são consideravelmente maiores e adicionam cerca de 10k à área de metadados, então se verificarmos a ocupação:
A maior parte disso está na chave tokens.0.jwe.protected, que tem cerca de 10k. (No post acima, ela foi cortada para resumir).
Por exemplo:
E compare isso com a área de metadados configurada:
Então temos apenas 'alguns' bytes livres, e isso não é suficiente para uma nova chave.
Isso costumava funcionar em nossas implantações 9.1, porque
cryptsetup
faria a substituição no local, e essa era a raiz da minha confusão - ainda consigo reproduzir um luksChangeKey bem-sucedido imediatamente após a inicialização, compilando em um initrd 9.1 e um repositório 9.2:Não estou claro por que isso é diferente, pois este é o cryptsetup 2.6.0 com as mesmas bibliotecas de criptografia, mas no meu exemplo acima o ChangeKey usa
E não temos espaço para outro slot de chave, e nunca tivemos, porque clevis/tang ocupa mais do cabeçalho JSON, e é por isso que falha.
Portanto, as soluções para este problema são:
Reformate o disco com uma área de metadados json maior.
Por exemplo, o padrão de 16k significa 16k - 4k disponíveis, que é o que temos.
Esta seria uma opção destrutiva, exigindo um backup/restauração ou reconstrução do disco.
Inconvenientemente, a
part
diretiva kickstart não suporta nenhuma das opções necessárias para o--encryption
sinalizador, então você teria que fazer algo um pouco mais complicado com sua lógica de criação de partição, provavelmente por meio de um%pre
script para selecionar as unidades, o tamanho da partição e passar os parâmetros necessários para cryptsetup luksFormat diretamente.Despejar o cabeçalho, redimensioná-lo e restaurá-lo.
Não tenho um processo detalhado, mas deve ser possível
cryptsetup luksDump --dump-volume-key
criar um novo cabeçalho do tamanho apropriado com as mesmas configurações: parâmetros de criptografia (cifra, modo, tamanho do setor), uuid do dispositivo LUSK2 e mesmo deslocamento de dados - e, claro, chave de volume - e então substituir o cabeçalho antigo pelo novo.As páginas de manual cryptsetup-luksDump e cryptsetup-luksFormat serão uma referência aqui.
Com um GRANDE aviso de que isso é um pouco... perigoso, aqui está o que parece ter funcionado com sucesso:
Cruze os dedos e reinicie, porque se você errou, provavelmente deixou seu sistema irrecuperável. (Quero dizer, talvez você tenha feito backup do volkey/header em outro dispositivo, mas tenha em mente que isso é um risco à segurança, então tenha cuidado).
Verifique o sucesso usando luksChangeKey na senha temporária que você definiu no cabeçalho 'new', para alterá-la para qualquer senha que você normalmente usaria que fosse mais apropriada. (Se ele inicializou, você sabe que clevis/tang funcionaram).
Basta conviver com dois slots de chave e usar a opção de descriptografia do Clevis para alterar a "outra" chave quando precisar.
Irá extrair a senha que o clevis gera e usa, para que você possa usá-la para fazer um
luksKillSlot
seguido por umluksAddKey
.Isso pode não ser sempre adequado, mas no cenário acima - onde nossa frase-senha luks slot 0 é um caso de fallback para quando o mecanismo clevis/tang falha, será viável. Você ainda não poderá adicionar mais chaves.