sso
Login server (or identity provider, IDP) using the
ai/sso protocol (version 5) for
single-sign on and auth-server to
authenticate users.
This repository includes a few separate binaries:
- sso-server is the login server / IDP
- saml-server is a SSO-to-SAML bridge (for third-party software)
- sso-proxy is a reverse HTTP proxy that adds single-sign-on access
controls to backends
Configuration
The sso-server daemon requires a YAML configuration file,
/etc/sso/server.yml by default. It understands the following
attributes:
secret_key_file
: path to the Ed25519 secret key (should be exactly
64 bytes)
public_key_file
: path to the Ed25519 public key (should be exactly
32 bytes)
domain
: SSO domain
allowed_services
: a list of regular expressions. A request will be
allowed only if the target SSO services matches one of these
expressions.
allowed_exchanges
: a list of regular expression source /
destination pairs (dictionaries with src_regexp
and dst_regexp
attributes). Exchange requests will only be allowed if source and
destination SSO services both match one of these pairs.
allowed_cors_origins
: a list of "origins" (path-less URLs) for
CORS (Cross-Origin Resource Sharing) used to set
Access-Control-Allow-Origin headers; allows some sites to refresh
their SSO credentials on secondary or asynchronous requests
service_ttls
: a list of dictionaries used to set time-to-live for
SSO tickets for specific services. Each dictionary should have the
following attributes:
regexp
: regular expression that should match the SSO service
ttl
: TTL in seconds
auth_session_lifetime
: time-to-live (in seconds) for the
sso-server user authentication session. When it expires, the user
will have to login again.
session_secrets
: a list of two (or more, as long as the number is
even) secret keys to use for HTTP cookie-based sessions, in
authentication-key, encryption-key pairs. Authentication keys
can be 32 bytes (SHA128) or 64 bytes (SHA512), encryption keys
should be 16 (AES-128), 24 (AES-192) or 32 (AES-256) bytes long. For
key rotation, multiple pairs (old, new) can be specified so that
sessions are not immediately invalidated.
csrf_secret
: a secret key used for CSRF protection
auth_service
: the service name to use for the authentication
request sent to auth-server (generally "sso")
device_manager
: configuration for the device tracking module:
auth_key
: a long-term key to authenticate HTTP-based cookies
zone_maps
: zone mapping definitions, that control IP -> zone
resolution, whether via GeoIP or simple lists of IPs/ranges. The
contents should be a list of mapping definitions, with the
following attributes:
type
: either geoip (for GeoIP files) or ipset (text files
containing lists of IPs or network ranges)
geoip_data_files
: for GeoIP maps, specifies the location of
the MaxMind-format GeoIP data files. This mapping will only look
for country-level data, and if this value is unset it will look
in the default system-wide location
path
: for file-based mappings, path to the file containing IP
or CIDR ranges, one per line
value
: for file-based mappings, the value (zone name) that
should be returned if the IP matches
keystore
: configures the connection to the keystore service
url
: URL for the keystore service
sharded
: if true, requests to the keystore service will be
partitioned according to the user's shard attribute
tls_config
: client TLS configuration
cert
: path to the client certificate
key
: path to the private key
ca
: path to the CA used to validate the server
keystore_enable_groups
: (a list) if set, the keystore will only be
enabled for users that are members of these groups
u2f_app_id
: set the U2F AppID - if unset, it will be autodetected
based on the domain name in the request
url_path_prefix
: URL path prefix of the SSO server application
(default /)
http_server
: specifies standard parameters for the HTTP server
tls
: server-side TLS configuration
cert
: path to the server certificate
key
: path to the server's private key
ca
: path to the CA used to validate clients; if set, clients
will be required to send a certificate (mTLS)
acl
: TLS-based access controls, a list of entries with the
following attributes:
path
is a regular expression to match the request URL path
cn
is a regular expression that must match the CommonName
part of the subject of the client certificate
trusted_forwarders
: list of trusted IP addresses (reverse
proxies). If a request comes from here, we will trust the
X-Forwarded-Proto and X-Real-IP headers when determining the
client IP address
max_inflight_requests
: maximum number of in-flight requests to
allow before server-side throttling kicks in
site_name
: sting to be used as site <title>
.
site_logo
: path to an image to be used as logo, placed above the
modal form.
site_favicon
: path to a favicon.
login_username_label
: label to use for the "username" field in
the password login form (default: Username).
account_recovery_url
: if set, causes a "recover password" link
to appear on the login form with this URL as the target.
Device tracking
The idea is to track a small amount of non-personally-identifying data
for each device, and use it to notify users of unexpected
accesses. This information is tracked by the
user-meta-server.
It is implemented very simply, with a long-term cookie stored in the
browser.
Key store
On login, the login server can unlock the user's key store
(see keystore). The
associated key will be cleared either on logout, or when the login
session expires.
SSO Proxy
The sso-proxy server adds SSO authentication and access controls to
unauthenticated backends (legacy applications, or apps that do not
support authentication altogether).
It is a straightforward reverse proxy that handles the SSO-related
methods directly and forwards everything else unchanged to the
backend. While it is possible to specify multiple backends for each
endpoint, the load balancing algorithm is extremely unsophisticated:
the proxy will simply pick a random backend on every request, without
any tracking of whether backends are up or not (this is obviously
improvable). Also note that the authenticated identity is not
passed along to the backend: since the backends are unauthenticated,
it wouldn't be safe for them to trust this information anyway, unless
they have a way to ensure it comes only from the trusted sso-proxy
(perhaps using TLS or other forms of transport verification). Finally,
sso-proxy only handles incoming requests based on their Host
attribute, not the request path. And the only access control rules
currently supported are group-based.
The proxy server has its own configuration file, /etc/sso/proxy.yml
by default, which has the following attributes:
session_auth_key
and session_enc_key
are secrets to be used for
HTTP-based sessions. For details on their syntax see the description
for session_secrets
above.
sso_server_url
is the URL for the login server
sso_public_key_file
should point at a file containing the SSO
public key
sso_domain
is the SSO domain
backends
is the list of configured endpoints and associated
backends, each entry has the following attributes:
host
the HTTP host to serve
allowed_groups
is a list of the groups whose users will be
allowed access to the service
upstream
is a list of host:port addresses for the upstream
backends
tls_server_name
allows you to explicitly set the value of the
ServerName TLS extension on the outbound request. This is done do
de-couple the transport layer between proxy and backend from the
details of the actual HTTP request.
client_tls
specifies the client TLS configuration. If set, the
upstream request will use HTTPS, otherwise plain HTTP. Known
attributes:
cert
: path to the client certificate
key
: path to the private key
ca
: path to the CA used to validate the server
Given its characteristics, the proxy is currently best suited for
relatively low-volume, administrative applications, rather than for
user-visible services.
API
The sso-server binary serves different types of HTTP traffic:
- the login/logout interface (user-facing)
- the SSO login endpoint (user-facing)
- the SSO ticket exchange endpoint (service-facing)
The ticket exchange API allows a service (the source) to exchange a
valid SSO ticket for itself with a SSO ticket, for the same user,
meant for a third-party service (destination). Its endpoint is
located at the URL /exchange
and it accepts the following query
parameters:
cur_tkt
: valid source SSO ticket
cur_svc
: source SSO service
cur_nonce
: nonce for cur_tkt
new_svc
: destination SSO service
new_nonce
: nonce for the new SSO ticket
Note that annoyingly cur_svc and cur_nonce are redundant, as they
are already contained within cur_tkt, but the SSO ticket API won't
allow us to decode the ticket without verifying it at the same time.
The new ticket will not be valid any longer than the original one, or
the configured TTL for the new service, whichever comes first.
Group membership in the original ticket is passed along unchanged to
the new ticket.
Implementation notes
The single-sign-on functionality works using HTTP cookies and
redirects between the protected service and the SSO server implemented
in this package. This part works without any Javascript, it's just
plain old HTTP (the browser must accept cookies though). SSO cookies
have a builtin (signed) expiration timestamp, and are set to be
automatically deleted on browser exit.
Logout, on the other hand, is more complex: in order to get the
browser to delete the cookies from the signed-in services, we use
XMLHttpRequests from the logout page, and expect the service logout
endpoints to support authenticated CORS. If Javascript is not
available, however, we try to clear the cookies using image requests,
but this may not work depending on the browser (Safari), or the
presence of privacy-protecting extensions meant to block third-party
cookies. In this case a message is displayed asking the user to quit
the browser, but this isn't really a satisfying solution.