Devops Discoveries

Creating a Secure Corporate Apt Repository with Salt

There are many reasons an organization could use it’s own internal apt repository. But controlling access to this repository for clients that are outside your internal network can be difficult. But if your repository contains proprietary or confidential packages, securing access is not optional. Thankfully apt supports client authentication with SSL certificates. And with the new x509 module, managing these certificates can be made fully automatic.

The x509 module is not yet in the latest release of salt, so you’ll need to manually add it to your custom paths.

cd /srv/salt/_modules
wget https://raw.githubusercontent.com/saltstack/salt/develop/salt/modules/x509.py
cd /srv/salt/_states
wget https://raw.githubusercontent.com/saltstack/salt/develop/salt/states/x509.py

Now setup targeting in the top file.

/srv/salt/top.sls

First the CA needs to be configured. It will need to create a CA private key and certificate, then publish that certificate to the mine where other minions will get it. It will also need to have a signing policy which allows the apt server and clients to get signed certificates.

Start by creating the signing policy configuraiton.

/srv/salt/pki/files/signing_policies.conf

Note that in the below state I’m triggering a restart of the salt-minion service when the configuration changes. You’ll need another state managing the status of salt-minion for this to work.

/srv/salt/pki/server.sls

To create the repository I’ll be using reprepro. A good gude to configuring reprepro can be found here Reprepro requires some configuration files to define the distributions and components. Here are these config files which salt will manage.

/srv/salt/aptrepo/server/files/conf/distributions

# Internal Trusty Packages
Origin: Internal #
Label: prod
Suite: trusty-prod
Codename: trusty-prod
Architectures: i386 amd64 source
Components: main
Description: Internal Trusty prod repository
Contents: .gz .bz2
Tracking: keep
SignWith: yes
Log: packages.trusty-prod.log

/srv/salt/aptrepro/server/files/conf/incoming

Name: incoming
IncomingDir: /srv/packages/incoming
TempDir: /srv/packages/tmp
Default: prod

/srv/salt/aptrepro/server/files/conf/options

outdir +b/www
logdir +b/logs
gnupghome +b/gpg

To sign packages added to the repository, gpg keys are required. Personally I opted to create the GPG keys locally, then copy them to the salt server where they will be managed.

cd /srv/salt/aptrepo/server/files
mkdir gpg
GNUPGHOME=gpg gpg --gen-key  # Make sure and use an empty password

Another file that needs to be managed is the NGINX configuration file. Notice the ssl_verify_client on;, this is what enables client authentication.

/srv/salt/aptrepo/server/files/nginx-default

server {
    listen	443;
    ssl on;
    ssl_certificate      /etc/nginx/certs/server.crt;
    ssl_certificate_key  /etc/nginx/certs/server.key;
    ssl_client_certificate /etc/nginx/certs/aptrepo_ca.crt;
    ssl_verify_client on;

    ## Let your repository be the root directory
    root        /srv/packages/www;
    autoindex on;

    ## Always good to log
    access_log  /var/log/nginx/repo.access.log;
    error_log   /var/log/nginx/repo.error.log;
}

Lastly, a script which will automatically import packages added to the incoming directory. Salt will create a cron job that regularly runs this script. Now adding packages to the internal repository is a simple matter of SCPing them to the incoming directory on the aptrepo server.

/srv/salt/aptrepo/server/files/processincoming.sh

#!/bin/sh
cd /srv/packages

for f in incoming/*.deb
do
	reprepro -C main includedeb trusty-prod $f
	rm $f
done

Now we’re ready to put it all together with a salt state.

/srv/salt/aptrepo/server/init.sls

Now to configure clients to be able to use the new repository. First apt needs to know that our repository requires a client certificate.

/srv/salt/aptrepo/client/files/45aptrepo-ssl

Acquire::https::aptrepo.example.com {
  Verify-Peer "true";
  Verify-Host "true";

  CaInfo "/etc/ssl/aptrepo_ca.crt";
  SslCert "/etc/ssl/aptrepo_client.crt";
  SslKey "/etc/ssl/aptrepo_client.key";
};

And now a client state to add the repository and generate the certificates clients will need to use it.

/srv/salt/aptrepo/client/init.sls

That it, now you have a fully managed internal repository on aptrepo. You can create expose this repository to the internet and only your clients trusted by salt and issued a client certificate will be able to use it.