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 withsrc_regexp
anddst_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 -
geo_ip_data_files
: GeoIP databases to use (in mmdb format), if unset the module will use the default GeoLite2-Country db
-
-
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.
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
andsession_enc_key
are secrets to be used for HTTP-based sessions. For details on their syntax see the description forsession_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.