O comando trivial rm -rf destrói meu sistema operacional em uma máquina de teste

1

Eu uso o Bash 4.3.48 (1) e executei o seguinte padrão de comando em uma máquina VPS de teste:

rm -rf ${drt}/${pma}*

Este comando apagou todo o sistema operacional (Ubuntu). Isso ficou evidente ao executar cd / , que não retornou nada além do seguinte erro:

bash: cd /: No such file or directory

Em uma análise mais profunda, isso aconteceu porque as duas variáveis no comando original acima não foram declaradas:

  1. Eu criei um arquivo chamado ~/repoName/assignments_variables.sh que contém uma lista de variáveis exportadas (incluindo drt e pma ).

  2. Em vez de executar source /etc/bash.bashrc , executei /etc/bash.bashrc , o que estava errado, porque esse arquivo é relevante apenas para a sessão atual (embora possa ser usado para executar posteriormente arquivos que por si mesmos executam dados em subsessões) .

Agora que está claro para mim que o caminho ruim é a causa geral do problema, gostaria de mergulhar um pouco mais e perguntar:

Por que o rm -rf ignorou as expansões de variável incorretas e continuou indo para o /* ? Eu sei que rm -rf deve excluir um diretório somente se ele o encontrar, portanto, não deve depender de partes do caminho de um diretório inexistente (como /* que causou a exclusão do arquivo operacional). sistema). Como eu poderia melhorar o comando rm -rf para cobrir possíveis casos semelhantes de um caminho ruim (devido a expansões errôneas de variáveis) no futuro?

Existe alguma diretiva Bash para garantir que o Bash nunca expanda as variáveis vazias (até eu desfazer essa diretiva)?

Deixe-me enfatizar: eu costumo trabalhar não apenas com backups, mas com backups duplos. Isso foi realmente um ambiente de teste com backups x3 em segundo plano.

    
por user9303970 11.02.2018 / 05:53

3 respostas

2

Como foi apontado, as duas variáveis drt e pma não estavam presentes no ambiente do seu script, o que significa que o shell as expandiu para strings vazias. O comando resultante foi rm -rf /* (com * expandido para o que estava disponível no diretório raiz).

O próprio diretório raiz não será removido executando rm -rf /* como root. Além disso, eu (e Jesse_b que apontou para mim) estou um pouco confuso com a mensagem de erro

bash: cd /: No such file or directory

A única maneira de provocar essa mensagem de erro é executar o comando "cd /" (observe as aspas). Se / foi realmente removido, a mensagem de erro seria

bash: cd: /: No such file or directory

isaac já deu algumas dicas sobre como evitar que você faça coisas como essa no futuro, mas Eu pensei que eu iria apenas entrar com meus conselhos.

  • Não trabalhe em um prompt do shell de root.
  • Se estiver escrevendo scripts que exigem permissões de root, use sudo no script dos comandos específicos que realmente exigem essas permissões elevadas. Não execute o script inteiro como root, especialmente durante o desenvolvimento.
  • Você geralmente não precisa tocar em /etc/bash.bashrc por qualquer motivo e nunca deve precisar explicitamente obtê-lo. Variáveis de ambiente podem ser criadas
    • do usuário $HOME/.bashrc (se o script for executado a partir de uma sessão interativa) ou
    • no próprio script ou
    • em um arquivo separado (escrito para o script) originado pelo script explicitamente ou
    • em um arquivo apontado pela variável de ambiente BASH_ENV .

Outra medida de segurança que salvou você nesse caso é executar o script em set -u ( nounset ) e possivelmente também em set -e ( errexit ). A opção nounset shell ameaçará a expansão de uma variável não definida como um erro, enquanto a opção errexit shell sairá da sessão do shell imediatamente se um comando sair com um status de saída diferente de zero.

O script

#!/bin/bash -ue

echo "$hello"
echo "world"

nunca produzirá world , mas, em vez disso, provocará a mensagem

script.sh: line 3: hello: unbound variable

antes de terminar. (É o -u que faz a finalização, -e is não faz nada neste exemplo simples ).

    
por 11.02.2018 / 15:29
4

O que é expandido se as variáveis não existem?
Vamos tentar (observe o eco):

$ unset drt pma
$ echo rm -rf ${drt}/${pma}*
rm -rf /bin /boot /dev /etc /hello /home /initrd.img /lib /lib32 /lib64 /libx32 /media /mnt /opt /proc /root /run /sbin /srv /sys /tmp /usr /var /vmlinuz /vmlinuz.old

é todo o sistema (como você já confirmou).
O problema é a expansão feita pelo shell.

prevenção

Como evitamos que isso aconteça?
Impedindo que o shell expandisse variáveis vazias.
A expansão de ${var:?message} produz message se var estiver vazia ou não definida.
E para a execução (erro crítico). Teste:

$ echo rm -rf "${drt:?Missing variable drt}"/"${pma:?Missing variable pma}"*

E esta solução é compatível com POSIX.

    
por 11.02.2018 / 06:36
2

As variáveis não configuradas se interpolam para nada, então o que foi executado foi o glob rm -rf /* , que é tudo sob / , o que é ruim como você descobriu.

Adicione a verificação de erros. E, também, cite o "$variables" porque senão o shell faz um split-glob neles e então coisas estúpidas podem acontecer quando o $variables contém um espaço ou outros caracteres problemáticos. Especialmente quando um rm -rf está envolvido.

if [[ -z "$drt" ]]; then
    echo >&2 "drt variable not set ??"
    exit 1
fi
if [[ -z "$pma" ]]; then
    echo >&2 "pma variable not set ??"
    exit 1
fi
rm -rf "${drt}"/"${pma}"*

(Ou use uma linguagem menos problemática para programar em ...)

    
por 11.02.2018 / 06:05