Downloading Artifacts from Jenkins with Authentication
Some Background
Jenkins is a platform for build automation, and as such allows you to store the results of the build (the binaries, commonly known as “artifacts”) for later.
I saw other people on the internet manually downloading their results via the web UI:
However, I wanted to pull the artifacts from my automation platform (currently Chef), and ran into an authentication predicament.
The Problem
Normally, when using HTTP basic authentication, there’s a “challenge-response” mechanism, looking something like this:
However, Jenkins doesn’t challenge clients for credentials (response 401), and instead fails immediatly (response 403).
This is called “Preemptive authentication”, and is considered a bad habit because the client hands out credentials when it’s not definitely required. Therefore, most clients require special configuration to handle this.
For instance, when using wget
, one can use:
wget --auth-no-challenge --http-user=USER --http-password=BESTPASS http://server/jenkins
However, when using any tool where the URL is implicit, such as PIP or Chef’s remote_file, I can only provide credentials by specifying them in the URL (e.g. http://back:slasher@private.com/repo
), so I don’t have any way of modifying the authentication method.
The result - I can’t download artifacts directly from Jenkins, messing up my deployment cookbooks.
I thought about storing the results in a secondary server to act as a repo, but I really liked having Jenkins automatic maintenance (only keeping the last successful build’s artifact) and the simplicity of downloading from Jenkins directly, because less steps in building-downloading-installing means less places to fail.
The Solution
I ended up using Apache on the Jenkins server as a credentials-requiring-proxy, meaning that:
- Apache will require credentials using the good-old “challenge-response” method
- Apache will forward the request, including the now-provided crednetials, to Jenkins
- Jenkins will do it’s thing, providing the latest build’s artifacts
The setup was as pretty standard reverse proxy, except for the authentication part - I needed Apache to require credetials, but accept any non-empty set.
I used mod_authn_anon to require authentication with *
as the value, causing it to accept any user/password provided.
The result looks like this:
<VirtualHost *:1234>
<Location />
# Allow any user, but require one
Anonymous *
AuthType basic
AuthName 'Jenkins Proxy'
AuthBasicProvider anon
Require valid-user
</Location>
ProxyPass / http://localhost:1111/
</VirtualHost>
Where http://localhost:1111/
is Jenkins’ normal web UI.
Since I only use “end” URLs (as in not following redirections from the server), I didn’t need to add a ProxyPassReverse directive like ProxyPassReverse / http://localhost:1111/
, which causes Apache to rewrite HTTP headers to match the proxy rather than the original server.
I won’t post the entire apache configuration, because it’s pretty trivial. However, the modules I needed are:
- mod_proxy
- mod_proxy_http
- mod_auth_basic
- mod_authn_anon
Now everything works and I’m happy.