Commit 4feb3be4 authored by ale's avatar ale

Update id/auth

parent 564412b9
Pipeline #716 passed with stages
in 1 minute and 7 seconds
......@@ -60,6 +60,11 @@ func (c *TLSAuthConfig) match(req *http.Request) bool {
return false
var serverCiphers = []uint16{
// TLSServerConfig configures a TLS server with client authentication
// and authorization based on the client X509 certificate.
type TLSServerConfig struct {
......@@ -87,7 +92,7 @@ func (c *TLSServerConfig) TLSConfig() (*tls.Config, error) {
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: cas,
CipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
CipherSuites: serverCiphers,
MinVersion: tls.VersionTLS12,
PreferServerCipherSuites: true,
......@@ -40,10 +40,8 @@ The authentication server data model is based on the concept of a
in LDAP, but it has to be told the specific details of how to find
them and how to map the information there to what it needs.
# Configuration
The behavior of auth-server can be configured with a YAML file.
The YAML file should contain a dictionary with the following attributes:
......@@ -121,6 +119,15 @@ method, U2F support is currently missing.
## LDAP Backend
The *ldap* backend will look up user information in a LDAP database.
It needs some parameters to be passed in the top-level *ldap_config*
* `uri` of the LDAP server (like *ldapi:///var/run/ldap/ldapi*)
* `bind_dn` is the DN to bind with
* `bind_pw_file` points at a file containing the bind password
The *ldap* backend will currently always attempt to bind on every
### Query definition
......@@ -173,14 +180,85 @@ App-specific passwords should be encoded as colon-separated strings:
The password should be encrypted. The comment is a free-form string
set by the user to tell the various credentials apart.
## OTP implementation
The authentication server uses a very simple implementation of
time-based OTP (TOTP), supporting a single secret per user and without
any fancy features such as emergency tokens etc. The reason for this
is that TOTP authentication requires just plain read-only access to
the user database, while counter-based authentication with proper
token revocation is a read-write, locked operation which is more
difficult to perform on a LDAP backend.
# Usage
The *auth-server* runs on a local UNIX socket. You can use UNIX
permissions to control who has access to this socket. The Debian
package makes it group-readable to the *auth-server* group, so you can
add specific users to it easily.
The daemon can run either standalone or be socket-activated by
systemd, which is what the Debian package does.
## Wire protocol
The rationale behind the wire protocol ("why not http?") is twofold:
first, we wanted strict access control, and that's more easily done
with UNIX permissions, so UNIX sockets were chosen. Then, the protocol
should be able to transfer data maps, and it must be trivial to
implement (and verify) in C, Go and Python. Furthermore, it should
minimize external dependencies.
The protocol is line-based: multiple authentication requests can be
sent over the same connection, but every request must wait for a
response (i.e. no pipelining). Commands are single words, and can be
followed by a space and an attribute/value map. The responses are
simply attribute/value maps.
Attribute maps should have the following characteristics:
* maps can't be nested, they are simple key/value sets where both keys
and values are strings
* keys can't contain '=' characters
They are encoded using the following algorithm:
* if this is not the first attribute/value pair, add a space character
* add the key string
* add the '=' character
* if the value contains a non-printable character or a double quote:
* add the base64-encoded value
* if it does not:
* add a '"' character, then the value, then another '"' character.
## API
There is only one command: `auth`, which must be followed by the
authentication request. Parameters for an authentication request are:
* `service`: the service requesting the authentication
* `username`: name of the user to authenticate
* `password`: password (cleartext) provided by the user
* `otp` (optional): TOTP-based 2FA token
* `u2f_app_id` (optional): U2F AppID
* `u2f_response` (optional): U2F response object
* `key_handle`
* `signature_data`
* `client_data`
* `device` (optional): information about the client device
* `id`: a unique ID, specific to this device
* `remote_addr`: remote IP address (will be minimized)
* `remote_zone`: remote zone (country-level IP aggregation)
* `browser`: browser name
* `os`: client OS
* `user_agent`: client User-Agent string
* `mobile`: boolean variable indicating a mobile device
Responses will contain the following attributes:
* `status`: status of the request, one of *ok*,
*insufficient_credentials* or *error*
* `2fa_method`: if *status* is *insufficient_credentials*, one of
*otp* or *u2f* indicating which 2FA method should be used for the
next request
* `u2f_req`: when *2fa_method* is *u2f*, this field will contain a U2F
SignResponse object:
* `version`
* `challenge`
* `registered_keys`: a list of registered keys
* `user`: when *status* is *ok* (the authentication has been
successful), this dictionary will contain user information:
* `email`: email of this user
* `groups`: groups the user is a member of.
......@@ -90,9 +90,11 @@ func (r *Request) DecodeFromMap(m map[string]string, prefix string) {
r.DeviceInfo = decodeDeviceInfoFromMap(m, prefix+"device.")
// UserInfo contains optional user information.
// UserInfo contains optional user information that may be useful to
// authentication endpoints.
type UserInfo struct {
Email string
Shard string
Groups []string
......@@ -100,13 +102,19 @@ func (u *UserInfo) EncodeToMap(m map[string]string, prefix string) {
if u.Email != "" {
m[prefix+"email"] = u.Email
if u.Shard != "" {
m[prefix+"shard"] = u.Shard
for i, g := range u.Groups {
m[fmt.Sprintf("%sgroup.%d.", prefix, i)] = g
func decodeUserInfoFromMap(m map[string]string, prefix string) *UserInfo {
u := UserInfo{Email: m[prefix+"email"]}
u := UserInfo{
Email: m[prefix+"email"],
Shard: m[prefix+"shard"],
i := 0
for {
s, ok := m[fmt.Sprintf("%sgroup.%d.", prefix, i)]
......@@ -5,44 +5,44 @@
"checksumSHA1": "raJx5BjBbVQG0ylGSjPpi+JvqjU=",
"path": "",
"revision": "cd9ee1e6cc35b08a350d3dcb1b331750b363a82d",
"revisionTime": "2017-12-14T07:42:45Z"
"revision": "8cedcb1d73128f5566216cb3e39ad1ccea318213",
"revisionTime": "2017-12-16T15:39:23Z"
"checksumSHA1": "2X2UMundICtpGTb8pTdBk7PCKss=",
"path": "",
"revision": "cd9ee1e6cc35b08a350d3dcb1b331750b363a82d",
"revisionTime": "2017-12-14T07:42:45Z"
"revision": "8cedcb1d73128f5566216cb3e39ad1ccea318213",
"revisionTime": "2017-12-16T15:39:23Z"
"checksumSHA1": "g6I5506EXkjQetiXSYbVTypDnDM=",
"checksumSHA1": "wY0SM35qAhX3P2IZzDnYa068cPw=",
"path": "",
"revision": "cd9ee1e6cc35b08a350d3dcb1b331750b363a82d",
"revisionTime": "2017-12-14T07:42:45Z"
"revision": "8cedcb1d73128f5566216cb3e39ad1ccea318213",
"revisionTime": "2017-12-16T15:39:23Z"
"checksumSHA1": "Br4iXxLFs+Yp7doAZQUccQA/su4=",
"checksumSHA1": "C1BVHHj8iBgBN5EWr3ucsGoEzew=",
"path": "",
"revision": "ddba7d73598682b17e4683f5a3873f12e158c679",
"revisionTime": "2017-12-13T22:22:39Z"
"revision": "1ac213f96bd3ab25b3ad98729831ca28eb8e1784",
"revisionTime": "2017-12-17T09:15:51Z"
"checksumSHA1": "48njEQBB73cV6tTPBEOnzSVRjeA=",
"path": "",
"revision": "ddba7d73598682b17e4683f5a3873f12e158c679",
"revisionTime": "2017-12-13T22:22:39Z"
"revision": "1ac213f96bd3ab25b3ad98729831ca28eb8e1784",
"revisionTime": "2017-12-17T09:15:51Z"
"checksumSHA1": "3alRLG3a43ORlVZyfQc/JsT0KtI=",
"path": "",
"revision": "e7fb4821845b47ce2e372e384b49f4b5216bba93",
"revisionTime": "2017-12-09T17:36:20Z"
"revision": "b09f1210471f6a60402e8ced4783be3889a4074f",
"revisionTime": "2017-12-15T13:50:57Z"
"checksumSHA1": "HGK52MX+2CEKVzb9I5y1BfgDkWQ=",
"path": "",
"revision": "e7fb4821845b47ce2e372e384b49f4b5216bba93",
"revisionTime": "2017-12-09T17:36:20Z"
"revision": "b09f1210471f6a60402e8ced4783be3889a4074f",
"revisionTime": "2017-12-15T13:50:57Z"
"checksumSHA1": "usT4LCSQItkFvFOQT7cBlkCuGaE=",
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment