Jenkins: Running Steps as sudo

I came across a situation recently where I needed my Jenkins user to be able to run commands as sudo. I have a job that is scheduled nightly to install and update dependencies prior to running my builds. Since apt-get needs to be run with sudo, I needed some way to workaround the password input step.

The simplest approach is to utilize the sudoers file to enable a "no password" exception for our Jenkins user.

These steps apply to both Linux and OSX

The sudo Group

In order for your jenkins user to use the sudo command, make sure they are in the sudo group. If not, you can add them via:

usermod -a -G sudo jenkins

The sudoers File

You can control a user's sudo privileges with the sudoers file. On most systems, this can be found at /etc/sudoers. We will use this file to enable our jenkins user to issue sudo without requiring a password.

Here's what my sudoers file looks like prior to editing it:

#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
#
Defaults    env_reset
Defaults    mail_badpass
Defaults    secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"

# Host alias specification

# User alias specification

# Cmnd alias specification

# User privilege specification
root    ALL=(ALL:ALL) ALL

# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL

# Allow members of group sudo to execute any command
%sudo    ALL=(ALL:ALL) ALL

# See sudoers(5) for more information on "#include" directives:

#includedir /etc/sudoers.d

Editing the sudoers file

In order to edit the sudoers file, we must use the visudo command. This command performs some status checks that helps prevent errors inside your sudoers file. ALWAYS USE visudo! If you make a mistake, visudo will save your modifications to a temporary file; the official sudoers file will only be overwritten if your modifications generate no parsing errors.

Open the sudoers file for editing with the following command:

sudo visudo -f /etc/sudoers

We can enable our jenkins user to run a script by adding a line to the sudoers file. If our script or command is in a fixed location, we can specify it precisely:

username hostname = (root) NOPASSWD: /path/to/command

But in my case, the script is not kept at a fixed location. Instead, I opted for the less secure approach of allowing things inside of the jenkins workspace directory to be run as sudo without a password. If you terminate the path string with a /, sudo can be used to run commands inside that directory, but not any subdirectories. We're getting close with this feature, but unfortunately Jenkins does not always place my builds in the same place.

By coupling the command with a wildcard, I can make sure scripts that are available inside of the setup/ subdirectory of all projects starting with ea can be run as sudo.

Here's an example for the user jenkins on the machine build-1:

jenkins build-1 = (root) NOPASSWD: /var/lib/jenkins/workspace/ea*/setup/

Note that if there are multiple entries for a user inside of the /etc/sudoers file, sudo uses the last rule that applies. You can set multiple rules for specific files if you desire. Just remember to keep the most generic rule first, and all exceptions afterward.

Alternative: /etc/sudoers.d

We can create a file inside of the /etc/sudoers.d directory instead of modifying the base sudoers file. This keeps the primary sudoers file clean and prevents any conflicts or errors during an upgrade. All files inside of the /etc/sudoers.d directory will be automatically included thanks to this command at the bottom of your sudoers file:

#includedir /etc/sudoers.d

To create a new file in /etc/sudoers.d with the correct permissions, use the following command :

sudo visudo -f /etc/sudoers.d/filename

Now we just need to include the relevant line in our file:

jenkins build-1 = (root) NOPASSWD: /var/lib/jenkins/workspace/ea*/setup/

And we're done!

Further Reading