Jenkins comes with an embedded HTTP server, which you can reverse proxy with nginx - but what do you need to lock down and where do you need to do it?
## The Problem
Here's a demonstration of the ports that Jenkins listens on, out of the box:
$ java -jar jenkins.war & [...time passes...] $ netstat -npl | grep java | sort | tr -s ' ' tcp6 0 0 :::37510 :::* LISTEN 22736/java tcp6 0 0 :::49635 :::* LISTEN 22736/java tcp6 0 0 :::8009 :::* LISTEN 22736/java tcp6 0 0 :::8080 :::* LISTEN 22736/java udp6 0 0 :::33848 :::* 22736/java udp6 0 0 :::5353 :::* 22736/java
And if we kill java and immediately start it up again (bear with me), here's the result of those same checks:
$ java -jar jenkins.war & [...time passes...] $ netstat -npl | grep java | sort | tr -s ' ' tcp6 0 0 :::21319 :::* LISTEN 22797/java tcp6 0 0 :::59271 :::* LISTEN 22797/java tcp6 0 0 :::8009 :::* LISTEN 22797/java tcp6 0 0 :::8080 :::* LISTEN 22797/java udp6 0 0 :::33848 :::* 22797/java udp6 0 0 :::5353 :::* 22797/java
So we have 2 ports that seem to change (tcp/37510/21319 & tcp/49635/59271), along with the following:
tcp/8009 tcp/8080 udp/33848 udp/5353
I've chosen to try and disable as many of these listeners as possible, and to leave HTTP traffic as the sole route into the server. This will probably cause some problems when bringing slave instances online, later, but it'll do as a single-node setup until then.
This is the AJP port, used by an alternative protocol to HTTP on Java
servers. Disable it with
This is the default port that Jenkins (or, rather the embedded Winstone HTTP server) uses for HTTP traffic. There's nothing wrong with that, per se, but given that we're going to hide Jenkins behind the more robust nginx, we probably want to tell Jenkins only to listen on localhost, not 0.0.0.0.
We do this with
Additionally, if you don't have 127.0.0.1:8080 available for Jenkins to
use, the port can be changed with (for example)
This appears to be some sort of well-known slave-related port which, after receiving traffic, tell anyone connected the value of one of the randomly selected ports we saw, above. To disable this, we're going to have to be a bit sneaky, because there isn't a way to disable it at the CLI (or inside Jenkins config) as far as I could tell.
We make the essential assumption here that you're not running Jenkins/Java as root. If you are doing this, please take a refesher class at Sysadmin School.
In order to disable this listener, we merely have to tell Jenkins it should use a privileged port below 1024, which will then fail to bind as we're not running as root. I like to use 1023, since it's so close to 1024 that it can't be a coincidence, which tells the next Sysadmin looking at this something. Hopefully.
Do this with
-Dhudson.udp=1023, but make sure it comes before the
jenkins.war CLI parameter or it won't work.
If your first thought on seeing those "53"s was "DNS?", then give yourself a
cookie. It can be disabled with
-Dhudson.DNSMultiCast.disabled=true, but only
when placed before the
Whilst it doesn't appear Jenkins tries to set up an HTTPS listener out of the
box, that behaviour might change, and might be affected by other configuration
settings in your environment. To definitively disable HTTPS, use
That's the end of the predictable ports. Now for the 2 random ports Jenkins opens.
Unfortunately, they don't appear to be controlled at the command line, but from config files and settings. That means you need to run Jenkins once, poke it slightly to cause it to create its config files, and then shut it down and edit them.
Using the command line we've built up thus far, run Jenkins and wait for it to say "INFO: Jenkins is fully up and running":
$ java -Dhudson.DNSMultiCast.disabled=true -Dhudson.udp=1023 -jar \ jenkins.war --httpListenAddress=127.0.0.1 --httpPort=9005 \ --ajp13Port=-1 --httpsPort=-1
Now hit the Jenkins web UI on whatever address you've told it to listen on. Then:
- Go to "Manage Jenkins"
- Go to "Configure"
- Hit the "Save" button at the bottom of the page
Wait a couple of seconds to make sure the config gets written to disk.
Back at the server's CLI, hit Ctrl-C and check Jenkins shuts down.
In your $HOME/.jenkins/ directory, edit the file
config.xml and change the
<slaveAgentPort> from 0 to -1.
Finally, put the following contents into
<?xml version='1.0' encoding='UTF-8'?> <org.jenkinsci.main.modules.sshd.SSHD> <port>-1</port> </org.jenkinsci.main.modules.sshd.SSHD>
Let's start up Jenkins with these changes, and see what's now listening for network traffic:
$ java -Dhudson.DNSMultiCast.disabled=true -Dhudson.udp=1023 -jar \ jenkins.war --httpListenAddress=127.0.0.1 --httpPort=9005 \ --ajp13Port=-1 --httpsPort=-1 & [...time passes...] $ sudo netstat -npl | grep java | tr -s ' ' tcp6 0 0 127.0.0.1:9005 :::* LISTEN 23662/java
Only the single HTTP port, on localhost, that we need for reverse proxying. Success!
Though you should probably think about putting some authorisation on that at the nginx layer now …