Storage criptografado em roteador residencial

De Área31 Hackerspace
Membro do hackerspace satisfeito com seu storage residencial criptografadinho com LUKS.
Autor: 
* Coffnix

"Home Network Defense"

Utilize criptografia pra tudo

Motivo

Atualmente, existem muitas soluções de armazenamento projetadas para compartilhar arquivos em redes domésticas e empresariais. No entanto, muitas delas não priorizam mecanismos robustos de segurança. Portanto, para atender aos padrões de segurança desejados, é frequentemente necessário recorrer a soluções de terceiros, a nível de sistema operacional. Um exemplo notável é a criptografia de arquivos usando LUKS.

Requisitos

Adquira um storage que ofereça um volume de rede por meio de NFS ou SMB/CIFS, bem como um dispositivo externo USB. MacOS que suporte HFS+ (opcional). Linux com suporte a LUKS e ao módulo hfsplus.

Configuração do Storage ou Roteador

Para este exemplo, utilizaremos o roteador Archer GX90 (AX6600).


Crie um novo usuário e defina uma senha robusta. Altere a senha do usuário guest ou, se preferir, desative-o.


Configuração do Dispositivo de Armazenamento USB

A grande maioria dos roteadores modernos suporta os sistemas de arquivos HFS+, NTFS, FAT32 e exFAT. Dada a necessidade de otimização de performance e aprimoramento da segurança dos dados, optaremos pelo HFS+. No entanto, se você encontrar um dispositivo que também suporte EXT4, este é igualmente recomendável, sendo um sistema de arquivos robusto e adequado para essa finalidade.

Formatação do Dispositivo de Armazenamento

Para formatar o dispositivo, o utilitário Diskutil no macOS é altamente recomendado. No entanto, ferramentas como parted, cfdisk e outras podem ser alternativas viáveis. Após a formatação, monte o disco em seu sistema local e proceda à criação de um arquivo vazio com as dimensões desejadas. Em nosso exemplo, trabalhamos com um disco USB de 1TB destinado a backups do Time Machine, e o arquivo criado possui 400GB.

A escolha de bs=10M foi feita considerando que o cliente deste volume de disco será um Raspberry Pi com processador ARMv8 rodando Linux, se comunicando via cabo de rede (RJ-45 Gigabit LAN 1000Mb/s) sob protocolo CIFS, com um roteador com processador Broadcom, ou seja, ambos bem lentos. Configurações como bs=512 ou bs=1M poderiam resultar em consumo elevado de CPU e rede, comprometendo a performance. Essas decisões exigem que você considere que o tempo de sistema da CPU é inversamente proporcional ao valor do block size. Isto é, quanto menor o bs, mais chamadas de sistema são geradas pelo comando dd e pelo uso em si do volume de disco.

Para sistemas de armazenamento mais robustos, com clientes de alto desempenho, pode ser benéfico experimentar valores menores para bs, como bs=1M. Em situações específicas com dispositivos de baixo desempenho, como cartões MMC, já utilizei valores extremamente reduzidos, como bs=4 (sim, apenas 4 bytes), atingindo taxas de transferência de até 12MB/s. Em contextos de HDD SATA, SCSI ou SAS ou para criação de imagens para LiveCD, geralmente emprego bs=512.


Execute o comando abaixo para criar o arquivo de imagem de disco. Este procedimento é aplicável tanto no macOS quanto no Linux. Se necessário, ajuste o caminho de destino em of=/Volumes/wd-1tb/storage_file.img para refletir o diretório correto de montagem:

root # dd if=/dev/zero of=/Volumes/wd-1tb/storage_file.img bs=10M count=40960


Após criar o arquivo, desmonte o disco e conecte-o ao roteador via porta USB. Ative o compartilhamento SMB e o Time Machine caso deseje utiliza-lo.


Monte o volume CIFS no Linux

Para acessar os recursos compartilhados via CIFS fornecidos pelo roteador, siga os passos abaixo. No exemplo em questão, o volume designado pelo roteador é "G" e seu endereço IPv4 é 192.168.20.1. É fortemente recomendado optar pelo protocolo CIFS em detrimento do SMBFS, dadas as vantagens em termos de estabilidade, eficiência no uso de cache e outros benefícios. O protocolo suportado pelo roteador é vers=2.0. Caso esteja utilizando um roteador mais antigo, você pode tentar usar vers=1.0, embora isso seja desaconselhado devido às vulnerabilidades de segurança associadas a essa versão. Adicionalmente, sugiro não configurar a montagem automática via /etc/fstab, uma vez que isso pode resultar em complicações durante o processo de inicialização.

root # mount -t cifs //192.168.20.1/G /tp-share -o username=coffnix,password=area31@lalala,iocharset=utf8,vers=2.0


Caso deseje, adicione a seguinte linha no /etc/fstab:

   /etc/fstab
//192.168.20.1/G /tp-share cifs username=coffnix,password=area31@lalala,iocharset=utf8,vers=2.0,noauto 0 0


Criação e Montagem da Imagem Encriptada com LUKS

No contexto da criptografia, um dos aspectos cruciais é o tratamento adequado dos cabeçalhos. Assim, para maximizar a segurança dos seus dados, é imperativo separar o cabeçalho (header) da imagem LUKS do corpo principal da imagem. Esse cabeçalho deve ser armazenado localmente no diretório /etc/area31/headerstore. Ao configurar seu sistema, sempre opte por uma senha forte e robusta, resistente a ataques de força bruta.

A segurança robusta dos dados armazenados frequentemente requer um equilíbrio entre proteção e desempenho. O Raspberry Pi, equipado com o processador ArmV8 Cortex-A72, embora seja um dispositivo capaz, não possui as otimizações e acelerações específicas encontradas em CPUs de desktop para criptografia, como o AES-NI. Essas acelerações são essenciais para um desempenho ágil na criptografia, e sua ausência torna o processo mais lento e intensivo.

Dado esse cenário, é preciso escolher algoritmos de criptografia que sejam não apenas seguros, mas também otimizados para tal hardware. Nesse contexto, a cifra xchacha20 é uma excelente escolha. Ela é conhecida por seu desempenho equilibrado em CPUs e SoCs sem aceleração AES. Combinada com a função de hash SHA-256, que é otimizada para arquiteturas de 64 bits, essa combinação oferece um equilíbrio entre segurança robusta e desempenho aceitável, tornando-a ideal para entusiastas de segurança cibernética.

root # mkdir -p /etc/area31/headerstore ; cryptsetup luksFormat --force-password --type luks2 --cipher xchacha20,aes-adiantum-plain64 --hash sha256 --key-size 256 --iter-time 5000--pbkdf argon2i --align-payload 8192 /tp-share/storage_file.img --align-payload 8192 --header /etc/area31/headerstore/header.img

Caso use CPU com suporte a AES opte por usar algo mais seguro:

root # mkdir -p /etc/area31/headerstore ; cryptsetup luksFormat --force-password --type luks2 --cipher aes-xts-plain64 --hash sha512 --key-size 512 /tp-share/storage_file.img --align-payload 8192 --header /etc/area31/headerstore/header.img


Em seguida, monte o volume LUKS utilizando a senha definida anteriormente:

root # cryptsetup open --header=/etc/area31/headerstore/header.img /tp-share/storage_file.img storage_file_luks

Caso queira montar o volume sem utilizar senha, basta criar um arquivo de chave para o LUKS:

root # mkdir -p /etc/area31/keys ; dd bs=512 count=4 if=/dev/random of=/etc/area31/keys/storage_file_luks.key

Você também pode criar o arquivo de chave utilizando openSSL:

root # mkdir -p /etc/area31/keys ; openssl genrsa -out /etc/area31/keys/storage_file_luks.key 4096

Corrija a permissão do arquivo de chave, adicione ao LUKS e monte o volume utilizando a chave criada:

root # chmod 400 /etc/area31/keys/storage_file_luks.key
root # cryptsetup luksAddKey /tp-share/storage_file.img /etc/area31/keys/storage_file_luks.key --header /etc/area31/headerstore/header.img
root # cryptsetup open --key-file /etc/area31/keys/storage_file_luks.key --header=/etc/area31/headerstore/header.img /tp-share/storage_file.img storage_file_luks


Se quiser visualizar as informações detalhadas do LUKS, execute:

root # cryptsetup status storage_file_luks


Ou

root # cryptsetup luksDump /tp-share/storage_file.img --header /etc/area31/headerstore/header.img


Formatação e Montagem do LUKS

O comando mkfs.ext4 é usado para criar um sistema de arquivos EXT4, que é uma versão avançada e amplamente utilizada do sistema de arquivos extensível. Ao criar um sistema de arquivos, especialmente um de grande tamanho, como uma imagem de 400GB gerada pelo dd, a inicialização padrão do sistema de arquivos pode ser um processo demorado.

A opção -E lazy_itable_init é particularmente útil neste contexto. Quando esta opção é ativada, a inicialização das tabelas de inode do sistema de arquivos é adiada até o primeiro montagem. Em outras palavras, em vez de inicializar todos os inodes durante a criação do sistema de arquivos, essa tarefa é adiada e realizada de forma preguiçosa, ou "lazy", em segundo plano, após o sistema de arquivos ser montado.

Ao não usar a opção -E lazy_itable_init, o processo de criação do sistema de arquivos pode se tornar extremamente lento, pois o mkfs.ext4 tentará inicializar todos os inodes imediatamente. Em uma imagem de 400GB, isso pode levar horas. Portanto, ao utilizar a opção -E lazy_itable_init, você está otimizando o processo de criação, tornando-o significativamente mais rápido.

Em resumo, o -E lazy_itable_init serve para acelerar o processo de criação de sistemas de arquivos em volumes grandes, adiando a inicialização das tabelas de inode para após a primeira montagem e realizando-a de forma incremental em segundo plano.

Após ter montado o volume CIFS, proceda à formatação do arquivo utilizando o sistema de arquivos EXT4 ou qualquer outro de sua preferência.

root # mkfs.ext4 -E lazy_itable_init /dev/mapper/storage_file_luks

Com o volume devidamente formatado, realize sua montagem:

root # mkdir -p /storage ; mount -o noatime,nobarrier /dev/mapper/storage_file_luks /storage

Se achar conveniente, pode adicionar este ponto de montagem ao /etc/fstab, usando a opção "noauto" para prevenir montagem automática e "nobarrier" por questões de performance:

   /etc/fstab
/dev/mapper/storage_file_luks /storage ext4 noatime,nobarrier,noauto 0 2


Verificando a Integridade do Mapeamento do Dispositivo

Após montar o volume LUKS, a verificação da integridade e do status do dispositivo mapeado é uma etapa vital para garantir a consistência e a estabilidade do armazenamento criptografado. O utilitário `dmsetup` oferece uma visão direta do status do dispositivo mapeado, garantindo que o mapeamento entre o volume criptografado e o dispositivo de bloco virtual esteja funcional e íntegro.

Execute o seguinte comando para visualizar informações detalhadas sobre o mapeamento do dispositivo associado ao volume LUKS:

root # dmsetup info

A saída fornecerá detalhes abrangentes sobre o dispositivo mapeado, incluindo seu estado, contagem de acesso, e outros atributos essenciais que refletem a saúde e o status do mapeamento do dispositivo.

- **State**: Indica se o volume está ativo e disponível para leitura/escrita.

- **Open count**: Reflete o número de entidades que têm o dispositivo aberto para I/O.

- **UUID**: Confirme se o UUID corresponde ao esperado para garantir que você está interagindo com o volume LUKS correto.

Certificar-se de que o dispositivo mapeado está em um estado ACTIVE e saudável é crucial para garantir que as operações de leitura e escrita no volume criptografado ocorram sem interrupções e de maneira segura.


Considerações sobre o "nobarrier" e "noatime" utilizado na montagem do EXT4

Ao optar pela opção "noatime" durante a montagem de um volume, você desativa a atualização dos registros de acesso (atime) dos arquivos. Isso pode proporcionar uma melhoria significativa na performance, pois o sistema não precisará registrar cada vez que um arquivo é lido.

Vantagem: Ao usar "noatime", o sistema de arquivos se torna mais rápido, especialmente em operações intensivas de leitura, porque evita escritas desnecessárias no disco.

Desvantagem: Alguns aplicativos, embora raros, podem depender da informação de "atime" para funcionar corretamente. Desativando o "atime", você pode perder a capacidade de rastrear a última vez que um arquivo foi acessado.

Dessa forma, é importante considerar as necessidades do seu sistema e os possíveis efeitos colaterais antes de optar por usar a opção "noatime".


Sobre o "nobarrier", convém contar uma historinha.... Antigamente, pra ser mais sincero a mais de 15 anos atrás, servidores Linux que utilizavam o sistema de arquivos Ext4, rodando um kernel Linux versão 2.6.32 ou superior, poderiam experimentar baixa performance e um alto valor de load average.

Alguns discos rígidos, mesmo os SCSI de "alta performance" da época, em situações de queda de energia, não conseguiam gravar completamente as informações contidas em seu cache de gravação. Isso poderia resultar em arquivos corrompidos e perda de dados. Até o kernel 2.6.31, o sistema de arquivos Ext4 apenas garantia que as informações haviam sido enviadas ao HD, sem necessariamente assegurar sua gravação permanente. No entanto, a partir do kernel 2.6.32, houve uma mudança: o Ext4 passou a assegurar que os dados foram, de fato, gravados no disco, minimizando o risco de corrupção de arquivos em quedas de energia. Esta mudança, embora positiva em termos de integridade dos dados, teve como consequência uma redução significativa na performance de certos softwares, como o PostgreSQL e Mysql.

Até hoje, a prática comum de mercado para servidores que possuem proteção contra quedas de energia, como no-breaks e UPSs, e que usem sistemas de arquivos Ext4 é de desativar o "barrier". Isso é feito desativando a verificação de escrita de dados via kernel ao montar o volume com a opção -o nobarrier.

Entretanto, ao optar pela opção "nobarrier", é essencial estar ciente dos riscos. Desativar o "barrier" pode comprometer a integridade dos dados, especialmente em situações de falha de energia ou crashes do sistema. O "barrier" é uma proteção que verifica se a escrita do dado foi bem-sucedida, garantindo a ordem das gravações no disco e proporcionando uma proteção adicional contra corrupção de dados. Ao desativar o "barrier", você prioriza a performance em detrimento da segurança dos dados. Portanto, é altamente recomendável avaliar cuidadosamente suas necessidades de performance e os riscos associados antes de desativar esta funcionalidade.


Script para montar o LUKS automaticamente

Implemente uma solução sem esforço para montar o LUKS automaticamente em seu sistema. Ao criar e integrar este script com o seu crontab, você elimina a necessidade de inserir uma senha durante a montagem, proporcionando um processo mais suave e automatizado.

Certifique-se de seguir o passo anterior e criar a chave para uma montagem sem a necessidade de senha, garantindo assim que o script funcione de maneira impecável e segura.

crie o script:

root # mkdir -p /etc/area31/scripts ; touch /etc/area31/scripts/mount-storage-luks.sh ; chmod +x /etc/area31/scripts/mount-storage-luks.sh
   /etc/area31/scripts/mount-storage-luks.sh (bash source code) - Script para montar o LUKS automaticamente
#!/bin/bash

SHARE_IP="192.168.20.1"
SHARE_NAME="G"
SHARE_USER="coffnix"
SHARE_PASS="vip1337nix"

ROOT_DIR="/etc/area31"
MNT_DIR="/tp-share"
MNT_LUKS="/storage"

LUKS_DEVICE="${MNT_DIR}/storage_file.img"
LUKS_NAME="storage_file_luks"
LUKS_HEADER="${ROOT_DIR}/headerstore/header.img"
LUKS_KEY="${ROOT_DIR}/keys/${LUKS_NAME}.key"

##########################################################################

###############################################################################
# Testa se script já encontra-se em execução

test_running() {
RUNNING=$(ps aux | grep "$(printf '%s\n' "${0##*/}")" | grep bash | grep -v "bash -c" | grep -v $$ | grep -v grep | wc -l)
if [ ${RUNNING} -gt 1 ]; then
	echo "Script ja esta rodando, aguardando 10 segundos para tentar novamente."
	sleep 10
	RUNNING=$(ps aux | grep "$(printf '%s\n' "${0##*/}")" | grep bash | grep -v "bash -c" | grep -v $$ | grep -v grep | wc -l)
	if [ ${RUNNING} -gt 1 ]; then
		echo "Script ja esta rodando, nao sera executado novamente."
		exit 1
	fi
fi
}

test_running

###############################################################################
# Iniciando script

# Carrega módulos
# Módulos para carregar
MODULES=(algif_skcipher xchacha20 adiantum sha256_arm64 nhpoly1305 dm_crypt)

# Carrega módulos
for MODULE in "${MODULES[@]}"; do
	if ! lsmod | grep -q "^${MODULE} "; then
		modprobe "${MODULE}"
		if [ $? -ne 0 ]; then
			echo "Falha ao carregar o módulo ${MODULE}."
			exit 1
		fi
	fi
done


# Verificar se está montado
if mountpoint -q ${MNT_LUKS}; then
	touch "${MNT_LUKS}/.test"
	if [ $? -ne 0 ]; then
		echo "Erro ao escrever no ${MNT_LUKS}. Permissão negada ou ponto de montagem não acessível."
	else
		echo "Arquivo .test criado com sucesso em ${MNT_LUKS}."
		# Remover o arquivo .test após a verificação
		rm "${MNT_LUKS}/.test"
		echo "${MNT_LUKS} já está montado."
		exit 0
	fi
fi

# Verificar se /storage está montado
if mountpoint -q ${MNT_DIR}; then
	echo "${MNT_DIR} está montado."

	CHECK_LUKS_INACTIVE="$(cryptsetup status ${LUKS_NAME}|grep inactive|wc -l)"
	if [ ${CHECK_LUKS_INACTIVE} -eq 0 ]; then
		echo "LUKS desmontado"

		# Obtendo dispositivos de loopback associados a 'tp-share'
		devices=$(losetup -la | grep "${LUKS_DEVICE}" | awk '{print $1}')

		# Para cada dispositivo encontrado, verifique se há um dispositivo "filho"
		for device in $devices; do
			# Verificar se o dispositivo tem um "filho"
			child_device=$(losetup -la | grep "$device" | awk '{print $1}')

			# Se houver um dispositivo "filho", desmonte e desassocie
			if [ ! -z "$child_device" ]; then
				mountpoint=$(mount | grep $child_device | awk '{print $3}')
				if [ ! -z "$mountpoint" ]; then
					echo "Desmontando $mountpoint do dispositivo filho..."
					umount $mountpoint &> /dev/null
				fi
				echo "Desassociando dispositivos loopback associados a $child_device..."
				cryptsetup close ${LUKS_NAME}

				losetup -d $child_device &> /dev/null
			fi
		done

		# Para cada dispositivo encontrado, desmonte e desassocie
		for device in $devices; do
			# Se montado, desmonte
			mountpoint=$(mount | grep $device | awk '{print $3}')
			if [ ! -z "$mountpoint" ]; then
				echo "Desmontando $mountpoint..."
				umount $mountpoint
			fi

			# Se estiver associado a um volume LUKS, feche
			luksname=$(cryptsetup status ${LUKS_NAME} | grep "Name:" | awk '{print $2}')
			if [ ! -z "$luksname" ]; then
				echo "Fechando volume LUKS $luksname..."
				cryptsetup close ${LUKS_NAME}
			fi

			# Desassocie o dispositivo de loopback
			echo "Desassociando $device..."
			losetup -d $device &> /dev/null
		done
		sleep 2

		umount ${MNT_DIR}
		if [ $? -ne 0 ]; then
			echo "Falha ao desmontar o ${MNT_DIR}. Tente fechar todos os processos antes de desmonta-lo."
			exit 1
		fi
	else
		umount ${MNT_DIR}
		if [ $? -ne 0 ]; then
			echo "Falha ao desmontar o ${MNT_DIR}. Tente fechar todos os processos antes de desmonta-lo."
			exit 1
		fi

	fi
fi

# Montar CIFS
mkdir -p ${MNT_DIR}
chattr +i ${MNT_DIR} &> /dev/null
mount -t cifs //${SHARE_IP}/${SHARE_NAME} ${MNT_DIR} -o username=${SHARE_USER},password=${SHARE_PASS},iocharset=utf8,vers=2.0
if [ $? -ne 0 ]; then
    echo "Falha ao montar CIFS em ${MNT_DIR}."
    exit 1
fi

# Abrir o volume LUKS
cryptsetup open --key-file ${LUKS_KEY} --header=${LUKS_HEADER} ${LUKS_DEVICE} ${LUKS_NAME}
if [ $? -ne 0 ]; then
    echo "Falha ao abrir volume LUKS."
    exit 1
fi

# Montar o volume LUKS em /storage
mkdir -p ${MNT_LUKS}
chattr +i ${MNT_LUKS} &> /dev/null
mount -o noatime,nobarrier /dev/mapper/${LUKS_NAME} ${MNT_LUKS}
if [ $? -ne 0 ]; then
    echo "Falha ao montar volume LUKS em ${MNT_LUKS}."
    cryptsetup close ${LUKS_NAME}
    exit 1
fi

echo "Tudo pronto!"
exit 0


Para adicionar ao crontab:

   /etc/crontab
*/10 * * * *     root	/etc/area31/scripts/mount-storage-luks.sh &> /dev/null


Conclusão e performance do LUKS

Com estas etapas concluídas, você possui um volume de rede montado no seu Linux, criptografado e com todas as vantagens do sistema de arquivos escolhido. Estas podem incluir funcionalidades como XATTR (atributos estendidos), Journaling, e suporte a CHATTR no caso do EXT4. Para um nível adicional de segurança, é sugerido utilizar o arquivo do cabeçalho LUKS de uma localização remota, obtendo-o via SCP (SSH).


Avaliação de Desempenho com Compressão Zstandard (Zstd)

Ao trabalhar com dados, especialmente em volumes grandes, a compressão torna-se uma ferramenta vital, não apenas para economizar espaço em disco, mas também para melhorar a eficiência na transferência de dados. O script apresentado nesta seção realiza uma avaliação prática da compressão de dados usando o algoritmo Zstandard (Zstd) em diferentes níveis de compressão, proporcionando uma visualização direta das vantagens e desvantagens em termos de tamanho de arquivo e tempo de compressão.

O script realiza os seguintes passos:

1- Geração de Dados: Inicialmente, cria-se um conjunto de ${OPENSSL_FILES_NUMBER} arquivos utilizando o OpenSSL para gerar dados aleatórios na forma de chaves RSA. Cada arquivo é nomeado de forma sequencial e armazenado temporariamente no sistema de arquivos.

2- Concatenação de Dados: Um arquivo maior é criado concatenando aleatoriamente os arquivos gerados no passo anterior. A quantidade de concatenações realizadas é controlada pela variável ${NUM_CONCATENATIONS}.

3- Compressão e Avaliação: O arquivo concatenado é então comprimido usando três níveis diferentes de compressão Zstd (5, 10 e 19). O script mede e apresenta o tamanho do arquivo antes e depois da compressão, a razão de compressão, a velocidade de compressão e o tempo total gasto para cada nível de compressão.

4- Limpeza: Por fim, o script limpa os arquivos temporários criados durante o processo para liberar o espaço em disco utilizado.

Esta abordagem permite avaliar o impacto de diferentes níveis de compressão em termos de eficiência e desempenho, oferecendo insights que podem ser cruciais para otimizar pipelines de dados e processos de backup. Este script é especialmente útil para realizar testes em diferentes tipos de dados ou ambientes de armazenamento, permitindo afinar estratégias de compressão de dados conforme necessário.

Certifique-se de adaptar as variáveis e caminhos do arquivo conforme sua necessidade e ambiente de trabalho.

   /etc/area31/scripts/bench_zstd.sh (bash source code) - Script para benchmark de compressão utilizando o olgoritmo ZSTD
#!/bin/bash

OPENSSL_FILES_NUMBER="1000"
TEST_FILE="/storage/test"
NUM_CONCATENATIONS="4000"

echo "Creating ${OPENSSL_FILES_NUMBER} files with random data using openssl..."
for i in $(seq 1 ${OPENSSL_FILES_NUMBER}); do
    openssl genpkey -algorithm RSA -out "${TEST_FILE}_part_${i}.pem" 2> /dev/null
done

echo "Creating new file by concatenating ${NUM_CONCATENATIONS}x random data generated by openssl..."
for i in $(seq 1 ${NUM_CONCATENATIONS}); do
    cat "${TEST_FILE}_part_$((RANDOM % ${OPENSSL_FILES_NUMBER} + 1)).pem" >> "${TEST_FILE}"
done

for i in 5 10 19; do
  echo -e "####################"
  echo -e "# ZST compress level = ${i}\n"
  echo -e "* Compressing file..."

  COMPRESSED_FILE="${TEST_FILE}-compress-${i}.zst"
  start_time=$(date +%s.%N)
  zstd -${i} "${TEST_FILE}" -o "${COMPRESSED_FILE}"
  end_time=$(date +%s.%N)
  elapsed_time=$(echo "$end_time - $start_time" | bc)

  ORIGINAL_SIZE_BYTES=$(du -b "${TEST_FILE}" | awk '{print $1}')
  COMPRESSED_SIZE_BYTES=$(du -b "${COMPRESSED_FILE}" | awk '{print $1}')

  calculations=$(awk -v original_size="$ORIGINAL_SIZE_BYTES" -v compressed_size="$COMPRESSED_SIZE_BYTES" -v time="$elapsed_time" -v test_file="$TEST_FILE" 'BEGIN {original_size_mb = original_size / 1048576; compressed_size_mb = compressed_size / 1048576; ratio = (original_size_mb - compressed_size_mb) / original_size_mb * 100; speed = original_size_mb / time; printf test_file " file size: %.2f MB\nCompressed file size: %.2f MB\nCompression Ratio: %.2f%%\nDisk speed: %.2f MB/s\nTime: %dm%.3fs", original_size_mb, compressed_size_mb, ratio, speed, int(time/60), time%60}')

  echo -e "$calculations\n"
  echo -e "####################\n"

  rm "${COMPRESSED_FILE}"
done

rm "${TEST_FILE}"
rm ${TEST_FILE}_part_*.pem


Ex:

Cookies nos ajudam a entregar nossos serviços. Ao usar nossos serviços, você concorda com o uso de cookies.