I’ve been helping an awesome colleague on DevOps for our Jenkins farm, which we use
for continuous integration and continuous deployment to our preproduction environments.
We are really trying to do it right:
Use Puppet to provision the Jenkins master, Linux VM build slaves, Windows VM slaves,
and even OS X bare metal slaves (for iOS builds)
Automated backups of Jenkins config files to a private GitHub repo for disster recovery
Patches the GitHub OAuth plugin to make sure you have the same collaborator permissions
(read/write/admin) in a Jenkins job as you do the GitHub repo.
Have a Jenkins staging environment to test upgrades to Jenkins and plugins to avoid surprises.
Run Jenkins on the Long Term Support (LTS) release channel to avoid surprises.
I wish my shop used CentOS or Debian; sadly we are stuck on SUSE Enterprise. SUSE is really
good at turning 5 minute tasks on CentOS or Debian into uber frustrating hour-long ordeals.
One of the glitches we faced was running the Jenkins web UI on port 80. SUSE lacks the
authbind package for binding to port below port 1024 as a non-root user. We wanted to run the
Jenkins deamon as a regular privilege user, so running as root was not an option.
We are currently smoke testing this LSB /etc/init.d/jenkins.portforwarding
script, which is
just a wrapper around iptables
. So far, it seems to get the job done.
#!/bin/sh
#
# SUSE system statup script for port forwarding port 80 to the port
# for the Jenkins continuous build server.
#
# Jenkins normally does not run as root, thus /etc/init.d/jenkins cannot
# bind to ports < 1024 since SUSE does not support authbind (or similar).
#
# The MIT License (MIT)
#
# Copyright (C) 2014 Steve Jansen
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
### BEGIN INIT INFO
# Provides: jenkins.portforwarding
# Required-Start: jenkins
# Should-Start:
# Required-Stop: jenkins
# Should-Stop:
# Default-Start: 3 5
# Default-Stop: 0 1 2 6
# Short-Description: Port forwarding for Jenkins continuous build server
# Description: Forward port 80 traffic to the Jenkins continuous build server
### END INIT INFO
# Check for existence of needed config file and read it
JENKINS_CONFIG=/etc/sysconfig/jenkins
JENKINS_FORWARD_PORT=80
test -r "$JENKINS_CONFIG" || { echo "$JENKINS_CONFIG not existing";
if [ "$1" = "stop" ]; then exit 0;
else exit 6; fi; }
# Read config
. "$JENKINS_CONFIG"
. /etc/rc.status
rc_reset # Reset status of this service
if [ "$JENKINS_PORT" -eq "$JENKINS_FORWARD_PORT" ]
then
echo "Jenkins already running on port $JENKINS_FORWARD_PORT, skipping port forwarding"
rc_exit
fi
readonly iptables_table="-t nat"
readonly base_rule="-p tcp --dport $JENKINS_FORWARD_PORT -j REDIRECT --to-ports $JENKINS_PORT"
readonly external_rule="PREROUTING -p tcp -m tcp --dport $JENKINS_FORWARD_PORT -j REDIRECT --to-ports $JENKINS_PORT"
readonly loopback_rule="OUTPUT -p tcp -m tcp --dport $JENKINS_FORWARD_PORT -j REDIRECT --to-ports $JENKINS_PORT"
check_status ()
{
iptables-save $iptables_table | grep -- "$external_rule" > /dev/null 2>&1 && iptables-save $iptables_table | grep -- "$loopback_rule" > /dev/null 2>&1
return $?
}
case "$1" in
start)
check_status
if [ $? -eq 0 ]
then
echo "Jenkins port forwarding already forwarding port $JENKINS_FORWARD_PORT to port $JENKINS_PORT"
rc_exit
fi
echo "Starting Jenkins port forwarding"
echo "Enabling iptables port forwarding from port $JENKINS_FORWARD_PORT to port $JENKINS_PORT"
iptables $iptables_table -A $external_interface $external_rule && iptables $iptables_table -A $loopback_rule
result=$?
if [ $result -eq 0 ]
then
rc_status -v
else
rc_failed $result
rc_status -v
fi
;;
stop)
check_status
if [ $? -ne 0 ]
then
echo "Jenkins port forwarding already stopped"
rc_exit
fi
echo "Disabling iptables port forwarding from port $JENKINS_FORWARD_PORT to port $JENKINS_PORT"
iptables $iptables_table -D $external_interface $external_rule && iptables $iptables_table -D $loopback_rule
result=$?
if [ $result -eq 0 ]
then
rc_status -v
else
rc_failed $result
rc_status -v
fi
;;
restart)
$0 stop
$0 start
rc_status
;;
status)
echo "Checking status of iptables port forwarding from port $JENKINS_FORWARD_PORT to port $JENKINS_PORT"
check_status
result=$?
if [ $result -eq 0 ]
then
echo "Port forwarding from port $JENKINS_FORWARD_PORT to port $JENKINS_PORT is enabled"
rc_status -v
else
echo "Port forwarding from port $JENKINS_FORWARD_PORT to port $JENKINS_PORT is disabled"
rc_failed $result
rc_status -v
fi
;;
*)
echo "Usage: $0 {start|stop|status}"
exit 1
;;
esac
rc_exit
If all goes well, I will merge this logic into a pull request for the Jenkins init.d script for OpenSuse .