summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2017-11-14 13:52:41 +0100
committerLars Wirzenius <liw@liw.fi>2017-11-14 17:38:45 +0100
commitc8bbfc9672b8c45800c47a9432904426809bee9b (patch)
treebb20c02c9d3896e1bb76839306c735ee85e46b98 /doc
parent825bfda4c13d74938feff48a5fb8663dc1ab4f67 (diff)
downloadqvisqve-c8bbfc9672b8c45800c47a9432904426809bee9b.tar.gz
Add: start of an arch doc
Diffstat (limited to 'doc')
-rw-r--r--doc/arch.mdwn183
-rwxr-xr-xdoc/build.py95
-rwxr-xr-xdoc/build.sh43
-rw-r--r--doc/salami.css96
4 files changed, 417 insertions, 0 deletions
diff --git a/doc/arch.mdwn b/doc/arch.mdwn
new file mode 100644
index 0000000..6b9eaea
--- /dev/null
+++ b/doc/arch.mdwn
@@ -0,0 +1,183 @@
+---
+title: "Architecture of Salami, an authorization server"
+author: QvarnLabs Ab
+date: work-in-progress
+...
+
+# Introduction
+
+**What.** Salami is an authorization server. At this stage, it is an
+[OAuth2][] authorization server, but later on it will grow into a
+provider for [OpenID Connect][]. Salami controls access to a web API:
+the API client authenticates itself to Salami, gets an access token
+from Salami, and gives the access token to the API server. This
+de-couples the acts of authentication and authorization and resource
+access, which simplifies the individual software components.
+
+[OAuth2]: https://en.wikipedia.org/wiki/OAuth
+[OpenID Connect]: https://en.wikipedia.org/wiki/OpenID_Connect
+
+**Why.** Salami is not the first server of its kind. We wrote Salami
+because we wanted something we liked:
+
+* is fully free, open source software
+* has a simple, clear architecture and implementation
+* has a chance of being accepted into the Debian distribution
+* is simple and fast to install, and configure
+* is simple, transparent to operate
+* scles to many requests per second, many identities
+* is flexible and easily extensible, as far as authentication methods
+ go
+* is robust and reliable
+* is stable
+
+**Web, not enterprise.** We operate in a "web environment, which is
+different from a typical enterprise or large organisation environment.
+We do not care about "single sign-on" across unrelated services, for
+example.
+
+**Why not an alternative.** We've used [Gluu][], but for our uses it
+is cumbersome, and not simple.
+
+[Gluu]: https://www.gluu.org/
+
+**Usage driven development.** We develop Salami mainly based on the
+needs of actual users, not to complete feature comparsion matrices.
+
+## Glossary
+
+* access token
+
+# Current state
+
+**Currently, Salami does not exist.** We have just started developing
+it. The first development phase of Salami aims to produce an OAuth2
+authorization server that supports the client credential grant only.
+This means only the API client authenticates itself to Salami, but not
+the actual end-user. This is so that we can use Salami with the
+[Qvarn][] server and have the Qvarn API tests pass.
+
+[Qvarn]: http://qvarn.org/
+
+Further, the first development phase aims for a simplistic OAuth2
+server. For example, the list of API clients will be hardcoded in the
+configuration file. This is acceptable to get development started, but
+soon after that we will add features such as dynamically registering
+clients.
+
+## Vision for the future
+
+In the longer term, we aim for Salami to be an OpenID provider using
+the OpenID Connect protocol. This includes being able to have the
+end-user authenticate and authorize use of resources. We will make it
+simple and flexible to provide authentication methods (such as
+username/password, client-side certificates, and U2F tokens).
+
+Eventually, we aim to have Salami certified as a compliant OpenID
+Connect implementation.
+
+We also intend to make Salami be flexible, easy, and secure.
+
+# Known problems and things to solve later
+
+The main problem of Salami at this stage is that it doesn't exist. All
+other problems can be derived from that.
+
+We don't have a good way of rotating the token signing keys.
+
+# Requirements
+
+For the first development phase of Salami, our acceptance criteria
+are:
+
+* setting up a Salami instance is simple: the software should be
+ provided as a Debian package installable on Debian 9 (stretch), and
+ a corresponding Ansible playbook that configures the instance
+
+* configuring a Qvarn instance to use the Salami instance is simple:
+ the Ansible playbook for Qvarn should be updated to work with Salami
+ instead of Gluu
+
+* the Qvarn API tests pass
+
+Once we have that working, we will add more requirements.
+
+## Non-requirements
+
+At this stage, we do not care about speed, portability, or other such
+qualities. We care about security enough that we want to avoid any
+glaringly obvious security issues, but, for example, we don't care
+about storing API client secrets in an encrypted fashion.
+
+# Architecture overview
+
+Salami provides an HTTP API interface for authentication. The only
+relevant endpoint is `/token` and to use it, the client must
+authenticate itself using its "client id" and "client secret" using
+HTTP Basic Authentication. A successful response will have the access
+token in its JSON body.
+
+A request:
+
+ POST /token HTTP/1.1
+ Host: salami.example.com
+ Authorization: Basic c2FsYW1pOnBhc3N3b3Jk
+ Content-Type: application/x-www-form-urlencoded
+
+ grant_type=client_credentials?scope=version
+
+A successful response:
+
+ HTTP/1.1 200 OK
+ Content-Type: application/json
+
+ {
+ "access_token": "CAFEF00D",
+ "token_type": "bearer",
+ "expires_in": 3600,
+ }
+
+The client should extract the access token (`CAFEF00D`) and add it to
+any requests it makes to the resource server:
+
+ Authorization: Bearer CAFEF00D
+
+The client does not need to understand the token, it merely copies it
+into requests it makes.
+
+## Components
+
+Salami consists of several components
+
+@startuml
+title Salami components
+component [API client] as client
+node "Salami" {
+ component [Haproxy] as haproxy
+ component [Salami\nbackend] as backend
+ component [Salami\nconfiguration] as config
+}
+
+client -> haproxy : 2. https
+haproxy -> backend : 3. http
+backend <- config : 1. read at startup
+haproxy <- backend : 4. access token
+client <- haproxy : 5. access token
+@enduml
+
+**haproxy** is a load balancer. For Salami we use it to provide TLS
+for communication with the client.
+
+The **backend** implements the actual Salami HTTP endpoints and
+creates and returns access tokens to the client.
+
+The **configuration** lists the API clients (the id, the secret, and
+any scopes the client is allowed to have), as well as the RSA keys
+used to sign the access token. The API provider (Qvarn) will be
+configured to know the public RSA key so that it can verify that an
+access token has been created by Salami.
+
+The client and haproxy use TLS. haproxy and the backend use plain
+HTTP, but they will be deployed in an environment where the plain text
+communication cannot be overheard, such as via the `localhost`
+interface (haproxy and backend would run on the same host).
diff --git a/doc/build.py b/doc/build.py
new file mode 100755
index 0000000..5818cb7
--- /dev/null
+++ b/doc/build.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+#
+# Copyright 2017 Lars Wirzenius
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+
+import os
+import sys
+import tempfile
+
+import cliapp
+
+
+def parse_markdown(f, tmpdir):
+ lines = f.readlines()
+ result = []
+ uml_filenames = []
+ while lines:
+ prefix, uml, lines = extract_uml(lines)
+ result.extend(prefix)
+ if uml:
+ filename = create_uml_file(tmpdir, uml)
+ uml_filenames.append(filename)
+ result.extend(include(uml, filename))
+ return result, uml_filenames
+
+
+def extract_uml(lines):
+ start = find_line(lines, '@startuml\n')
+ if start is None:
+ return lines, None, []
+
+ end = find_line(lines, '@enduml\n')
+ if end is None:
+ return lines, None, []
+
+ return lines[:start], lines[start:end+1], lines[end+1:]
+
+
+def find_line(lines, pattern):
+ for i, line in enumerate(lines):
+ if line.startswith(pattern):
+ return i
+ return None
+
+
+def create_uml_file(tmpdir, lines):
+ text = ''.join(lines)
+ fd, filename = tempfile.mkstemp(dir=tmpdir)
+ os.write(fd, text.encode())
+ os.close(fd)
+ return filename
+
+
+def include(uml, filename):
+ title = get_title(uml)
+ return [
+ '![{}]({}.png)\\ \n'.format(title, filename),
+ '\n',
+ ]
+
+
+def get_title(uml):
+ i = find_line(uml, 'title ')
+ if i is not None:
+ return uml[i].split(' ', 1)[1]
+
+
+def plantuml(filename):
+ cliapp.runcmd(['plantuml', '-tpng', '-output', '.', filename])
+
+
+def main():
+ tmpdir = sys.argv[1]
+ lines, filenames = parse_markdown(sys.stdin, tmpdir)
+ sys.stdout.write(''.join(lines))
+ for filename in filenames:
+ plantuml(filename)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/build.sh b/doc/build.sh
new file mode 100755
index 0000000..418fa26
--- /dev/null
+++ b/doc/build.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+# Copyright 2017 Lars Wirzenius
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -eu
+
+export DISPLAY=
+
+output="$(dirname "$1")/$(basename "$1" .mdwn)"
+
+tmp="$(mktemp -d)"
+
+clean()
+{
+ rm -rf "$tmp"
+}
+
+trap clean EXIT
+
+cat -- "$@" | ./build.py "$tmp" > "$tmp/foo.mdwn"
+pandoc --toc \
+ -V documentclass:report \
+ -Vgeometry:a4paper \
+ -Vfontsize:12pt \
+ -Vmainfont:FreeSerif \
+ -Vsansfont:FreeSans \
+ -Vmonofont:FreeMonoBold \
+ -Vgeometry:"top=2cm, bottom=2.5cm, left=2cm, right=1cm" \
+ --chapters \
+ -o "$output.pdf" "$tmp/foo.mdwn"
+pandoc --toc -o "$output.html" --number-sections --self-contained -H salami.css "$tmp/foo.mdwn"
diff --git a/doc/salami.css b/doc/salami.css
new file mode 100644
index 0000000..86f806e
--- /dev/null
+++ b/doc/salami.css
@@ -0,0 +1,96 @@
+<!--
+Copyright 2017 Lars Wirzenius
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<style>
+html {
+ background: white;
+ font-family: serif;
+ margin-left: 3em;
+ margin-right: 2em;
+ margin-top: 2em;
+}
+
+form#searchform {
+ font-family: monospace;
+ text-align: right;
+}
+
+div.actions {
+ font-family: monospace;
+ text-align: right;
+}
+
+div.actions ul, div.actions li {
+ display: inline;
+}
+
+div.pageheader {
+ font-family: monospace;
+ margin-bottom: 2em;
+}
+
+div.pageheader span.title {
+ display: block;
+ font-size: 200%;
+ font-weight: bold;
+ font-family: sans-serif;
+ margin-top: 0.5em;
+}
+
+div#pagebody {
+}
+
+div.pagefooter {
+ font-family: monospace;
+ margin-top: 3em;
+}
+
+div#pagebody {
+}
+
+div#TOC ul {
+ list-style: none;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ font-family: sans-serif;
+ font-weight: bold;
+ margin-top: 2em;
+}
+
+h1 {
+ font-size: 150%;
+}
+
+h2 {
+ font-size: 120%;
+}
+
+h3 {
+ font-size: 100%;
+}
+
+ul li, ol li {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+pre {
+ margin-left: 4em;
+}
+
+</style>