Jenkins - More than Just Target Practice
For a while, FortyNorth Security has been exploring different options that will automate building internal tools and running tests against code as it is pushed to a repository. The primary goal is to have an automated system easily and quickly ensure that the latest version of our code works (compiles) and does what is expected. During this time, Adam Chester published an excellent blog post on automating, building, and modifying code using Azure DevOps. If you are interested in automating everything within Azure, we highly recommend reading his blog. However, we wanted to reach our goal with a slightly different process: we wanted the option to work off the cloud, in a more protected environment. We were looking for a solution that could be used in-house with BitBucket or Gitlab and trigger builds anytime code is pushed. This is why, we decided to test using Jenkins.
If you are attending our red team training Intrusion Operations, we will share with you internally-developed custom scripts and tools that we use on a daily basis to perform assessments. You will be able to take this code home with you and use it for your own projects. Of course, this means you will most likely want to customize it to suit your own needs. Follow this Jenkins walkthrough to set up an environment where you can automatically test, compile, and locate errors in your code. You will save time and headaches with the added benefit of being able to perform all this testing in a safe, off the cloud, environment.
In our lab environment, we want to install BitBucket and Jenkins, each on their own separate Linux virtual machine. For our use case, we will test building a C# application. Therefore, we also have a Windows 10 VM with administrative rights and have installed Visual Studio. After installing each application and creating user accounts, we want to create a basic C# application and push it into its own repository within BitBucket. Within Jenkins, we installed the default suggested plugins (git, mercurial, svn, etc.), but also installed two extra plugins, specifically the “Bitbucket Plugin” and “MSBuild” plugin.
The Bitbucket plugin accepts and understands how to parse incoming web hooks from Bitbucket, which signals that a new build needs to occur. The MSBuild plugin allows Jenkins to build Visual Studio projects. After installing the above plugins, the next step is to configure both BitBucket and Jenkins to work with each other. Let’s start with BitBucket.
Within BitBucket, you will need to install the Post Webhooks for Bitbucket application (it’s free). Once installed, go into the repository that you want to have built every time that new code is pushed to it. If you look at the repository settings, you should see an option for “Post Webhooks” to click on.
We will configure how BitBucket notifies Jenkins of code pushes (or other triggers you might want), within the “Post Webhooks” screen. You will want to click “Add Webhook” and configure your Webhook similar to the following image.
The title of the Webhook can be anything you’d like, but we chose “Jenkins” since it clearly makes sense. The URL needs to point to your Jenkins instance, so specify it by IP or hostname. You specifically need to set the URI of the Webhook to be “/bitbucket-hook/” and make sure that you leave the trailing “/” (if you don’t configure it this way, the hook will not trigger a build). If you want to ignore any committers or branches, specify them. The status of the hook should be Active. Finally, you can specify the events which will fire the Webhook. In our case, we wanted a build every time code was pushed to the repository or a requested pull was merged in. Once you hit save, your configuration steps with BitBucket are complete.
Now, let’s configure Jenkins. First, we should enable a TCP port for JNLP agents (this is how Jenkins will instruct the remote Windows agent to build a new project). You can configure this within Manage Jenkins -> Configure Global Security. Scroll down until you find the “Agents” section, and configure the TCP port to be a fixed port of your choice.
Second, we need to install, on our Windows system, an agent that Jenkins can use as a distributed node. To do that, within Jenkins you will need to access the Manage Nodes menu (Configure Jenkins -> Manage Nodes). Once in this screen, click “New Node”.
You will want to provide your node a name of your choice, set it as a Permanent Agent, and hit “OK”. The next menu will ask you to configure your node.
Let’s go down the configurations one by one:
- Name – This should be pre-populated with the name you provided the node
- Description – Provide any description you would like for your node
- # of Executors – This basically means the number of simultaneous builds. According to Jenkins a good practice is one executor per processor
- Remote Root Directory – This is the directory where Jenkins will store configuration data and will build all source code. Make sure this is accessible to the Jenkins Agent or to the user account that is installing the agent
- Labels – Group this agent with other agents via a label assigned to them (give it “windows” for windows agents)
- Usage – You’ll likely want this configured to “Use this node as much as possible”
- Launch Method – For Windows, the easiest will be to set this to “Launch agent via Java Web Start”, and leave its configuration to the default values
- Availability – Keep this agent online as much as possible
- Tool Locations – You will need to install git on your Windows system and provide the path to the git binary.
Once your node is configured, you can hit “Add”. The next step is to install the agent on your Windows system via a screen similar to the image below (taken from https://wiki.jenkins.io/pages/viewpage.action?pageId=75893612). You can do this two ways, but in both cases you are required to run as admin. You can either run the specified java command, or you can download the jnlp executable (Launch agent from browser) and run it on your windows system.
The easiest way is to download the jnlp, run as admin, and once started tell it to install the Windows service. This will install a service that configures your window system to interact with Jenkins in the event of a reboot. Once you install the service, you should now see your agent checking in to Jenkins.
Now, we need to configure Jenkins to MSBuild. Go to the Global Tool Configuration menu (Manage Jenkins -> Global Tool Configuration), and add the value for MSBuild.
Provide the path to the appropriate MSBuild on your system (typically within your Visual Studio installation or C:\Windows\Microsoft.Net\Framework\<VERSION>\ directory).
Finally, it’s time to configure a job for Jenkins to perform. From the main Jenkins menu, select the “New Item” link. Give your item a name, select “Freestyle Project”, and hit “OK”. We’ll now go through each section of the job that you will need to configure.
For the general task info section, you can provide a description. We instruct Jenkins to maintain a maximum of 20 build (information about if the project compiles successfully or not). The only other option is specifying that this will only run on a specific node, and provide it the same label that we gave our Windows node.
Since we are using BitBucket, we’re going to configure this task to use Git for source code management. For the repository URL, provide the same URL you would use as if you were cloning the repo on the command line yourself. You can leave all sections at their default value, except for the “Credentials” information.
You will likely specify the “Kind” of authentication to be “Username with password” in which case you can specify the username and password for authentication, or “SSH Username with private key”. Feel free to choose whichever method works for you, just properly fill out the information. In the event that you are using “SSH Username with private key”, you will need to make sure that the SSH key is on the Window system where you are going to build your project (due to the fact that Windows will need to clone down the project). You can make a .ssh directory within the user account you are using on the Windows system and store the SSH keys in the standard location (id_rsa & id_rsa.pub).
For the build triggers, you want to select two options, the first being “Build when a change is pushed to BitBucket“. This setting will tell Jenkins to look for the Webhook from Bitbucket and build any time that it receives it. The other option is Poll SCM. This needs to be set as well, in order to ensure builds are automatically triggered from BitBucket. Since Jenkins will be receiving notifications for builds, you can set the poll frequency to be very low (as shown in the image).
The final section is telling Jenkins (and the agent) which version of MSBuild to use (make sure to select the version that you specified in the earlier step) and the specific file to build. In this test, we specified the ConsoleTest.sln file, because that’s what our application uses. If any command line arguments are needed, provide them as well, otherwise you can now hit “Apply” and “Save” and your job should be built!
Time to test!
If you want to test your configuration, just click on the link “Build Now” and Jenkins will perform an on-demand build. Ideally, you should be able to click on the build number within the “Build History” section, then click on the “Console Output” and see that your build was successful.
You can follow this process to automate builds and tests against your various projects to ensure that everything is compiling and testing correctly. In our red team training “Intrusion Operations“, we will be providing sample code for our attendees to use throughout the course and on their own assessments. Follow this blog post to add your customizations to the code within a build pipeline and validate that everything works as you expect!
Upcoming dates for Intrusion Operations training by FortyNorth Security, get your tickets here:
If you have any questions at all, feel free to contact us, otherwise we hope to see you in our classes!