Brandorr

Creating a secure Debian or Ubuntu Apt repo

Creating a personal apt repo is a great idea for managing custom packages while still utilizing apt's management and dependency abilities. (We are not going to cover all the specifics of why one would want to create a custom apt repo, that should be self-explanatory.) Creating a secure apt repo accessible via apt is relatively straight forward, three items need to be setup: the repo dir, httpd, and GPG to sign the packages.

Conventions

  • The packages are being managed by a user called Jason, who does not have root permissions.
  • The following packages are already installed on your system: gnupg apt-utils.
  • From time to time this blog post may not use obvious short cuts, this is being done intentionally.

Setting up the repository directory

The first step is to create the directory structure.

bash$ mkdir /home/jason/apt-repo
bash$ mkdir /home/jason/apt-repo/conf

In the conf directory there must be a configuration file called distributions

Origin: Acme Inc
Label: Acme Inc
Suite: lenny
Codename: lenny
Version: 5.0
Architectures: amd64 i386 source
Components: main
UDebComponents: main
Description: Acme Inc custom packages

For the sake of simplicity, we are only defining one component to work with, called main. It works just like the main component of official repos from vendors like Debian and Ubuntu.

(Note: for the time being we are not signing our packages just yet. Soon!)

The other directories will be created as needed by the tool that adds packages to the repo, reprepro. Therefore you do not need to worry about them.

Setting up httpd

Regardless of what httpd you choose to use, you want to allow for HTTP access to the /home/jason/apt-repo directory.

For apache use the following directory defintion:

<Directory /home/jason/apt-repo>
  Options Indexes FollowSymLinks Multiviews
  Order allow,deny
  Allow from all
<Directory>

<Directory /home/jason/apt-repo/db>
  order deny,allow
  deny from all

<Directory /home/jason/apt-repo/conf>
  order deny,allow
  deny from all
</Directory>

Adding packages to repo

Adding packages to the repo is very easy:

bash$ cd /home/jason/apt-repo
bash$ reprepro includedeb lenny /path/to/file.deb

Use apt-get to fetch packages with your new repo

You must tell your clients machines to use the new repo in order to fetch packages from it:

bash$ sudo echo "deb http://YOUR-APT-REPO-SERVER main" > /etc/apt/sources.list.d/custom_repo
bash$ sudo apt-get update
bash$ apt-get install CUSTOM_PACKAGE

If your packages are sane (which will be covered in another blog post!), you should have been able to install your packages without any problems.

Almost .... did you get an error about your repository being unsigned? Read on!

Signing the repo

Dpkg packages do not have the ability to be individually signed the way RPM packages do. Instead, the package publisher must sign the contents of the repo the package comes in order to validate the authenticity of the package.

The first step is to create a GPG key used to sign the repo. In the section below, the material in CAPS is example user input.

If you haven't done so already, secure the permissions of your .gnupg directory.

bash$ sudo chown jason.jason .gnupg
bash$ sudo chmod 0700 .gnupg

Now lets generate that key!

bash$ sudo gpg  --homedir /jason/.gnupg --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: keyring `.gnupg/secring.gpg' created
gpg: keyring `.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?2
DSA keys may be between 1024 and 3072 bits long.
What keysize do you want? (2048) 2048
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
        = key expires in n days
      w = key expires in n weeks
      m = key expires in n months
      y = key expires in n years
Key is valid for? (0) 3y
Key expires at Mon 06 Oct 2014 03:44:07 PM EDT
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) "

Real name: JOHN SMITH
Email address: JOHNSMITH@EXAMPLE.COM
Comment: GPG KEY USED FOR SIGNING APT REPOS
You selected this USER-ID:
    "JOHN SMITH (GPG KEY USED FOR SIGNING APT REPOS) "

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.
gpg: WARNING: some OpenPGP programs can't handle a DSA key with this digest size
.+++++.+++++.+++++.+++++...+++++.+++++..+++++.+++++..+++++.+++++..++++++++++.++++++++++.++++++++++.+++++..+++++...++++++++++..+++++.+++++....+++++++++++++++..++++++++++>.+++++>+++++............................................................................................+++++
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: .gnupg/trustdb.gpg: trustdb created
gpg: key EE519117 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
gpg: next trustdb check due at 2014-10-06
pub   2048D/EE519117 2011-10-07 [expires: 2014-10-06]
      Key fingerprint = E6A5 2C1B BDC6 2B58 73C0  A9B1 38CD 8A84 EE51 9117
uid                  JOHN SMITH (GPG KEY USED FOR SIGNING APT REPOS) 
sub   2048g/2AF69C3B 2011-10-07 [expires: 2014-10-06]

You should be able to see the key now:

bash$ gpg  --homedir .gnupg --list-keys
.gnupg/pubring.gpg
-------------------
pub   2048D/EE519117 2011-10-07 [expires: 2014-10-06]
uid                  JOHN SMITH (GPG KEY USED FOR SIGNING APT REPOS) 
sub   2048g/2AF69C3B 2011-10-07 [expires: 2014-10-06]

It is the latter part of the pub key ID we are concerned about, in this case EE519117

We want to add the following line to the distributions file:

Origin: Acme Inc
Label: Acme Inc
Suite: lenny
Codename: lenny
Version: 5.0
Architectures: amd64 i386 source
Components: main
UDebComponents: main
Description: Acme Inc custom packages
SignWith: EE519117

The next time you add a package to the repo, add it with the following command:

bash$ cd /home/jason/apt-repo
bash$ reprepro --ask-passphrase includedeb lenny /path/to/file.deb

Adding the package will cause the repo to be signed with the key. If you do not want to wait till you've added a package to sign the repo, you may do the following: cd /home/jason/apt-repo/dists/lenny ; gpg --output Release.gpg -ba Release

Client configuration for the new key

You need to publish your key and have the clients install it in order to use it. You can extract it like so:

bash$ sudo gpg --armour --export EE519117 > repo-pub.key

On your client machiness you can install it like so:

bash$ sudo apt-key add /path/to/repo-pub.key

You should be able to install packages with out any issues regarding security signatures!

Future blog posts

  • Multi component repos
  • Pass-phrase less repos (dangerous! but easy to manage in trusted environments)
  • Custom dpkg packages
  • Easy backporting of dpkg packages