Skip to content

Commit 77d3946

Browse files
committed
Add support for Gitlab tokens
This changesets adds a new configuration option to be consumed by the Gitlab producer that, when set, forces the webservice to verify that the request from Gitlab comes from a trusted source.
1 parent 048c0f4 commit 77d3946

File tree

6 files changed

+88
-0
lines changed

6 files changed

+88
-0
lines changed

INSTALL.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,20 @@ are enqueued:
356356
INFO 2015-12-10T14:43:01.705468 - hostgroups/foo - '0000003c/56698165ac7909' added to the queue
357357
```
358358

359+
Requests can be authenticated using a [secret
360+
token](https://docs.gitlab.com/ee/user/project/integrations/webhooks.html#validate-payloads-by-using-a-secret-token)
361+
which has to be configured both on the Gitlab side (via the group or
362+
repository settings) and on the Jens side via:
363+
364+
```ini
365+
[gitlabproducer]
366+
secret_token = your_token_here
367+
```
368+
369+
If there's no token set (default) then all requests will be accepted
370+
regardless of the presence or the value of the header
371+
`X-Gitlab-Token`.
372+
359373
### Setting a timeout for Git operations via SSH
360374

361375
To protect Jens from hanging indefinitely in case of a lack of response from

conf/main.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@ queuedir = /var/spool/jens-update
2929

3030
[git]
3131
ssh_cmd_path = /etc/jens/myssh.sh
32+
33+
[gitlabproducer]
34+
secret_token = placeholder

jens/configfile.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,6 @@
3131
queuedir = string(default='/var/spool/jens-update')
3232
[git]
3333
ssh_cmd_path = string(default=None)
34+
[gitlabproducer]
35+
secret_token = string(default=None)
3436
"""

jens/settings.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ def parse_config(self, config_file_path):
7676
# [git]
7777
self.SSH_CMD_PATH = config["git"]["ssh_cmd_path"]
7878

79+
# [gitlabproducer]
80+
self.GITLAB_PRODUCER_SECRET_TOKEN = config["gitlabproducer"]["secret_token"]
81+
7982
if self.logfile:
8083
logging.basicConfig(
8184
level=getattr(logging, self.DEBUG_LEVEL),

jens/webapps/gitlabproducer.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ def hello_gitlab():
2727
payload = request.get_json(silent=True) or {}
2828
if payload:
2929
logging.debug('Incoming request with payload: %s' % str(payload))
30+
31+
if settings.GITLAB_PRODUCER_SECRET_TOKEN:
32+
server_token = request.headers.get('X-Gitlab-Token')
33+
if server_token != settings.GITLAB_PRODUCER_SECRET_TOKEN:
34+
logging.error("Bad Gitlab Token (%s)", server_token)
35+
return 'Unauthorised', 401
36+
3037
try:
3138
url = payload['repository']['git_ssh_url']
3239
except (KeyError, TypeError) as error:

jens/webapps/test/test_gitlabproducer.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,62 @@ def test_repository_not_found(self):
8484
}
8585
}))
8686
self.assertEqual(reply.status_code, 404)
87+
88+
@patch('jens.webapps.gitlabproducer.enqueue_hint')
89+
def test_secret_token_ignored_if_not_configured(self, mock_eq):
90+
self.settings.GITLAB_PRODUCER_SECRET_TOKEN = None
91+
_payload = {
92+
'repository': {
93+
'name': 'it-puppet-site',
94+
'git_ssh_url': "file://%s" % self.site_bare
95+
}
96+
}
97+
reply = self.app.post('/gitlab',
98+
content_type='application/json',
99+
headers={'X-Gitlab-Token': 'tokenvalue'},
100+
data=json.dumps(_payload))
101+
mock_eq.assert_called_once_with('common', 'site')
102+
self.assertEqual(reply.status_code, 200)
103+
104+
def test_wrong_secret_token(self):
105+
self.settings.GITLAB_PRODUCER_SECRET_TOKEN = 'expected'
106+
_payload = {
107+
'repository': {
108+
'name': 'it-puppet-site',
109+
'git_ssh_url': "file://%s" % self.site_bare
110+
}
111+
}
112+
reply = self.app.post('/gitlab',
113+
content_type='application/json',
114+
data=json.dumps(_payload))
115+
self.assertEqual(reply.status_code, 401)
116+
117+
def test_secret_token_configured_wrong_token(self):
118+
self.settings.GITLAB_PRODUCER_SECRET_TOKEN = 'expected'
119+
_payload = {
120+
'repository': {
121+
'name': 'it-puppet-site',
122+
'git_ssh_url': "file://%s" % self.site_bare
123+
}
124+
}
125+
reply = self.app.post('/gitlab',
126+
content_type='application/json',
127+
headers={'X-Gitlab-Token': 'tokenvalue'},
128+
data=json.dumps(_payload))
129+
self.assertEqual(reply.status_code, 401)
130+
131+
@patch('jens.webapps.gitlabproducer.enqueue_hint')
132+
def test_secret_token_configured_good_token(self, mock_eq):
133+
self.settings.GITLAB_PRODUCER_SECRET_TOKEN = 'expected'
134+
_payload = {
135+
'repository': {
136+
'name': 'it-puppet-site',
137+
'git_ssh_url': "file://%s" % self.site_bare
138+
}
139+
}
140+
reply = self.app.post('/gitlab',
141+
content_type='application/json',
142+
headers={'X-Gitlab-Token': 'expected'},
143+
data=json.dumps(_payload))
144+
mock_eq.assert_called_once_with('common', 'site')
145+
self.assertEqual(reply.status_code, 200)

0 commit comments

Comments
 (0)