Pi пре 1 година
родитељ
комит
23b1de5e86
4 измењених фајлова са 91 додато и 0 уклоњено
  1. 8 0
      Dockerfile-auth
  2. 14 0
      docker-compose.yml
  3. 66 0
      forward_auth.py
  4. 3 0
      requirements-auth.txt

+ 8 - 0
Dockerfile-auth

@@ -0,0 +1,8 @@
+FROM python:3-slim
+WORKDIR /usr/src/app
+COPY requirements-auth.txt ./
+RUN python3 -m pip install --upgrade pip && \
+    python3 -m pip install --no-cache-dir -r requirements-auth.txt
+COPY forward_auth.py ./
+COPY hashes.py ./
+CMD [ "python", "forward_auth.py" ]

+ 14 - 0
docker-compose.yml

@@ -45,6 +45,20 @@ services:
       - traefik
     restart: always
 
+  auth:
+    image: auth
+    hostname: auth
+    build:
+      context: .
+      dockerfile: Dockerfile-auth
+    volumes:
+      - /etc/private-ca:/etc/private-ca:ro
+    expose:
+      - 1234
+    networks:
+      - traefik
+    restart: always
+
 networks:
   traefik:
     external: true

+ 66 - 0
forward_auth.py

@@ -0,0 +1,66 @@
+import cherrypy
+from bottle import (
+  default_app,
+  auth_basic,
+  HTTPError,
+  HTTPResponse,
+  route,
+  request,
+)
+from passlib.hash import bcrypt
+from hashes import users
+
+def basic_auth(user, password, realm=None):
+  if realm in users and user in users[realm]:
+    return bcrypt.verify(password, users[realm][user])
+  print(f"user not found {realm} {user}")
+  return False
+
+def verify_cert(pem):
+    return True
+
+def auth_client_cert(check):
+    def decorator(func):
+        def wrapper(*a, **ka):
+            if 'X-Forwarded-Tls-Client-Cert' in request.headers:
+                cert = request.headers['X-Forwarded-Tls-Client-Cert']
+            else:
+                cert = None
+            if cert and verify_cert(cert):
+                return HTTPResponse(status=200)
+            return func(*a, **ka)
+        return wrapper
+    return decorator
+
+def auth_basic(check, text="Access denied"):
+    ''' Callback decorator to require HTTP auth (basic).
+        TODO: Add route(check_auth=...) parameter. '''
+    def decorator(func):
+        def wrapper(vhost, *a, **ka):
+            realm = vhost
+            user, password = request.auth or (None, None)
+            if user is None or not check(user, password, realm=realm):
+                err = HTTPError(401, text)
+                err.add_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
+                return err
+            return func(vhost, *a, **ka)
+        return wrapper
+    return decorator
+
+@route('/authenticate/<vhost>')
+@auth_client_cert(verify_cert)
+@auth_basic(basic_auth)
+def auth(vhost):
+    return HTTPResponse(status=200)
+
+cherrypy.server.ssl_certificate = '/etc/private-ca/server-cert.pem'
+cherrypy.server.ssl_private_key = '/etc/private-ca/server-key.pem'
+
+cherrypy.config.update({
+    'server.socket_host': "0.0.0.0",
+    'server.socket_port': 1234,
+})
+
+cherrypy.tree.graft(default_app(), "/")
+cherrypy.engine.start()
+cherrypy.engine.block()

+ 3 - 0
requirements-auth.txt

@@ -0,0 +1,3 @@
+bottle
+cherrypy
+passlib