Builds reproduzíveis do NixOS: reconstrução independente da ISO mínima de instalação é bem-sucedida
(discourse.nixos.org)- A ISO mínima de instalação do NixOS foi reconstruída de forma independente e idêntica bit a bit à distribuição publicada pelo Hydra, mostrando que é possível verificar se os binários distribuídos correspondem ao código-fonte
- Esta verificação reproduz não apenas os pacotes incluídos na ISO, mas também o próprio processo de geração da ISO, confirmando um escopo mais amplo do que a simples reprodutibilidade de pacotes
- A reconstrução começou a partir do appliance VirtualBox do NixOS 20.03, usando a revisão
63678e9f3d3adonixpkgs, com--option substitute falsepara desativar a dependência de cache binário - Se a OVA de 2020 ou o
gitbaixado tivessem um backdoor sofisticado, isso ainda poderia servir como vetor de ataque; por isso, a verificação baseada em um sistema totalmente bootstrapado ainda segue pendente - A reconstrução da ISO mínima é um marco importante, mas os próximos passos incluem remover soluções temporárias, reproduzir mais mídias de instalação, criar infraestrutura para reconstruções independentes regulares e ferramentas de atestado de build
Reprodutibilidade verificada na ISO mínima
- A build da ISO
nixos-minimalpublicada pelo Hydra foi reconstruída de forma independente, obtendo um resultado idêntico bit a bit - O escopo da reprodução se divide em dois eixos
- Todos os pacotes incluídos na ISO
- O próprio processo de build que cria a ISO
- Pacotes necessários para a build da ISO, mas não incluídos dentro dela, também foram compilados, sem depender de binários em cache
- Builds reproduzíveis oferecem um caminho de confiança para verificar se os binários distribuídos correspondem fielmente ao código-fonte e se não foram adulterados em pipelines de build como o Hydra
Procedimento de reconstrução e limitações
- A reconstrução foi realizada iniciando um novo appliance VirtualBox com NixOS 20.03
- CPU e memória foram alocados em quantidade suficiente, e o disco foi expandido para cerca de 65 GB
- Após instalar o
git, foi feito o clone denixpkgse o checkout da revisão63678e9f3d3a - Com
--option substitute false, os itens necessários foram compilados na máquina local em vez de serem buscados em cache binário
- O procedimento inclui medidas temporárias para contornar problemas conhecidos
- Ainda existem limitações do ponto de vista de confiança na cadeia de suprimentos
- Se a OVA de 2020 ou o
gitbaixado tivessem um backdoor sofisticado, isso ainda poderia ser um vetor de ataque - Reconstruir a partir de um sistema totalmente bootstrapado seria melhor, mas esse estágio ainda não foi alcançado
- O progresso relacionado continua na thread do projeto de segurança da cadeia de suprimentos do nixpkgs
- Se a OVA de 2020 ou o
Diferença entre o anúncio de 2021 e o resultado atual
- Em 2021, houve um anúncio de que a ISO mínima era 100% reproduzível, mas na época apenas os pacotes necessários para a build da ISO haviam sido reproduzidos individualmente, enquanto ainda restavam diferenças na reconstrução da ISO em si
- A causa eram problemas remanescentes no cache do Hydra e na forma de geração da ISO
- Depois que esses problemas foram corrigidos, surgiram regressões como um problema upstream no Python 3.10, e só nesta semana o estado necessário para verificar toda a cadeia voltou a ser alcançado
- Os próximos passos incluem remover soluções temporárias, reproduzir mais pacotes e reproduzir outras mídias de instalação, como a ISO do Gnome
- Também são necessárias infraestrutura para reconstruções independentes regulares e ferramentas para compartilhamento e consumo de atestados de build, como o trustix
1 comentários
Comentários do Hacker News
Recompilar a ISO mínima a partir do código-fonte é um marco impressionante na jornada rumo a um sistema que possa ser compilado de forma reproduzível com base no código-fonte
O Guix também alcançou recentemente um resultado ortogonal, mas igualmente impressionante, nessa mesma jornada: fez o bootstrap de toda a cadeia de ferramentas do compilador a partir de um único binário reproduzível de 357 bytes, sem outros blobs binários de compiladores
Talvez em breve os dois se unam e seja possível compilar de forma reproduzível uma distribuição inteira a partir do código-fonte
https://guix.gnu.org/en/blog/2023/the-full-source-bootstrap-...
O ponto central que muita gente não entende é que a ideia não é provar que o resultado é 100% confiável, e sim provar que ele é 100% fiel ao código-fonte
Ou seja, se alguma coisa suspeita como um backdoor furtivo for descoberta, isso sempre poderá ser reproduzido de forma conclusiva
Do lado do mal, isso significa não ter para onde correr nem onde se esconder
Parece que daria para documentar manualmente todos os 357 bytes de código de máquina e torná-los compreensíveis para humanos
Talvez seja uma pergunta boba, já que nunca fiz algo assim, mas fico me perguntando por que a reprodutibilidade não é o comportamento padrão
Se você compilou duas cópias de um software a partir do mesmo código-fonte, não entendo muito bem o que impede que elas sejam completamente idênticas toda vez
Sei que há muitas partes móveis, mas ainda não entendo bem como surgem as diferenças
Uma lista de problemas frequentes pode ser vista aqui: https://reproducible-builds.org/docs/
No geral, o ponto principal é que os desenvolvedores não testam se a compilação é reproduzível
Quando isso entra nos testes de release, normalmente continua reproduzível
Exemplos clássicos que tornam algo explicitamente não reproduzível são timestamps e informações do autor
Também há coisas que quebram a reprodutibilidade padrão de forma implícita; por exemplo, muitos runtimes não definem a ordem dos itens de um hashmap, e o compilador pode percorrer esse hashmap para gerar o binário
Pode haver operações em que a ordem importa, e dependendo do estado da CPU podem sair binários um pouco diferentes, embora todos estejam corretos
Às vezes também são metadados dependentes do tempo ou do ambiente, ou coisas como a ordem de execução das threads
Desculpem a ignorância, mas eu achava que um dos principais motivos para a existência do NixOS era a reprodutibilidade
Achei que esses problemas já estivessem resolvidos
Usei o NixOS por umas 2 horas e queria experimentar o Hyprland, mas como o Hyprland exige alguma configuração, imaginei que no NixOS seria mais fácil reaproveitar a configuração de outras pessoas do que em outras distribuições
Só que foi difícil achar configurações, encontrei umas 3 gists aleatórias no GitHub, nenhuma funcionou, e acabei desistindo
Como não depende do ambiente completo do sistema, como nas distribuições comuns, em muitos casos isso já faz com que o mesmo binário seja gerado todas as vezes
Mas o próprio processo de build ainda pode ser não determinístico em vários pacotes, então isso por si só não produz reprodutibilidade completa de imediato
O sentido em que você está pensando é o de recompilar facilmente um pacote binário usando as mesmas versões de dependências, opções de build etc.
Ou seja, não deveria haver espaço para aparecer um novo erro de compilação do tipo “na minha máquina funcionou”
O sentido de que estão falando aqui é o de todos os artefatos de build serem binários idênticos byte a byte
Eles não devem depender do nome da máquina, do horário da compilação, da ordem em que os arquivos terminam de compilar em um build paralelo etc., e isso é bem mais difícil
Se você não está acostumado, dificilmente é a ferramenta que escolheria numa situação de “quero algo funcionando agora”
No NixOS, “reproduzível” costuma significar algo mais próximo de “o mesmo código Nix gera o mesmo comportamento do programa”
É parecido com o que as pessoas esperam de um Dockerfile, e está num nível de resolver problemas como “na minha máquina funcionou” ou “da última vez funcionou”
Já builds reproduzíveis têm como objetivo que os artefatos gerados em máquinas diferentes sejam idênticos bit a bit
Isso permite verificar se o código foi compilado a partir de um determinado conjunto de fontes, criando uma camada extra de segurança
Também fiquei curioso sobre quais termos de busca você usou para procurar configurações
Se você procurar por “nixos configuration”, vai encontrar resultados como https://github.com/search?q=nixos%20configuration&type=repos..., e só olhando para Hyprland já aparece bastante coisa, como https://github.com/search?q=wayland.windowManager.hyprland&t...
Vale a pena ver https://github.com/donovanglover/nix-config
É uma configuração baseada em Flake e inclui Hyprland e várias coisas interessantes
No momento, o NixOS não é uma ferramenta adequada para pessoas mais frágeis ou com pouco tempo
Espero que isso mude algum dia, mas, se você aguentar e passar por isso, pode obter benefícios
Fico curioso se usaram a busca de código do GitHub
As opções relacionadas do Home Manager podem ser encontradas aqui: https://mipmip.github.io/home-manager-option-search/?query=h...
Depois é só pesquisar no GitHub: https://github.com/search?utf8=%E2%9C%93&q=lang%3Anix+hyprla...
Algumas buscas por opções podem sugerir um público mais casual ou mais avançado
É preciso lembrar que a reprodutibilidade de Nix / NixOS / Nixpkgs é reprodutibilidade da fonte
Se a fonte mudar, você recebe um aviso, mas isso é diferente da reprodutibilidade de binários, em que os binários podem variar a cada build
A reprodutibilidade binária de Nix / NixOS / Nixpkgs, pelo menos de forma sistemática, não parece ser bem testada
Guix, Arch Linux e Debian lidam melhor com reprodutibilidade binária do que Nix / NixOS / Nixpkgs
Referências: https://r13y.com/ (Nix*) / https://tests.reproducible-builds.org/debian/reproducible.ht... (Debian) / https://tests.reproducible-builds.org/archlinux/archlinux.ht... (Arch Linux) / https://data.guix.gnu.org/repository/1/branch/master/latest-... (Guix, pode demorar para carregar, e uma cópia em cache está em https://archive.is/lTuPk)
Reprodutibilidade de entrada significa “invalidação perfeita de cache com base nas entradas”
Nix e Guix fazem isso perfeitamente por projeto, e às vezes isso até causa rebuilds demais
Debian e Arch Linux não tratam isso como preocupação principal e lidam com a questão de quais pacotes rebuildar quando um certo arquivo-fonte é atualizado por meio de abordagens ad hoc, como gatilhos manuais de rebuild
Reprodutibilidade de saída significa “o processo de build é determinístico e sempre produz o mesmo binário”, e é esse o tema do post original
O Nix ajuda nisso ao construir pacotes em sandbox, mas não é uma solução mágica
Nesse ponto, o Nix está no mesmo barco que Debian e Arch Linux
Na prática, as distribuições frequentemente enviam patches de reprodutibilidade para upstream, e outras distribuições também se beneficiam disso
Nesse contexto, https://reproducible.nixos.org é o equivalente dos outros links apresentados; concordo que o relatório do Nix é menos detalhado, mas isso não significa que a reprodutibilidade binária do Nix seja pior
Se isso for lido como “o Nix só é bom em reprodutibilidade de entrada e é ruim em reprodutibilidade binária”, então está errado
É justamente esse marco que está sendo comemorado aqui
Ele fala em reproduzir bit a bit não só os binários, mas também a forma como eles são empacotados em ISO
O r13y.com está desatualizado e, pelo que eu lembro, o menos de 1% que faltava era por causa de uma regressão upstream do Python
A reprodutibilidade dos próprios binários, excluindo o empacotamento em ISO, já tinha sido alcançada há alguns anos
Quando se vai para pacotes além da ISO principal, a comparação fica complicada
A forma de lidar com pacotes é sutil, mas neste contexto importante e diferente; muitos pacotes que provavelmente estariam no AUR do Arch entram no Nix como pacotes normais, e a maioria dos pacotes upstream do tipo -bin simplesmente não é necessária no Nix
Em geral, o Nix facilita a criação de builds reproduzíveis, mas isso nem sempre é possível independentemente do Nix, e muitas vezes exige patches
Somando isso ao fato de que o repositório principal de pacotes do Nix tem mais de 80 mil pacotes, enquanto o Arch tem menos de 15 mil sem contar o AUR, comparações por porcentagem não são muito úteis
Um mal-entendido muito comum é achar que o hash dos caminhos do Nix store é baseado na saída do build, quando na verdade ele se baseia em todas as fontes e entradas usadas para construir os binários no ambiente isolado, independentemente de serem binários ou não
Por isso, o benefício de segurança que as pessoas esperam não surge exatamente como imaginam, mas, em compensação, isso permite usar até software que não é compilado de forma reproduzível em uma forma de distribuição razoavelmente reproduzível, com as mesmas funcionalidades, configurações de compilador, versões de dependências, usuário, configuração etc.
É verificar se os binários são iguais quando compilados a partir da mesma fonte em máquinas diferentes
O ponto principal é que os binários não mudem a cada build
O método de teste também diz que cada build é executado duas vezes, em momentos diferentes, com hardware diferente e kernels diferentes
Pelo visto, aparece como 85,6% reproduzível: https://reproducible.archlinux.org
Fico curioso sobre quanto trabalho seria necessário no NixOS, que tem mais de 80 mil pacotes nos repositórios oficiais
Considerando os objetivos ou a realidade do nixpkgs, isso não é verdade de forma alguma
O post original fala sobre reproduzir uma ISO mínima binária que contém vários pacotes binários
É irônico e engraçado que o projeto OpenBSD esteja indo com força total na direção oposta
O OpenBSD faz com que cada instalação tenha deslocamentos de endereço únicos e aleatorizados
Entendo que os dois objetivos, builds reproduzíveis e instalações únicas, são ortogonais e podem ser alcançados ao mesmo tempo, mas essa dualidade ainda é engraçada
Ou também daria para aleatorizar os deslocamentos na inicialização do programa, o que manteria a reprodutibilidade e ainda aumentaria a segurança
Aí os deslocamentos mudariam a cada execução
Os próprios pacotes ainda podem ser reproduzíveis
Toda a aleatorização é feita localmente, depois de baixar o pacote e verificar o checksum
Agora eu só queria que, como quase todas as outras distribuições Linux fazem desde os anos 90, os mantenedores ao menos assinassem os pacotes
Assim, todos teriam alguma noção de que o código que estão compilando é o mesmo código enviado e revisado por pessoas conhecidas
Até que as assinaturas sejam padronizadas, é difícil imaginar usar Nix em produção para proteger algo de valor
Em geral, eles não dão nenhuma garantia sobre o conteúdo dos pacotes
Esperar garantias significativas da assinatura deles me parece parecido com esperar suporte ao produto do entregador
Também não é preciso confiar que o empacotamento não foi feito de forma maliciosa
Como o Nix faz builds reproduzíveis, se você não quiser depender do cache binário, pode inspecionar a derivation e compilar você mesmo
Se o conteúdo original é malicioso ou não, isso no fim é uma questão entre o desenvolvedor e o usuário
Se outras distribuições te levaram a acreditar no contrário, eu diria que isso foi quase enganoso
A única exceção que me vem à cabeça é o Tails, mas o Tails não é tão amplo quanto o Nix
Pelo que vejo, o Debian não faz mais isso e o sistema de build assina os builds; o Fedora também não; do Arch não tenho certeza, mas acho que também não
O sistema de build do NixOS assina todos os artefatos de build com a própria chave e verifica as assinaturas no download
Se você for mais paranoico, o Nix pelo menos facilita bastante compilar tudo direto do código-fonte
É um marco muito impressionante, e parabéns a todos que tornaram isso possível
Pelo que foi dito, até ao realmente recompilar a ISO ainda apareciam diferenças, e a causa eram problemas remanescentes no cache do Hydra e a forma como a ISO era gerada
Fico curioso se alguém pode explicar como corrigiram “a forma como a ISO era gerada”
Já tentei fazer uma ISO reproduzível antes, mas não consegui fazer o sistema de arquivos gerar os extents de forma determinística
A etapa final daquele processo gera a ISO no diretório
./result/isoAcho que o que você procura é o comando chamado por esse build, mas não sei exatamente qual etapa você está tentando localizar
Por exemplo, a chamada do
xorrisoestá aqui: https://github.com/NixOS/nixpkgs/blob/master/nixos/lib/make-...Para fazer isso, não seria preciso falsificar a hora do sistema?
Horários costumam acabar entrando nos binários de várias formas
Isso é tão comum que muitos compiladores implementaram a variável de fato padrão SOURCE_DATE_EPOCH para falsificar timestamps: https://reproducible-builds.org/docs/source-date-epoch/
Isso não ajuda a resolver o problema descrito por Ken Thompson em “Reflections on Trusting Trust”?
Se for possível fazer bootstrap completo do sistema inteiro a partir do código-fonte, parece que ficaria mais difícil inserir algo como um compilador com backdoor embutido
Em teoria, ainda poderia haver um backdoor sofisticado no ambiente em que a ISO é construída
Se você realmente quiser resolver esse problema, pode olhar o Diverse Double Compiling(https://dwheeler.com/trusting-trust/) ou bootstrap completo do ambiente(https://bootstrappable.org/)
A seção “essa abordagem não tem um problema de bootstrap?” do texto também é relevante
Ainda assim, só o fato de reproduzir o build já ajuda muito a tornar esse tipo de ataque cada vez menos plausível
Por causa de trabalho recente, andei vivendo dentro do ecossistema Red Hat
Fico curioso sobre como isso se compara com Fedora Silverblue, Ansible ou Fedora Silverblue + Ansible
O Imagebuilder diz ter reprodutibilidade, mas, pelo que sei, ele instala a maioria dos pacotes rpm como binários, não a partir do código-fonte
Então, a menos que todos os pacotes de entrada também sejam reproduzíveis, isso não é reprodutibilidade estrita
Se a explicação sobre compilar pacotes a partir do código-fonte, criar a imagem da distribuição e lidar com reprodutibilidade não fez muito sentido para você, provavelmente você não é o público-alvo principal
Já o Ansible especifica os passos que o SO deve seguir
Silverblue e Nix são praticamente ortogonais entre si, além do fato de ambos serem distribuições Linux
O Silverblue é uma tentativa de mudar a forma de entrega de software usando apenas contêineres sobre um host imutável
Se você estiver procurando uma alternativa ao Ansible que imite o Nix até certo ponto usando Jsonnet e rastreamento de estado, vale a pena dar uma olhada no Etcha: https://etcha.dev
O Nix é imutável
Novas mudanças são criadas de forma totalmente nova, e só depois que a build é bem-sucedida todos os pacotes são “linkados simbolicamente” ao sistema atual
O Fedora Silverblue é baseado em ostree https://github.com/ostreedev/ostree
Ele funciona de forma parecida com o git para a árvore raiz, mas para aplicar mudanças é preciso reinicializar o sistema inteiro
O Nix faz isso por meio de links simbólicos para os pacotes, então não é necessário reinicializar o sistema
Há uma explicação mais detalhada aqui: https://dataswamp.org/~solene/2023-07-12-intro-to-immutable-...