Skip to content

Commit 6f77f62

Browse files
gisjediosallou
authored andcommitted
DCOS Strict security support (#6)
* Added DCOS login support for EE Strict mode
1 parent 8bd234b commit 6f77f62

14 files changed

+315
-46
lines changed

.dockerignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*.pyc
2+
.env
3+
build
4+
5+
.idea
6+
Dockerfile

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,6 @@ ENV/
9090

9191
# Rope project settings
9292
.ropeproject
93+
94+
# Pycharm project settings
95+
.idea

Dockerfile-test-framework

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM python:2
2+
3+
COPY requirements.txt /tmp
4+
RUN pip install -r /tmp/requirements.txt --no-cache
5+
6+
COPY . /tmp
7+
8+
RUN cp /tmp/sample/test.py /test.py \
9+
&& cd /tmp \
10+
&& python setup.py install
11+
12+
ENV MESOS_URLS zk://leader.mesos:2181/mesos
13+
14+
CMD ["python", "/test.py"]

README.md

+38
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,44 @@ See sample/test.py for example.
1515

1616
Callbacks will "block" the mesos message treatment, so they should be short, or messages should be forwarded to a queue in an other thread/process where longer tasks will handle messages.
1717

18+
# DCOS EE Strict
19+
20+
Additions have been made to support login with ACS to allow access through to the master
21+
in a Strict security posture. The `sample/test.py` script is configured to accept the `SERVICE_SECRET` environment variable, which can be created and passed to the
22+
service using the script given below:
23+
24+
```
25+
dcos security org service-accounts keypair service-account-private.pem service-account-public.pem
26+
dcos security org service-accounts create -p service-account-public.pem -d "Service account for Scale data processing framework" service-account
27+
dcos security secrets create-sa-secret --strict service-account-private.pem service-account service-account-secret
28+
29+
# Test with SUPERUSER perms on user
30+
dcos security org users grant service-account dcos:superuser full
31+
32+
# Build image to test - update name in 2 commands below and marathon.json to your personal Docker Hub account name
33+
docker build -t gisjedi/python-mesos-http -f Dockerfile-test-framework .
34+
docker push gisjedi/python-mesos-http
35+
36+
# Deploy app consuming image to marathon
37+
dcos marathon app add sample/marathon.json
38+
39+
# Once success has been confirmed, more granular permissions should be applied
40+
dcos security org users revoke service-account dcos:superuser full
41+
dcos security org users grant service-account dcos:mesos:agent:task create
42+
dcos security org users grant service-account dcos:mesos:agent:task:app_id create
43+
dcos security org users grant service-account dcos:mesos:agent:task:user:nobody create
44+
dcos security org users grant service-account dcos:mesos:master:framework create
45+
dcos security org users grant service-account dcos:mesos:master:reservation create
46+
dcos security org users grant service-account dcos:mesos:master:reservation delete
47+
dcos security org users grant service-account dcos:mesos:master:reservation:principal:service_account delete
48+
dcos security org users grant service-account dcos:mesos:master:task create
49+
dcos security org users grant service-account dcos:mesos:master:task:app_id create
50+
dcos security org users grant service-account dcos:mesos:master:task:user:nobody create
51+
dcos security org users grant service-account dcos:mesos:master:volume create
52+
dcos security org users grant service-account dcos:mesos:master:volume delete
53+
dcos security org users grant service-account dcos:mesos:master:volume:principal:service_account delete
54+
```
55+
1856
# About
1957

2058
This library does not implement all options of mesos.proto and manages schedulers only (not executors). Implemented features should be enough to implement a scheduler, if something is missing, please ask, or contribute ;-)

mesoshttp/acs.py

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from time import time
2+
from datetime import timedelta
3+
4+
import jwt
5+
import requests
6+
from requests.auth import AuthBase
7+
8+
from mesoshttp.exception import ACSException
9+
10+
11+
class DCOSServiceAuth(AuthBase):
12+
"""Attaches a token to Request object that will allow requests to be made through DCOS Admin Router."""
13+
14+
def __init__(self, secret, verify=False):
15+
"""Take a DCOS service account secret and breaks out components needed for login flow
16+
17+
:param secret: dict that must include uid, private_key, scheme and login_endpoint keys
18+
:param verify: `False` or path to a PEM encoded trust bundle
19+
:return: :class:`DCOSServiceAuth`
20+
"""
21+
22+
# Service account
23+
self._user = secret['uid']
24+
self._key = secret['private_key']
25+
self._scheme = secret['scheme']
26+
self._acs_endpoint = secret['login_endpoint']
27+
self._verify = verify
28+
29+
self._expiration = time()
30+
self._token = None
31+
32+
@property
33+
def principal(self):
34+
"""Get the service account user value which will match the principal value that must be set in Mesos
35+
36+
:return: service account user name
37+
"""
38+
return self._user
39+
40+
@property
41+
def token(self):
42+
"""Get the authentication token for making API calls through DCOS AdminRouter
43+
44+
:return: token used for authentication
45+
"""
46+
47+
if time() > self._expiration or self._token is None:
48+
self._acs_login()
49+
50+
return self._token
51+
52+
def _generate_token(self, uid, private_key, scheme='RS256', expiry_seconds=180):
53+
"""Generate a JWT for ACS login
54+
55+
Updates the instance variable to track expiration
56+
57+
:param private_key: Service accounts PEM encoded private key
58+
:param scheme: algorithm used to encode JWT
59+
:param expiry_seconds: how many seconds authentication token will be good for
60+
:return: login token
61+
"""
62+
expire_time = time() + float(timedelta(seconds=expiry_seconds).seconds)
63+
token = jwt.encode({'exp': expire_time, 'uid': uid}, private_key, algorithm=scheme)
64+
self._expiration = expire_time
65+
return token
66+
67+
def _acs_login(self):
68+
"""Login to ACS and set an authentication token on the instance
69+
"""
70+
71+
payload = {'uid': self._user, 'token': self._generate_token(self._user, self._key, self._scheme)}
72+
response = requests.post(self._acs_endpoint, json=payload, verify=self._verify)
73+
74+
if response.status_code != 200:
75+
raise ACSException('Unable to authenticate against DCOS ACS: {} {}'.format(response.status_code,
76+
response.text))
77+
self._token = response.json()['token']
78+
79+
def __call__(self, r):
80+
r.headers['Authorization'] = 'token={}'.format(self.token)
81+
return r
82+

0 commit comments

Comments
 (0)