Criando pacotes Debian assinados com GPG


Leia em 7 minutos

Uma das grandes vantagens de se trabalhar em sistemas baseados em Debian é a grande disponibilidade de pacotes. Quase todos os projetos importantes são distribuídos também como pacotes .deb, extensão dos pacotes Debian.

E criar pacotes Debian é bastante simples, embora as informações sejam muitos espalhadas e, por que não, confusas para quem não é sysadmin. Neste artigo vou mostrar como é o processo que sigo para empacotar o Ruby, fazendo a assinatura do pacote e repositório com GPG.

Gerando sua chave GPG

Para assinar nosso repositório e pacotes, nós precisaremos gerar uma chave GPG. Execute o comando gpg --gen-key. Escolha as opções recomendadas. Antes de gerar sua chave, você precisará gerar “entropia”, o que significa dizer que dados randômicos serão criados à partir da atividade de sua máquina. Isso pode demorar muito! Felizmente, existe um pacote que permite fazer isso muito rapidamente.

$ sudo apt-get update
$ sudo apt-get -y upgrade
$ sudo apt-get -y install rng-tools

Depois que o pacote for instalado, execute o comando abaixo:

$ sudo rngd -r /dev/urandom

Agora, gere sua chave GPG:

$ gpg --gen-key
gpg (GnuPG) 1.4.11; Copyright (C) 2010 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: directory `/home/vagrant/.gnupg' created
gpg: new configuration file `/home/vagrant/.gnupg/gpg.conf' created
gpg: WARNING: options in `/home/vagrant/.gnupg/gpg.conf' are not yet active during this run
gpg: keyring `/home/vagrant/.gnupg/secring.gpg' created
gpg: keyring `/home/vagrant/.gnupg/pubring.gpg' created
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection?
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) Y

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

Real name: Nando Vieira
Email address: fnando.vieira@gmail.com
Comment:
You selected this USER-ID:
    "Nando Vieira <fnando.vieira@gmail.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
...........+++++
......................+++++
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
+++++
+++++
gpg: /home/vagrant/.gnupg/trustdb.gpg: trustdb created
gpg: key 8CEE231C marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
pub   2048R/8CEE231C 2012-07-27
      Key fingerprint = 926C C20B F27D 554E 0E5D  3699 9DB7 CC09 8CEE 231C
uid                  Nando Vieira <fnando.vieira@gmail.com>
sub   2048R/36DD9698 2012-07-27

Criando nosso pacote .deb

Nós iremos gerar nosso pacote Debian à partir do código-fonte do Ruby. Vale lembrar que fiz todo este processo em um box Vagrant, isolando todo e qualquer efeito colateral que possa acontecer.

ATENÇÃO: Se você decidir por fazer isso no Vagrant, lembre-se de fazer uma cópia de backup do diretório ~/.gnupg antes de destruir o box. Caso contrário, você não terá como assinar novos pacotes com a mesma chave.

Crie um diretório chamado ruby-deb.

$ cd /vagrant
$ mkdir ruby-deb
$ cd ruby-deb

Baixe o tarball do programa que você quer empacotar. No nosso caso, vou usar o tarball http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p286.tar.gz. Depois que o download do tarball terminar, extraia o arquivo.

$ wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p286.tar.gz
$ tar xvf ruby-1.9.3-p286.tar.gz

Agora nós iremos instalar o pacote dh-make, que permite converter um diretório de código-fonte em uma estrutura válida de acordo com a Política do Debian.

$ sudo apt-get -y install dh-make vim

De dentro do diretório ruby-1.9.3-p286, execute o comando dh_make. Isso irá gerar um diretório debian. Este diretório funciona como um manifesto de como seu pacote será gerado.

$ DEBFULLNAME='Nando Vieira' dh_make -e fnando.vieira@gmail.com -f ../ruby-1.9.3-p286.tar.gz

Type of package: single binary, indep binary, multiple binary, library, kernel module, kernel patch?
 [s/i/m/l/k/n] s

Maintainer name  : Nando Vieira
Email-Address    : fnando.vieira@gmail.com
Date             : Wed, 17 Oct 2012 16:36:35 +0000
Package Name     : ruby
Version          : 1.9.3-p286
License          : blank
Type of Package  : Single
Hit <enter> to confirm:
Done. Please edit the files in the debian/ subdirectory now. ruby
uses a configure script, so you probably don't have to edit the Makefiles.

Ele contém uma série de arquivos do Emacs que podem ser removidos.

$ rm debian/*.{ex,EX}

De todos os arquivos desse diretório, o mais importante é o arquivo debian/control. Ele define as dependências do pacote, versão, dentre outras informações. Edite-o conforme o exemplo abaixo:

Source: ruby
Section: universe/ruby
Priority: extra
Maintainer: Nando Vieira <fnando.vieira@gmail.com>
Build-Depends: debhelper (>= 8.0.0), autotools-dev, libssl-dev, libyaml-dev, libreadline6-dev, libffi-dev, zlib1g-dev
Standards-Version: 3.9.2
Homepage: http://ruby-lang.org

Package: ruby-stable
Architecture: i386 amd64
Depends: ${shlibs:Depends}, ${misc:Depends}, libyaml-0-2, libssl1.0.0, libffi6, zlib1g
Description: Interpreter of object-oriented scripting language Ruby, version 1.9.3
 Ruby is the interpreted scripting language for quick and easy
 object-oriented programming.  It has many features to process text
 files and to do system management tasks (as in perl).  It is simple,
 straight-forward, and extensible.

O campo Build-Depends deve conter as dependências que seu código-fonte precisa para ser compilado. Já o campo Depends define quais as dependências que devem ser instaladas quando o pacote for instalado.

Edite o arquivo debian/changelog. Atualize este arquivo com as informações sobre as mudanças introduzidas por este pacote. Ele também deve ter o versionamento do pacote.

ruby (1.9.3-p286-1) precise; urgency=low

  * Initial release

 -- Nando Vieira <fnando.vieira@gmail.com>  Wed, 17 Oct 2012 16:36:35 +0000

ATENÇÃO: O nome e e-mail que é colocado no arquivo debian/changelog devem ser exatamente os mesmos que foram adicionados na chave GPG.

Antes de gerar os pacotes, nós iremos instalar as dependências de compilação. São as mesmas que você adicionou no campo Build-Depends.

$ sudo apt-get install -y debhelper devscripts autotools-dev libssl-dev libyaml-dev libreadline6-dev libffi-dev zlib1g-dev

Agora é a hora de gerar os pacotes. Como a versão 1.9.3 precisa de uma versão de Ruby para poder fazer a compilação, vamos instalar o pacote disponível no Ubuntu.

$ sudo apt-get -y install ruby1.9.3

Agora, gere o pacote:

$ debuild

Hora de tomar um café! Esse passo demora bastante. No fim, se tudo deu certo, seu pacote terá sido criado com a mesma arquitetura que você está gerando o pacote. Isso significa que se você estiver em um sistema 64-bits, terá gerado um pacote também 64-bits.

Inspecionando o comando debuild vi que é possível especificar a arquitetura do pacote que será gerado com a opção -a, o que permitiria gerar um pacote 32-bits à partir de um sistema 64-bits. Infelizmente, para compilar o Ruby ela não funciona. Uma solução seria usar algo como o pbuilder, mas criar os ambientes de distribuição é extremamente demorado, então preferi automatizar o processo em um script bash e gerar as duas arquiteturas em boxes diferentes do Vagrant.

Uma coisa importante é que seu pacote já foi assinado com a chave GPG. Você pode instalá-lo com o comando dpkg.

$ sudo dpkg -i ../ruby-stable_1.9.3-p286-1_amd64.deb

Embora funcione bem, instalar o pacote manualmente não é maneira mais adequada. Por isso, nosso próximo passo será criar o repositório.

Criando seu repositório Debian

Criar um repositório é tão simples quanto criar o pacote. Primeiro, nós iremos instalar o pacote reprepro.

$ sudo apt-get install reprepro

Agora, nós iremos criar o diretório onde nosso repositório será definido.

$ cd /vagrant
$ mkdir repo

Dentro deste diretório crie o arquivo conf/distributions com o seguinte conteúdo:

Origin: apt.hellobits.com
Label: apt repository
Codename: precise
Architectures: amd64 i386
Components: main
Description: Custom packages
SignWith: yes
Pull: precise

Agora, podemos adicionar o nosso pacote ao repositório. Você terá que informar sua senha da chave GPG, para assinar o repositório.

$ reprepro --ask-passphrase -Vb . includedeb precise ../ruby-stable_1.9.3-p286-1_amd64.deb

Como nosso repositório está assinado, precisaremos exportar a chave GPG, que deverá ser adicionada antes que os pacotes possam ser instalados. Para fazer isso, execute o comando abaixo. Lembre-se que esse arquivo precisa estar disponível publicamente, então é uma boa ideia deixar no próprio repositório.

$ gpg --armor --export fnando.vieira@gmail.com > hellobits.key

Agora precisamos servir este repositório publicamente. Se você não tiver nada muito complexo (ou grande), pode usar o Heroku. Crie o arquivo .htaccess na raíz do diretório repo com o conteúdo abaixo.

php_flag engine off

Crie também um arquivo index.php vazio.

$ touch index.php

Ao criar estes dois arquivos, você poderá servir seu repositório usando os buildpacks do Heroku, nesse caso servindo apenas arquivos estáticos. Antes, eu estava servindo com o middleware Rack::Directory, mas vi essa dica no projeto apt-heroku criado recentemente pelo Fabio Kung, que também permite gerenciar repositórios com bastante facilidade. Só não estou usando o projeto do Kung porque não sei como funciona com arquivos assinados com GPG, e fiquei com preguiça de ir atrás.

Crie seu projeto no Heroku, envie os arquivos e pronto! Você tem um repositório Debian com pacotes assinados prontinho para ser usado.

Instalando pacotes de seu repositório Debian

Para adicionar a chave GPG do repositório ao Ubuntu, você vai precisar executar o comando apt-key.

$ wget -q -O - http://apt.hellobits.com/hellobits.key | sudo apt-key add -

Depois, crie o arquivo /etc/apt/sources.list.d/hellobits.conf com as informações sobre o nosso repositório.

$ echo 'deb http://apt.hellobits.com/ precise main' | sudo tee /etc/apt/sources.list.d/hellobits.list

Atualize a lista de pacotes e instale o pacote que você gerou.

$ sudo apt-get update
$ sudo apt-get install ruby-stable

Finalizando

Embora não seja um processo de outro mundo, também não é nada trivial gerar pacotes Debian.

Novamente, recomendo que você faça uma cópia de backup do diretório ~/.gnupg, que contém a sua chave GPG. Além dela, é interessante manter uma cópia do diretório debian, para não ter que fazer todo o processo sempre que for gerar uma atualização do pacote; neste caso, você precisará mexer apenas no arquivo debian/changelog.