Compare commits
14 Commits
ed2e93ed8e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
741ac73e35 | ||
|
|
bb5390928e | ||
|
|
e9be4808ed | ||
|
|
3b6312b596 | ||
|
|
f31221caf0 | ||
|
|
f1b0ace7ae | ||
|
|
c77485c4a1 | ||
|
|
755e3be41e | ||
|
|
d7dd7b5947 | ||
|
|
4b56c4bd55 | ||
|
|
aadfe81674 | ||
|
|
265476d24e | ||
|
|
af6fc43067 | ||
|
|
67ed0e0f34 |
10
eindopdracht/azure/bind/cloudinit.yaml
Normal file
10
eindopdracht/azure/bind/cloudinit.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
#cloud-config
|
||||
bootcmd:
|
||||
- echo "making directories"
|
||||
- mkdir -p /etc/bind
|
||||
- mkdir -p /var/lib/bind
|
||||
- echo "downloading files"
|
||||
- wget https://git.ventilaar.nl/ventilaar/clim/raw/branch/master/eindopdracht/testomgeving/bind/named.conf.local -O /etc/bind/named.conf.local
|
||||
- wget https://git.ventilaar.nl/ventilaar/clim/raw/branch/master/eindopdracht/testomgeving/bind/dns.mashallah.nl.zone -O /var/lib/bind/dns.mashallah.nl.zone
|
||||
packages:
|
||||
- bind9
|
||||
63
eindopdracht/azure/kubernetes/api.yaml
Normal file
63
eindopdracht/azure/kubernetes/api.yaml
Normal file
@@ -0,0 +1,63 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: api-deployment
|
||||
labels:
|
||||
app: api-deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: api-deployment
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: api-deployment
|
||||
spec:
|
||||
containers:
|
||||
- name: api-container
|
||||
image: 4grxfq/api
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 5001
|
||||
name: api-port
|
||||
env:
|
||||
- name: OPENID_SECRET
|
||||
value:
|
||||
- name: DNS_SERVER
|
||||
value: dnsns.mashallah.nl
|
||||
- name: MONGO_CONNECTIONSTRING
|
||||
value:
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: api-service
|
||||
labels:
|
||||
run: api-service
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 5001
|
||||
protocol: TCP
|
||||
selector:
|
||||
app: api-deployment
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: api-ingress
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: addon-http-application-routing
|
||||
spec:
|
||||
rules:
|
||||
- host: dnsapi.mashallah.nl
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: api-service
|
||||
port:
|
||||
number: 80
|
||||
63
eindopdracht/azure/kubernetes/gui.yaml
Normal file
63
eindopdracht/azure/kubernetes/gui.yaml
Normal file
@@ -0,0 +1,63 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: gui-deployment
|
||||
labels:
|
||||
app: gui-deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: gui-deployment
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: gui-deployment
|
||||
spec:
|
||||
containers:
|
||||
- name: gui-container
|
||||
image: 4grxfq/gui
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 5000
|
||||
name: gui-port
|
||||
env:
|
||||
- name: OPENID_SECRET
|
||||
value:
|
||||
- name: DNS_API
|
||||
value: http://api-service:80
|
||||
- name: MONGO_CONNECTIONSTRING
|
||||
value:
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: gui-service
|
||||
labels:
|
||||
run: gui-service
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 5000
|
||||
protocol: TCP
|
||||
selector:
|
||||
app: gui-deployment
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: gui-ingress
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: addon-http-application-routing
|
||||
spec:
|
||||
rules:
|
||||
- host: dnsgui.mashallah.nl
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: gui-service
|
||||
port:
|
||||
number: 80
|
||||
117
eindopdracht/azure/terraform/main.tf
Normal file
117
eindopdracht/azure/terraform/main.tf
Normal file
File diff suppressed because one or more lines are too long
@@ -6,5 +6,6 @@ RUN pip install --no-cache-dir -r /app/requirements.txt
|
||||
COPY openid.py /app/
|
||||
COPY dnszone.py /app/
|
||||
COPY openid.py /app/
|
||||
COPY mango.py /app
|
||||
COPY api.py /app/
|
||||
CMD ["python3", "/app/api.py"]
|
||||
@@ -6,6 +6,7 @@ from ipaddress import ip_address, IPv4Address
|
||||
from openid import *
|
||||
import os
|
||||
from dns import resolver
|
||||
from mango import Mango
|
||||
|
||||
app = Flask(__name__)
|
||||
api = Api(app)
|
||||
@@ -18,13 +19,14 @@ parser.add_argument('type')
|
||||
# verander de variabelen hieronder als je de script handmatig uitvoert
|
||||
|
||||
dnsserver = os.environ.get('DNS_SERVER')
|
||||
mongo_connect = os.environ.get('MONGO_CONNECTIONSTRING')
|
||||
dnsserverport = int(os.environ.get('DNS_PORT', default=53))
|
||||
debug = bool(os.environ.get('API_DEBUG', default=False))
|
||||
|
||||
# end change
|
||||
|
||||
if dnsserver is None:
|
||||
print('You did not set a DNS_SERVER environ')
|
||||
if dnsserver is None or mongo_connect is None:
|
||||
print('You did not set DNS_SERVER or MONGO_CONNECTIONSTRING environ')
|
||||
exit(1)
|
||||
|
||||
|
||||
@@ -47,15 +49,18 @@ def make_fqdn_check(subname, parentdomain): # make fqdn from given subdomain na
|
||||
|
||||
|
||||
def validate_authorization(req): # validate authorization header
|
||||
jwt = req.get('Authorization')
|
||||
if jwt is None:
|
||||
jwt = req.get('Authorization') # get Authorization header
|
||||
if jwt is None: # if not set
|
||||
return False
|
||||
|
||||
jwt = jwt.split(' ')[1] # get jwt from header
|
||||
|
||||
if GoogleOID.check_jwt(jwt)['error'] is False: # check if jwt is valid
|
||||
return True
|
||||
form, value = jwt.split(' ') # get header type and value
|
||||
|
||||
if form == "Bearer": # if bearer(openid)
|
||||
if GoogleOID.check_jwt(value)['error'] is False: # check if jwt is valid
|
||||
return True
|
||||
elif form == "Basic": # basic auth stored in db
|
||||
if mango.check_api_key(value): # check if apikey exists in db
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@@ -190,4 +195,5 @@ if __name__ == '__main__':
|
||||
|
||||
# do not setup dns zone globally because it errors on simultaneous requests
|
||||
GoogleOID = GoogleOID() # setup google oid
|
||||
mango = Mango(mongo_connect)
|
||||
app.run(debug=debug, host='0.0.0.0', port=5001) # run werkzeug
|
||||
|
||||
67
eindopdracht/dns_api/mango.py
Normal file
67
eindopdracht/dns_api/mango.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from pymongo import MongoClient
|
||||
import datetime
|
||||
|
||||
|
||||
class Mango:
|
||||
def __init__(self, connect):
|
||||
try:
|
||||
self.client = MongoClient(connect)
|
||||
self.users = self.client['dns']['users']
|
||||
self.keys = self.client['dns']['keys']
|
||||
except ConnectionError:
|
||||
print('MongoDB connection error')
|
||||
|
||||
def google_check_sso_uuid(self, uuid): # checks if uuid exist in any document
|
||||
found = self.users.find_one({"sso.google.profile.sub": uuid})
|
||||
|
||||
if found is None: # None if nothing found
|
||||
return {"error": True, "reason": "User not found"}
|
||||
else:
|
||||
return {"error": False}
|
||||
|
||||
def google_update_lastlogin(self, uuid): # replaces lastlogin with current time for given user
|
||||
found = self.users.find_one({"sso.google.profile.sub": uuid})
|
||||
|
||||
if found is None:
|
||||
return {"error": True, "reason": "User not found"}
|
||||
|
||||
found['sso']['google']['lastlogin'] = datetime.datetime.utcnow()
|
||||
|
||||
self.users.replace_one({"sso.google.profile.sub": uuid}, found)
|
||||
|
||||
return {"error": False}
|
||||
|
||||
def google_update_profile(self, profile): # overwrites user profile with the one that google has given
|
||||
found = self.users.find_one({"sso.google.profile.sub": profile['sub']})
|
||||
|
||||
found['sso']['google']['profile'] = profile
|
||||
|
||||
self.users.replace_one({"sso.google.profile.sub": profile['sub']}, found)
|
||||
|
||||
return {"error": False}
|
||||
|
||||
def google_get_profile(self, uuid): # returns google profile as stored in db for a given user
|
||||
found = self.users.find_one(({"sso.google.profile.sub": uuid}))
|
||||
return found['sso']['google']['profile']
|
||||
|
||||
def google_get_lastlogin(self, uuid): # returns lastlogin in pretty format for given user
|
||||
found = self.users.find_one(({"sso.google.profile.sub": uuid}))
|
||||
return found['sso']['google']['lastlogin'].strftime('%A %d-%m-%Y, %H:%M:%S')
|
||||
|
||||
def google_add_new_sub(self, uuid): # adds new document in db with only the sub for a given uuid
|
||||
self.users.insert_one({'sso':{'google':{'profile':{"sub": str(uuid)}}}})
|
||||
|
||||
def google_overwrite_jwt(self, uuid, jwt): # overwrite jwt in db for given user
|
||||
found = self.users.find_one({"sso.google.profile.sub": uuid})
|
||||
|
||||
found['sso']['google']['jwt'] = jwt
|
||||
self.users.replace_one({"sso.google.profile.sub": uuid}, found)
|
||||
|
||||
def google_get_jwt(self, uuid): # return jwt as stored in db for given user
|
||||
found = self.users.find_one({"sso.google.profile.sub": uuid})
|
||||
return found['sso']['google']['jwt']
|
||||
|
||||
def check_api_key(self, key): # True or False if api key exists in api keys db
|
||||
if self.keys.find_one({"key": key}):
|
||||
return True
|
||||
return False
|
||||
@@ -14,7 +14,7 @@ class GoogleOID:
|
||||
|
||||
self.settings = {'client_id': '954325872153-1v466clrtgg6h4ptt2ne5pgpb9mhilr5.apps.googleusercontent.com',
|
||||
'client_secret': client_secret,
|
||||
'callback_uri': 'http://dnsdash.mashallah.nl:5000/login/gcp/callback',
|
||||
'callback_uri': 'https://dnsgui.mashallah.nl/login/gcp/callback',
|
||||
'key_server': 'https://www.googleapis.com/oauth2/v3/certs'} # global oid settings
|
||||
|
||||
def settings(self): # make it so that the settings variable is callable
|
||||
|
||||
@@ -3,5 +3,6 @@ flask_restful
|
||||
pyjwt
|
||||
pyjwt[crypto]
|
||||
dnspython
|
||||
pymongo
|
||||
|
||||
werkzeug == 2.0.3 # er zit een fout in de laatste versie die plain http post requests altijd als json interperteerd
|
||||
@@ -7,6 +7,7 @@ class Mango:
|
||||
try:
|
||||
self.client = MongoClient(connect)
|
||||
self.users = self.client['dns']['users']
|
||||
self.keys = self.client['dns']['keys']
|
||||
except ConnectionError:
|
||||
print('MongoDB connection error')
|
||||
|
||||
@@ -59,3 +60,8 @@ class Mango:
|
||||
def google_get_jwt(self, uuid): # return jwt as stored in db for given user
|
||||
found = self.users.find_one({"sso.google.profile.sub": uuid})
|
||||
return found['sso']['google']['jwt']
|
||||
|
||||
def check_api_key(self, key): # True or False if api key exists in api keys db
|
||||
if self.keys.find_one({"key": key}):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -14,7 +14,7 @@ class GoogleOID:
|
||||
|
||||
self.settings = {'client_id': '954325872153-1v466clrtgg6h4ptt2ne5pgpb9mhilr5.apps.googleusercontent.com',
|
||||
'client_secret': client_secret,
|
||||
'callback_uri': 'http://dnsdash.mashallah.nl:5000/login/gcp/callback',
|
||||
'callback_uri': 'https://dnsgui.mashallah.nl/login/gcp/callback',
|
||||
'key_server': 'https://www.googleapis.com/oauth2/v3/certs'} # global oid settings
|
||||
|
||||
def settings(self): # make it so that the settings variable is callable
|
||||
|
||||
40
eindopdracht/lambda/lambda_function.py
Normal file
40
eindopdracht/lambda/lambda_function.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import json
|
||||
import boto3
|
||||
import base64
|
||||
|
||||
|
||||
def lambda_handler(event, context):
|
||||
method = str(event.get('requestContext').get('http').get('method'))
|
||||
|
||||
dynamodb = boto3.resource('dynamodb')
|
||||
table = dynamodb.Table('lambdatable')
|
||||
|
||||
if method == "POST":
|
||||
key = str(event.get('pathParameters').get('id'))
|
||||
url = str(base64.b64decode(event.get('body')), 'utf-8')
|
||||
|
||||
table.put_item(Item={'key': key, 'url': url})
|
||||
|
||||
return {'message': url}
|
||||
elif method == "DELETE":
|
||||
key = str(event.get('pathParameters').get('id'))
|
||||
|
||||
table.delete_item(Key={'key': key})
|
||||
return {'message': key}
|
||||
|
||||
else:
|
||||
key = str(event.get('pathParameters').get('id'))
|
||||
|
||||
data = table.get_item(Key={"key": key})
|
||||
|
||||
if len(data) is 1:
|
||||
return {'message': 'No such key'}
|
||||
|
||||
response = {
|
||||
"statusCode": 302,
|
||||
"headers": {
|
||||
'Location': data['Item']['url']
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
@@ -17,6 +17,7 @@ services:
|
||||
volumes:
|
||||
- ./mongo/import.sh:/docker-entrypoint-initdb.d/import.sh:ro
|
||||
- ./mongo/test-data.json:/docker-entrypoint-initdb.d/test-data.json:ro
|
||||
- ./mongo/test-keys.json:/docker-entrypoint-initdb.d/test-keys.json:ro
|
||||
ports:
|
||||
- "5027:27017"
|
||||
|
||||
@@ -40,6 +41,7 @@ services:
|
||||
DNS_SERVER: bind
|
||||
DNS_PORT: 53
|
||||
OPENID_SECRET: CHANGEME
|
||||
MONGO_CONNECTIONSTRING: "mongodb://root:test@mongo:27017"
|
||||
|
||||
gui:
|
||||
image: 4grxfq/gui
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
#!/bin/sh
|
||||
mongoimport /docker-entrypoint-initdb.d/test-data.json -d dns -c users --drop -u root -p test --authenticationDatabase admin
|
||||
mongoimport /docker-entrypoint-initdb.d/test-data.json -d dns -c users --drop -u root -p test --authenticationDatabase admin
|
||||
mongoimport /docker-entrypoint-initdb.d/test-keys.json -d dns -c keys --drop -u root -p test --authenticationDatabase admin
|
||||
3
eindopdracht/testomgeving/mongo/test-keys.json
Normal file
3
eindopdracht/testomgeving/mongo/test-keys.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"key": "qQT0IuiJwTIz5Jlxw7CwFEeNdcPJUzQqM16PVebJUqaXcLsNFiSVgr8se74itZA="
|
||||
}
|
||||
@@ -1,50 +1,55 @@
|
||||
1. regristreer gcp een applicatie en genereer client keys
|
||||
|
||||
2. zet op login met google button
|
||||
2. zet een webserver functie op die de client redirect naar google met de onderstaande GET parameters
|
||||
|
||||
3. de button opent functie dat de volgende request stuurt naar de url
|
||||
3. plaats een knop of hyperlink op de home pagina die naar de bovenste functie redirect
|
||||
|
||||
4. de flask applicatie redirect de client naar de onderstaande parameter, de onderstaande GET request wordt dus door de
|
||||
client uitgevoerd
|
||||
|
||||
```
|
||||
GET https://accounts.google.com/o/oauth2/v2/auth?
|
||||
client_id=CLIENTID &
|
||||
response_type=code &
|
||||
scope=openid profile email &
|
||||
redirect_uri=CALLBACK &
|
||||
nonce=RANDOM &
|
||||
client_id=CLIENTID & # de client id van je applicatie die je bij stap 1 hebt gegenereerd
|
||||
response_type=code & # je vraagt google om een code(deze kan je met je app secret van stap 1 een authorization token verkrijgen)
|
||||
scope=openid profile email & # de data die je opvraagt(openid=jwt profile=naam, foto enz... email=email)
|
||||
redirect_uri=CALLBACK & # waar google de client naar redirect met parameters
|
||||
nonce=RANDOM # om een replay attack te voorkomen
|
||||
|
||||
RESPONSE
|
||||
GET HTTP REDIRECT CALLBACK # een get request naar de callback met de volgende arguments
|
||||
code=AUTHORIZATIONCODE &
|
||||
scope=email profile
|
||||
authuser=0
|
||||
prompt=none
|
||||
RESPONSE # de onderstaande krijgt je applicatie terug op je callback
|
||||
GET http://localhost:5000/callback # een get request naar de callback met de volgende arguments
|
||||
code=AUTHORIZATIONCODE & # de code die je moet uitwisselen met je secret key
|
||||
scope=email profile & # de scopes die je mag opvragen
|
||||
authuser=0 & #
|
||||
prompt=none #
|
||||
```
|
||||
Hiervan moeten we de code parameter verkrijgen
|
||||
Hiervan moeten we de code parameter gebruiken
|
||||
|
||||
4. Nadat je de authorization code hebt verkregen moet je die omzetten in een (refresh)token, hierbij krijg je ook een
|
||||
5. Nadat je de authorization code hebt verkregen moet je die omzetten in een (refresh)token, hierbij krijg je ook een
|
||||
jwt met alle gebruiker profiel data.
|
||||
|
||||
6. De onderstaande request moet je applicatie in de achtergrond uitvoeren om de code om te wisselen naar bruikbare data
|
||||
|
||||
```
|
||||
POST https://oauth2.googleapis.com/token?
|
||||
code=AUTORIZATIONCODE &
|
||||
client_id=CLIENTID
|
||||
client_secret=CSECRET &
|
||||
redirect_uri=CALLBACK & # wordt niet gebruikt wel verplicht
|
||||
grant_type=authorization_code
|
||||
code=AUTORIZATIONCODE & # de code die je van de client hebt gekregen
|
||||
client_id=CLIENTID & # je applicatie id
|
||||
client_secret=CSECRET & # je applicatie secret
|
||||
redirect_uri=CALLBACK & # wordt niet gebruikt wel verplicht
|
||||
grant_type=authorization_code # de type code die je meegeeft
|
||||
|
||||
RESPONSE
|
||||
200 OK
|
||||
{
|
||||
"access_token": "ACCESS_TOKEN", # hoeft in principe niets mee gedaan te worden
|
||||
"expires_in": 3312,
|
||||
"scope": "https://www.googleapis.com/auth/userinfo.profile openid https://www.googleapis.com/auth/userinfo.email",
|
||||
"token_type": "Bearer",
|
||||
"id_token": "aaaa.bbbbbbbbbbbbbbbb.cccccccccc"
|
||||
# de JWT, als je deze checkt met de keys van google is de authorisatie voldoende, in het midden van de 2 punten is de profiel informatie te vinden encoded in base64
|
||||
"access_token": "ACCESS_TOKEN", # deze kan je gebruiken om extra profiel data op te vragen bij de google profile api
|
||||
"expires_in": 3312, # de tijd voor hoelang de access_token geldig is in seconden
|
||||
"scope": "https://www.googleapis.com/auth/userinfo.profile openid https://www.googleapis.com/auth/userinfo.email", # de scopes die je access_token mag benaderen
|
||||
"token_type": "Bearer", # de onderstaande id_token type
|
||||
"id_token": "aaaa.bbbbbbbbbbbbbbbb.cccccccccc" # de JWT, als je deze checkt met de keys van google is de authorisatie voldoende, in het midden van de 2 punten is de profiel informatie te vinden encoded in base64
|
||||
}
|
||||
```
|
||||
|
||||
5. Nadat je de response hebt gekeken moet je de id_token maniluperen zodat je de base64 encoded object tussen de twee
|
||||
punten verkrijgt.
|
||||
7. in principe voor puur openid authenticatie moet je de client de jwt toesturen, hiermee authentiseerd de client dan
|
||||
met je applicatie zolang de jwt geldig is.
|
||||
|
||||
6. In de base64 encoded JWT staat alle profiel data, bekijk de database of de sub key overeenkomt met wat er is opgeslagen
|
||||
8. aan de flask kant moet je de Authorization header van elke request van de client controleren of de meegegeven JWT
|
||||
Bearer token nog geldig is, door google is uitgegeven, geldend voor jouw applicatie, hier zijn libraries voor te vinden.
|
||||
Reference in New Issue
Block a user