gitolite hooks for automated Jenkins build start of multibranch pipelines
Tags: jenkins, git, cicd
Published: 2023-06-11 03:34:13

{%- # TOC start (generated with https://github.com/derlin/bitdowntoc) -%}

- [Windows Defender Firewall Network Rule](#windows-defender-firewall-network-rule)
- [Jenkins Hook Authentication](#jenkins-hook-authentication)
- [Hook Setup](#hook-setup)
   * [RC File Adjustment](#rc-file-adjustment)
   * [Hook Addition](#hook-addition)
- [Git Testing](#git-testing)

{%- # TOC end -%}

In the last installment a Jenkins multibranch pipeline was set up on a gitolite repository. The problem is that's not very automated as the multibranch pipeline must be scanned each time. To handle this we'll look at setting up gitolite with hooks which will notify Jenkins that one of the configured repositories it knows about hows changes and to scan the repos when a git push happens.

## Windows Defender Firewall Network Rule

The network adapter used by the WSL2 system has an unfortunate quality that it shows up as a public network to Windows Defender Firewall. Which, by default Windows Defender blocks such networks. Doing an `ipconfig` in powershell gives information on it:

```powershell
Ethernet adapter vEthernet (WSL):

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::3acb:acf5:5ce2:5998%94
   IPv4 Address. . . . . . . . . . . : 172.18.128.1
   Subnet Mask . . . . . . . . . . . : 255.255.240.0
   Default Gateway . . . . . . . . . :
```

Now if you do a lookup in ARIN WHOIS on that particular IP:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ersob85s7pv5e15lzbte.png)

It turns out it's designated as a private IP block (as should be). This is similar to 192.168 style IP addresses. It's also kind enough to provide a CIDR block that we'll be using `172.16.0.0/12`. This is needed as otherwise we'd have to allow traffic for all public networks which is a bad idea. So bringing up Windows Defender Firewall:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2g5d3i0wq997qtsp4mk5.png)

The "Advanced Settings" link will allow for new rules to be added. The steps from there are:

1. Click on "Inbound Rules"
2. Select "New Rule" on the left"
3. For "Type" select "Custom"
4. "All programs" for programs
5. Leaving "All Protocols" on the next screen is fine, though making it TCP restricted wouldn't hurt either
6. For "Which remote IP address does this rule apply to?" select "These IP addresses"
7. Click on "Add"
8. Enter in the "172.16.0.0/12" CIDR and click "OK"
9. "Allow Connection" on the next screen
10. I just left the defaults on the next screen
11. Name and description are up to you, but I named mine "WSL Network Access To Host"

After that click "Finish" to put the rule in place. In the WSL2 instance try doing `curl http://$(hostname).local:8080/` to access the jenkins server and ensuring it's HTML with something asking you to authorize. 

## Jenkins Hook Authentication

To actually use commit hooks with Jenkins you'll need to authenticate via a token. The step for setting one up is:

1. Go to the Jenkins UI
2. Under "Dashboard" select "Manage Jenkins"
3. Select "Configure Global Security" under "Security"
4. Select "Add new access token" under "Git plugin notifyCommit access tokens"
5. Give it a name and then click "Generate"
6. Save the token somewhere safe, as it will only display once (much like an AWS key/secret or GitHub personal token)

For this use case I simply put a `.jenkins_auth` file in the git user home directory and ran `chmod 400 ~/.jenkins_auth`. This is mainly due to the resulting hook being part of source control so it will simply output the contents instead. 

## Hook Setup

There are two ways to work with hooks in gitolite:

1. Manage them via the git user home directory on the server
2. Manage them in the gitolite-admin repository

The first option is more secure but involves having to login to the server to setup. On the other hand the second option is a bit easier to work with being a simple git repository, but it also means that someone with commit access to the gitolite-admin repository can run code at will on the server. Given that this is a simple home setup I'll be going with option 2.

### RC File Adjustment

First the RC file needs to be updated to allow hooks to work. The file is located in the git home directory `/home/git/.gitolite.rc` on the server hosting gitolite. The file needs to be edited and after a lot of comment removal becomes something like this:

```perl
%RC = (
    UMASK                           =>  0077,
    GIT_CONFIG_KEYS                 =>  '',
    LOG_EXTRA                       =>  1,
    ROLES => {
        READERS                     =>  1,
        WRITERS                     =>  1,
    },
    LOCAL_CODE                      =>  "$rc{GL_ADMIN_BASE}/local",

    ENABLE => [
            'help',
            'desc',
            'info',
            'perms',
            'writable',
            'ssh-authkeys',
            'git-config',
            'daemon',
            'gitweb',
            'repo-specific-hooks'
    ],

);

# ------------------------------------------------------------------------------
# per perl rules, this should be the last line in such a file:
1;

# Local variables:
# mode: perl
# End:
# vim: set syn=perl:
```

### Hook Addition

As far as the hooks themselves you can choose between:

1. Common hooks
2. Repo specific hooks

Configuration layout wise I prefer the second option so I'll go with that. First make a folder in the gitolite-admin repository for the hooks to go into:

```
> mkdir -p local/hooks/repo-specific
```

Next I'll put a `jenkins` file in there with the following content:

```bash
#!/bin/bash

PROJECT=$(basename "${PWD}" .git)
GIT_REPO_URL="git@gitserver:${PROJECT}"
JENKINS_TOKEN=$(cat /home/git/.jenkins_auth)
curl "http://$(hostname).local:8080/git/notifyCommit?url=${GIT_REPO_URL}&token=${JENKINS_TOKEN}"
```

So a few things here. The root directory all of this will be run in will end up as `/home/git/repositories/jenkins-test.git` and there will be no way to use something like `git remote get-url origin`. As the server is the same across the repos, that gets hardcoded in and a basename of the repo without `.git` gives the project name to use. The jenkins auth uses an absolute path to the git user directory where our auth file is.

Networking wise in WSL2 (which is what this gitolite server is running under), the name of the host machine (which is where jenkins is) can be found via `$(hostname).local`. Then the path `/git/notifyCommit?url=${GIT_REPO_URL}` lets Jenkins know the repo to compare against in whatever git/multibranch pipelines it has. Token is then added on as the contents of the `.jenkins_auth` file mentioned, giving us the working webhook.

Now I'll add a configuration entry to have this run on the `post-update` phase which will be when all the processing has been done in `gitolite.conf`:

```
repo gitolite-admin
    RW+     =   private_git_server

repo jenkins-test
    RW+     =   private_git_server
    option hook.post-update    =   jenkins
```

## Git Testing

To test this out I'll go ahead and make a change to the `jenkins-test` repository and see what happens:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wqt13joref7jw3we59gg.png)

A new build has been initiating via the multibranch pipeline without imitating a manual scan!