Here are the steps I took to not be vulnerable by default, and to not listen on non-critical ports:
- Add "-Dhudson.udp=53" to JENKINS_JAVA_OPTIONS
This is a 'trick' to disable the UDP mcast listen on UDP port 33848. It works for me because I also happen to have a DNS server on the host which is already bound to UDP/53. If I set this option to -1 (the standard way to disable ports in other Jenkins contexts), the server fails to start with (java.lang.IllegalArgumentException: port out of range:-1). So as far as I know, this can't be disabled, just blocked.
- Add "-Dhudson.DNSMultiCast.disabled=true" to JENKINS_JAVA_OPTIONS
This disables DNS multicast, which listens on UDP port 5353.
- Add "--ajp13Port=-1" to JENKINS_ARGS
This disables AJP, which listens on TCP port 8009.
- Add "-
argumentsRealm.passwd" and "-argumentsRealm.roles" to JENKINS_ARGS as per https://wiki.jenkins-ci.org/display/JENKINS/Quick+and+Simple+Security
- Start the service
- IMMEDIATELY go to Manage Jenkins / Configure System, Enable security (Delegate to servlet container, Matrix-based security), and save
This requires adding the admin user defined above, granting all permissions, and leaving the anonymous user with NO permissions. If there is an error in this step, the instance is effectively locked out and needs to be reset. During this period of time, any person or script could log into a public-facing instance and begin executing local commands as the service user via a project.
- In Configure System, also disable TCP port for JNLP slave agents and SSH Server / SSHD Port
This gets the instance to the point where it is listening only on port 8080.
- In Configure System, Prevent Cross Site Request Forgery exploits
Note: "Default Crumb Issuer" is the only valid option for this, but if it isn't manually selected this operation will not save.
After doing all of this, one has a Jenkins instance listening on port 8080 alone, with no anonymous rights. Outside the scope of this issue would be the setting up of HTTPS, either directly or via proxy with a local listening IP for Jenkins itself.
I think there are numerous improvements that could be made to this process:
- allow all non-essential ports to be disabled cleanly via JENKINS_ARGS, or ideally to not listen by default at all
- allow anonymous lockdown to be a default or a pre-start option (absent manually copying over /var/lib/jenkins config directories?)
- enable Cross Site Request Forgery exploit prevention by default
At present, I don't like the amount of workarounds and research that are necessary to get an instance into a reasonably secure state.
If this issue should be broken into sub-issues, let me know which ones and I'd be happy to do so. Thanks for the product!