ungleich-otp/README.md
2018-11-18 15:41:47 +01:00

7.1 KiB
Raw Blame History

ungleichotp

ungleich-otp is a full blown authentication and authorisation service made for micro services.

The basic idea is that every micro service has a (long term) triple constisting of (name, realm, seed) and creates time based tokens.

It basically revamps Kerberos in a simple way into the web area.

ungleichotp has been created and is maintained by ungleich.

Related documentation:

Overview

This repository contains three components:

  • ungleichotp-server: the reference implementation of the ungleichotp server
  • ungleichotp-client: a sample implementation of an ungleichotp client

Setup instructions

This is a standard django project and thus can be easily setup using

pip install -r requirements.txt

To bootstrap the application, you need your very first trusted seed to access the application. You can generate it using

to be filled in

After that, you can run the application using

python manage.py runserver

The usual instructions on how to setup an https proxy should be followed.

Realms

Access is granting/denied based on realms. There are two reserved realms, all other realms can be used by the users:

Reserved realms

Conceptually the realms "ungleich-admin" and "ungleich-auth" are reserved for higher priviliged applications.

Usually there is only 1 entry in ungleich-admin that is used to bootstrap and manage ungleich-otp.

All micro services that are trusted to authenticate another micro service should have an entry in the ungleich-auth realm, which allows them to verify a token of somebody else.

| Name | Capabilities | |------------------+--------------------------------------------| | ungleich-admin | authenticate, create, delete, list, update | | ungleich-auth | authenticate | | all other realms | NO ACCESS |

Environment / Configuration (unfinished)

  • POSTGRES_USERNAME
  • SECRET_KEY -- random (?)

Random notes / stuff (unfinished)

django.db.backends.postgresql django.contrib.admin

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydatabase',
        'USER': 'mydatabaseuser',
        'PASSWORD': 'mypassword',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

Custom auth

from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions

class ExampleAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        username = request.META.get('X_USERNAME')
        if not username:
            return None

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            raise exceptions.AuthenticationFailed('No such user')

        return (user, None)

Custom user

Dont forget to point AUTH_USER_MODEL to it. Do this before creating any migrations or running manage.py migrate for the first time.

To document

  • Login via username password interactively
  • Login via name/realm/token rest

The library (ungleichotp)

The server (ungleichotp-server)

The sample client (ungleichotp-client)

The included client application is a Django application that makes use of an ungleichotp-server to authenticate requests.

Usage

  • Ensure that the ungleichotp-server is running and reachable

Integrating ungleichotp

In Django

In other frameworks

In general, you will need to implement the following into your app for resources that need to have an authenticated user:

Retrieve name, realm and token from the request

This is application specific. In the sample Django rest framework, we use JSON to retrieve this values:

{
    "name": "info@ungleich.ch",
    "token": "947732",
    "realm": "ungleich-admin",
    "otherdata": "..."
}

Send name, realm and token from the request to the ungleichotp-server

Post a JSON object to /ungleichotp/verify that contains the following elements:

Request JSON object:

{
    version: "1",
    name: "your-name",
    realm: "your-realm",
    token: "current time based token",
    verifyname: "name that wants to be authenticated",
    verifyrealm: "realm that wants to be authenticated",
    verifytoken: "token that wants to be authenticated",
}

Response JSON object:

Either HTTP 200 with

{
    status: "OK",
}

OR return code 403:

  • If token for authenticating is wrong, you get
{"detail":"Incorrect authentication credentials."}
  • If token that is being verified is wrong, you get
{"detail":"You do not have permission to perform this action."}

Authorize the request

From the ungleichotp-server, you get a validated information that a name on a realm authenticated successfully. The associated permissions ("authorization") is application specific and needs to be decided by your application.

Limitations

  • Name, Realm and seed are hard coded to 128 bytes length. This can be changed, if necessary.
  • Only python3 support for ungleichotp

TODOs

  • (server) Serialize / input request
  • (server) Make seed read only
  • (server) Implement registering of new entries
  • (server) OTPSerializer: allow to read seed for admin
  • (server) Implement deleting entry
  • (server) Include verify in ModelSerializer
  • (server) Map name+realm == User (?)
    • name == name@realm
    • password is used for admin login (?)
    • seed
    • custom auth method
  • [n] (server) Try to fake username for django based on name+realm (?)
    • No need
  • [n] (server) maybe overwrite get_username()
    • No need
  • (server) Use Custom authentication - needs to have a user!
  • (server) Implement creating new "User" by POST / Model based
  • [n] (server) Remove hard coded JSON in /verify (no - good enough for the moment)
  • (server) Fully rename server from ungleichotp to ungleichotpserver
  • (security) Ensure that only the right realms can verify
  • (security) Ensure that only the right realms can manage
  • (doc) Add proper documentation
  • (server) Add tests for verify
  • (server) Add tests for authentication
  • (server) move totp constants into settings
  • (server) move field lengths into settings
  • (server) Document how admin vs. rest works
  • (server, client) Make settings adjustable by environment - k8s/docker compatible
  • (server, client) Read DB from outside (?) (fallback to sqlite)
  • (client) Establish auth using urllib
  • (client) Bootstrap Django + DRF (including an object for CRUD)
  • (client) Add custom authentication / remote auth
  • (client) Show case: any realm vs. specific realm
  • (library) Write a "client library" that can use ungleichotp
  • (library) extract generic parts from server
  • (library) upload to pypi

Changelog

0.6, 2018-11-18

  • Reuse TokenSerializer for VerifySerializer logic

0.5, 2018-11-18

  • Require authentication on all rest endpoints by token