URL: https://www.progressiverobot.com/how-to-use-reprepro-for-a-secure-package-repository-on-ubuntu-14-04/

Introduction to Packages and Repositories

We've all been there – needing a program – and what do we do? Most of us just <^>apt-get install postfix<^> and presto! We magically have Postfix installed.

It isn't really magic, though. The package manager apt-get searches for, downloads, and installs the package for you. This is highly convenient, but what if apt-get can't find the program you need on its standard list of repositories? Thankfully, apt-get allows users to specify custom download locations (called repositories).

In this tutorial, we will walk through setting up your own secure repository and making it public for others to use. We will be creating the repository on a Ubuntu 14.04 LTS Droplet, and testing the download from another Droplet with the same distribution.

To get the most out of this guide, make sure to check out our tutorial for managing packages with apt-get.

Prerequisites

Two Ubuntu 14.04 LTS Droplets

By the end of the guide you will have:

  • Prepared and published a repository signing key
  • Set up a repository with Reprepro, the repository manager
  • Made the repository public with the web server Nginx
  • Added the repository on another server

Prepare and Publish a Signing Key

repository illustration for: Prepare and Publish a Signing Key

First, we need a valid package signing key. This step is crucial for a secure repository, since we will be digitally signing all the packages. Package signing gives the downloader confidence that the source can be trusted.

In this section, you will generate an encrypted master public key and a signing subkey by following these steps:

  • Generate a Master Key
  • Generate a Subkey for Package Signing
  • Detach Master Key from Subkey

Generate a Master Key

Let's make the master key. This key should be kept safe and secure since this is what people will be trusting.

Before we begin, let's install <^>rng-tools<^> though <^>apt-get<^>:

				
					
apt-get install rng-tools

				
			

GPG requires random data, called entropy, to generate keys. Entropy is normally generated over time by the Linux kernel and stored in a pool. However, on cloud servers (like Droplets), the kernel may have trouble generating the amount of entropy required by GPG. To help the kernel, we install the rngd program (found in the rng-tools package). This program will ask the host server (where the Droplets are located) for entropy. Once retrieved, rngd will add the data to the entropy pool to be used by other applications like GPG.

If you get a message like this:

				
					
Trying to create /dev/hwrng device inode...

Starting Hardware RNG entropy gatherer daemon: (failed).

invoke-rc.d: initscript rng-tools, action "start" failed.

				
			

Start the <^>rngd<^> daemon manually with:

				
					
rngd -r /dev/urandom

				
			

By default rngd looks for a special device to retrieve entropy from <^>/dev/hwrng<^>. Some Droplets do not have this device. To compensate we use the pseudo random device <^>/dev/urandom<^> by specifying the <^>-r<^> directive. For more information, you can check out our tutorial: How to Setup Additional Entropy.

Now that we have a pool of entropy, we can generate the master key. Do this by invoking the command <^>gpg<^>. You will see a prompt similar to the following:

				
					
gpg --gen-key

				
			
				
					
gpg (GnuPG) 1.4.16; Copyright (C) 2013 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.



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? &lt;^&gt;1&lt;^&gt;

				
			

Specify the first option, “RSA and RSA (default)” 1, in the prompt. Selecting this will have <^>gpg<^> generate first a signing key, then a encryption subkey (both using the RSA algorithm). We don’t need an encryption key for this tutorial, but as a great person once said, “why not?” There is no disadvantage in having both, and you may use the key for encryption in the future.

Hit <^>Enter<^> and you’ll be prompted for a keysize:

				
					
RSA keys may be between 1024 and 4096 bits long.

What keysize do you want? (2048) &lt;^&gt;4096&lt;^&gt;

				
			

The key size correlates directly to how secure you want your master key to be. The higher the bit size, the more secure key. The Debian project recommends using 4096 bits for any signing key, so I would specify <^>4096<^> here. For the next 2-5 years the default bit size 2048 is sufficient if you'd rather use that. A size of 1024 is uncomfortably close to being unsafe and should not be used.

Press <^>Enter<^> for the expire prompt.

				
					
Please specify how long the key should be valid.

         0 = key does not expire

      &lt;n&gt;  = key expires in n days

      &lt;n&gt;w = key expires in n weeks

      &lt;n&gt;m = key expires in n months

      &lt;n&gt;y = key expires in n years

Key is valid for? (0) &lt;^&gt;0&lt;^&gt;

				
			

Master keys do not normally have expiration dates, but set this value as long as you expect to use this key. If you only plan to use this repository for only the next 6 months you can specify <^>6m<^>. <^>0<^> will make it valid forever.

Hit <^>Enter<^>, then <^>y<^>. You will be prompted to generate a “user ID”. This information will be used by others and yourself to identify this key – so use real information!

				
					
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) &lt;heinrichh@duesseldorf.de&gt;"



Real name: &lt;^&gt;Mark Lopez&lt;^&gt;

Email address: &lt;^&gt;mark.lopez@example.com&lt;^&gt;

Comment: 

You selected this USER-ID:

    "Mark Lopez &lt;mark.lopez@example.com&gt;"



Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? &lt;^&gt;o&lt;^&gt;

				
			

If the information is correct, hit <^>o<^> and <^>Enter<^>. We need to add a password to ensure that only you have access to this key. Make sure to memorize this password since there is no way to recover a gpg key password (a good thing).

				
					
You need a Passphrase to protect your secret key.



Enter passphrase: &lt;^&gt;(hidden)&lt;^&gt;

Repeat passphrase: &lt;^&gt;(hidden)&lt;^&gt;

				
			

Now for some magic (math) to happen. This might take a little while, so sit back or get a cup of your favorite drink.

				
					
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.



Not enough random bytes available.  Please do some other work to give

the OS a chance to collect more entropy! (Need 300 more bytes)

+++++

................+++++

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: /root/.gnupg/trustdb.gpg: trustdb created

gpg: key 10E6133F 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   4096R/&lt;^&gt;10E6133F&lt;^&gt; 2014-08-16

      Key fingerprint = 1CD3 22ED 54B8 694A 0975  7164 6C1D 28A0 10E6 133F

uid                  Mark Lopez &lt;mark.lopez@example.com&gt;

sub   4096R/&lt;^&gt;7B34E07C&lt;^&gt; 2014-08-16

				
			

Now we have a master key. The output shows that we created a master key for signing (`<^>0E6133F<^> on the <^>pub<^> line above). Your key will have different IDs. Make note of your signing key’s ID (the example uses <^>10E6133F<^>). We’ll need that information in the next steps when creating another subkey for signing.

Generate a Subkey for Package Signing

Now we'll create a second signing key so that we don’t need the master key on this server. Think of the master key as the root authority that gives authority to subkeys. If a user trusts the master key, trust in a subkey is implied.

In the terminal execute:

				
					


Replace the example ID with your key's ID. This command enters us into the &lt;^&gt;gpg&lt;^&gt; environment. Here we can edit our new key and add a subkey. You'll see the following output:



				
			

gpg (GnuPG) 1.4.16; Copyright (C) 2013 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.

Secret key is available.

pub 4096R/10E6133F created: 2014-08-16 expires: never usage: SC

trust: ultimate validity: ultimate

sub 4096R/7B34E07C created: 2014-08-16 expires: never usage: E

[ultimate] (1). Mark Lopez <[email protected]>

gpg>

				
					


At the prompt, type `addkey`:



				
			

addkey

				
					


Press &lt;^&gt;Enter&lt;^&gt;. GPG will prompt for your password. Enter the password that you used to encrypt this key. 



				
			

Key is protected.

You need a passphrase to unlock the secret key for

user: "Mark Lopez <[email protected]>"

4096-bit RSA key, ID 10E6133F, created 2014-08-16

gpg: gpg-agent is not available in this session

Enter passphrase: <hidden>

				
					


You will see the following prompt for key type. 



				
			

Please select what kind of key you want:

(3) DSA (sign only)

(4) RSA (sign only)

(5) Elgamal (encrypt only)

(6) RSA (encrypt only)

Your selection? <^>4<^>

				
					


We want to create a &lt;i&gt;signing&lt;/i&gt; subkey, so select "RSA (sign only)” `4`. RSA is faster for the client, while DSA is faster for the server. We're picking RSA in this case because, for every signature that we make on a package, possibly hundreds of clients will need to verify it. The two types are equally secure. 



Again we are prompted for a key size. 



				
			

RSA keys may be between 1024 and 4096 bits long.

What keysize do you want? (2048) <^>4096<^>

				
					


This tutorial uses &lt;^&gt;4096&lt;^&gt; for increased security. 



				
			

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) <^>1y<^>

				
					


We already have a master key, so the expiration time for the subkey is less important. One year is a good time frame. 



Hit Enter, and then type &lt;^&gt;y&lt;^&gt; (yes) twice for the next two prompts. Some math will generate another 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.

…………+++++

.+++++

pub 4096R/10E6133F created: 2014-08-16 expires: never usage: <^>SC<^>

trust: ultimate validity: ultimate

sub 4096R/7B34E07C created: 2014-08-16 expires: never usage: <^>E<^>

sub 4096R/<^>A72DB3EF<^> created: 2014-08-16 expires: 2015-08-16 usage: <^>S<^>

[ultimate] (1). Mark Lopez <[email protected]>

gpg>

				
					


Type &lt;^&gt;save&lt;^&gt; at the prompt.



				
			

save

				
					


In the output above, the &lt;^&gt;SC&lt;^&gt; from our master key tells us that the key is only for signing and certification. The &lt;^&gt;E&lt;^&gt; means the key may only be used for encryption. Our signing key can be correctly seen with only the &lt;^&gt;S&lt;^&gt;. 



Note your new signing key’s ID (the example shows &lt;^&gt;A72DB3EF&lt;^&gt; on the second &lt;^&gt;sub&lt;^&gt; line above). Your key's ID will be different. 



Enter &lt;^&gt;save&lt;^&gt; to return to the terminal and to save your new key. 



				
			

save

				
					


### Detach Master Key From Subkey 



The point of creating the subkey is so we don't need the master key on our server, which makes it more secure. Now we'll detach our master key from our subkey. We will need to export the master key and subkey, then delete the keys from GPG's storage, then re-import just the subkey. 



First let's use the &lt;^&gt;--export-secret-key&lt;^&gt; and &lt;^&gt;--export&lt;^&gt; commands to export the whole key. Remember to use your master key's ID!



				
			

gpg –export-secret-key <^>10E6133F<^> > private.key

gpg –export <^>10E6133F<^> >> private.key

				
					


By default &lt;^&gt;--export-secret-key&lt;^&gt; and &lt;^&gt;--export&lt;^&gt; will print the key to our console, so instead we pipe the output to a new file (&lt;^&gt;private.key&lt;^&gt;). Make sure to specify your own master key ID, as noted above. 



**Important: Make a copy of the &lt;^&gt;private.key&lt;^&gt; file somewhere safe** (not on the server). Possible locations are on a floppy disk or USB drive. This file contains your private key, your public key, your encryption subkey, and your signing subkey. 



**After you have backed up this file to a safe location,** delete the file: 



				
			

#back up the private.key file before running this# rm private.key

				
					


Now export your public key and your subkey. Make sure to change the IDs to match the master key and the second subkey that you generated (don't use the first subkey). 



				
			

gpg –export <^>10E6133F<^> > public.key

gpg –export-secret-subkeys <^>A72DB3EF<^> > signing.key

				
					


Now that we have a backup of our keys we can remove our master key from our server. 



				
			

gpg –delete-secret-key <^>10E6133F<^>

				
					


Re-import only our signing subkey. 



				
			

gpg –import public.key signing.key

				
					


Check that we no longer have our master key on our server:



				
			

gpg –list-secret-keys

sec<^>#<^> 4096R/10E6133F 2014-08-16

uid Mark Lopez <[email protected]>

ssb 4096R/7B34E07C 2014-08-16

ssb 4096R/A72DB3EF 2014-08-16

				
					


Notice the **#** after **sec**. This means our master key is not installed. The server contains only our signing subkey.



Clean up your keys:



				
			

rm public.key signing.key

				
					


The last thing you need to do is publish your signing key. 



				
			

gpg –keyserver keyserver.ubuntu.com –send-key <^>10E6133F<^>

				
					


This command publishes your key to a public storehouse of keys – in this case Ubuntu’s own key server. This allows others to download your key and easily verify your packages. 



## Set Up a Repository Using Reprepro



Now let's get to the point of this tutorial: creating an apt-get repository. Apt-get repositories are not the easiest things to manage. Thankfully [R. Bernhard](https://packages.qa.debian.org/r/reprepro.html) created Reprepro, who used to “produce, manage and sync a local repository of Debian packages” (also known as Mirrorer). Reprepro is under the GNU licence and completely open source. 



### Install and Configure Reprepro 



Reprepro can be installed from the default Ubuntu repositories. 



				
			

apt-get update

apt-get install reprepro

				
					


Configuration for Reprepro is repository-specific, meaning you can have different configurations if you make multiple repositories. Let's first make a home for our repository.



Make a dedicated folder for this repository and move to it.



				
			

mkdir -p /var/repositories/

cd /var/repositories/

				
					


Create the configuration directory.



				
			

mkdir conf

cd conf/

				
					


Create two empty config files (&lt;^&gt;options&lt;^&gt; and &lt;^&gt;distributions&lt;^&gt;).



				
			

touch options distributions

				
					


Open up the &lt;^&gt;options&lt;^&gt; file in your favorite text editor (&lt;^&gt;nano&lt;^&gt; is installed by default).



				
			

nano options

				
					


This file contains options for Reprepro and will be read every time Reprepro runs. There are several options that you can specify here. See the manual for the other options.  



In your text editor add the following. 



				
			

ask-passphrase

				
					


The &lt;^&gt;ask-passphrase&lt;^&gt; directive tells Reprepro to request a GPG password when signing. If we don’t add this to the options Reprepro will die if our key is encrypted (it is).  



Ctrl + x then y and Enter will save our changes and return to the console. 



Open the &lt;^&gt;distributions&lt;^&gt; file.



				
			

nano distributions

				
					


This file has four required directives. Add these to the file.

 

				
			

Codename: <^>trusty<^>

Components: <^>main<^>

Architectures: <^>i386 amd64<^>

SignWith: <^>A72DB3EF<^>

				
					


The &lt;^&gt;Codename&lt;^&gt; directive directly relates to the code name of the released Debian distributions and is required. This is the code name for the distribution that will be downloading packages, and doesn't necessarily have to match the distribution of this server. For example, the Ubuntu 14.04 LTS release is called **trusty**, Ubuntu 12.04 LTS is called **precise**, and Debian 7.6 is known as **wheezy**. This repository is for Ubuntu 14.04 LTS so **trusty** should be set here.



The &lt;^&gt;Components&lt;^&gt; field is required. This is only a simple repository so set `main` here. There are other namespaces such as “non−free” or “contrib” – refer to apt-get for proper naming schemes.



&lt;^&gt;Architectures&lt;^&gt; is another required field. This field lists binary architectures within this repository separated by spaces. This repository will be hosting packages for 32-bit and 64-bit servers, so &lt;^&gt;i386 amd64&lt;^&gt; is set here. Add or remove architectures as you need them. 



To specify how other computers will verify our packages we use the &lt;^&gt;SignWith&lt;^&gt; directive. This is an optional directive, but required for signing. The signing key earlier in this example had the ID &lt;^&gt;A72DB3EF&lt;^&gt;, so that is set here. Change this field to match the subkey’s ID that you generated. 



Save and exit from the file with Ctrl + `x then y and Enter. 



You have now set up the required structure for Reprepro.  



### Add a Package with Reprepro



First let's change our directory to a temporary location. 



				
			

mkdir -p /tmp/debs

cd /tmp/debs

				
					


We need some example packages to work with – &lt;^&gt;wget&lt;^&gt; them with:



				
			

wget https://github.com/Silvenga/examples/raw/master/example-helloworld_1.0.0.0_amd64.deb

wget https://github.com/Silvenga/examples/raw/master/example-helloworld_1.0.0.0_i386.deb

				
					


These packages were made purely for this guide and contain a simple bash script to prove the functionality of our repository. You can use different packages if you wish.



Running the program `ls` should give us this layout:



				
			

ls

				
					


				
			

example-helloworld_1.0.0.0_amd64.deb example-helloworld_1.0.0.0_i386.deb

				
					


We now have two example packages. One for 32-bit (i386) computers, another for 64-bit (amd64) computers. You can add them both to our repository with:



				
			

reprepro -b /var/repositories includedeb <^>trusty<^> example-helloworld_1.0.0.0_*

				
					


The **-b** argument specifies the “(b)ase” directory for the repository. The &lt;^&gt;includedeb&lt;^&gt; command requires two arguments -  `&lt; distribution code name &gt; and &lt; file path(s) &gt;`. Reprepro will prompt for our subkey passcode twice. 



				
			

Exporting indices…

C3D099E3A72DB3EF Mark Lopez <[email protected]> needs a passphrase

Please enter passphrase: <^>< hidden ><^>

C3D099E3A72DB3EF Mark Lopez <[email protected]> needs a passphrase

Please enter passphrase: <^>< hidden ><^>

				
					


Success! 



### Listing and Deleting



We can list the managed packages with the &lt;^&gt;list&lt;^&gt; command followed by the codename. For example: 



				
			

reprepro -b /var/repositories/ list <^>trusty<^>

trusty|main|i386: example-helloworld 1.0.0.0

trusty|main|amd64: example-helloworld 1.0.0.0

				
					


To delete a package, use the &lt;^&gt;remove&lt;^&gt; command. The remove command requires the codename of the package, and the package name. For example:



				
			

reprepro -b /var/repositories/ remove <^>trusty<^> <^>example-helloworld<^>

				
					


## Make the Repository Public



We now have a local package repository with a couple of packages. Next, we'll install Nginx as a web server to make this repository public. 



Install Nginx



				
			

apt-get update

apt-get install nginx

				
					


Nginx comes installed with a default example configuration. Make a copy of the file in case you want to look at it at another time. 



				
			

mv /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak

touch /etc/nginx/sites-available/default

				
					


Now that we have an empty configuration file, we can start configuring our Nginx server to host our new repository.



Open the configuration file with your favorite text editor.

 

				
			

nano /etc/nginx/sites-available/default

				
					


And add the following configuration directives:



				
			

server {

Let your repository be the root directory

root /var/repositories;

Always good to log

access_log /var/log/nginx/repo.access.log;

error_log /var/log/nginx/repo.error.log;

Prevent access to Reprepro's files

location ~ /(db|conf) {

deny all;

return 404;

}

}

				
					


Nginx has some pretty sane defaults. All we needed to configure was the root directory, while denying access to Reprepro’s files. See the in-line comments for more details.



Restart the Nginx service to load these new configurations.



				
			

service nginx restart

				
					


Your public Ubuntu repository is ready to use!



You'll need your Droplet's IP address to let users know the location of the repository. If you don’t know your Droplet's public address you can find it with &lt;^&gt;ifconfig&lt;^&gt;.



				
			

ifconfig eth0

				
					


				
			

eth0 Link encap:Ethernet HWaddr 04:01:23:f9:0e:01

inet addr:<^>198.199.114.168<^> Bcast:198.199.114.255 Mask:255.255.255.0

inet6 addr: fe80::601:23ff:fef9:e01/64 Scope:Link

UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1

RX packets:16555 errors:0 dropped:0 overruns:0 frame:0

TX packets:16815 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:1000

RX bytes:7788170 (7.7 MB) TX bytes:3058446 (3.0 MB)

				
					


In the above example, the server’s address is 198.199.114.168. Yours will be different. 



With your Reprepro server's IP address, you can now add this repository to any other appropriate server. 



## Install a Package from Our New Repository



If you haven't already, spin up another Droplet with Ubuntu 14.04 LTS, so that you can do a test installation from your new repository. 



On the new server, download your public key to verify the packages from your repository. Recall that you published your key to &lt;^&gt;keyserver.ubuntu.com&lt;^&gt;. 



This is done with the &lt;^&gt;apt-key&lt;^&gt; command. 



				
			

apt-key adv –keyserver keyserver.ubuntu.com –recv-keys <^>10E6133F<^>

				
					


This command downloads the specified key and adds the key to the apt-get database. The &lt;^&gt;adv&lt;^&gt; command tells &lt;^&gt;apt-key&lt;^&gt; to use GPG to download the key. The other two arguments are passed directly to GPG. Since you uploaded your key to “keyserver.ubuntu.com” use the &lt;^&gt;--keyserver keyserver.ubuntu.com&lt;^&gt; directive to retrived the key from the same location. The &lt;^&gt;--recv-keys &lt;key ID&gt;&lt;^&gt; directive specifies the exact key to add. 



Now add the repository's address for &lt;^&gt;apt-get&lt;^&gt; to find. You'll need your repository server's IP address from the previous step. This is easily done with the &lt;^&gt;add-apt-repository&lt;^&gt; program. 



				
			

add-apt-repository "deb http://<^>198.199.114.168<^>/ trusty main"

				
					


Note the string that we give &lt;^&gt;add-apt-repository&lt;^&gt;. Most Debian repositories can be added with the following general format:



				
			

deb (repository location) (current distribution code name) (the components name)

				
					


The repository location should be set to the location of your server. We have an HTTP server so the protocol is **http://**. The example’s location was &lt;^&gt;198.199.114.168&lt;^&gt;. Our server’s code name is **trusty**. This is a simple repository, so we called the component "main".  



After we add the repository, make sure to run an &lt;^&gt;apt-get update&lt;^&gt;. This command will check all the known repositories for updates and changes (including the one you just made). 



				
			

apt-get update

				
					


After updating apt-get, you can now install the example package from your repository. Use the &lt;^&gt;apt-get&lt;^&gt; command normally. 



				
			

apt-get install example-helloworld

				
					


If everything is successful you can now execute &lt;^&gt;example-helloworld&lt;^&gt; and see:



				
			

Hello, World!

This package was successfully installed!

				
					


Congratulations! You have just installed a package from the repository that you created!



To remove the example package, run this command:



				
			

apt-get remove example-helloworld