arpa2 / tlspool

TLS daemon with PKCS #11 backend
Other
6 stars 7 forks source link

Multi-user and Multi-tenant support #36

Closed vanrein closed 5 years ago

vanrein commented 8 years ago

The current TLS Pool is designed for the following two uses:

In addition, it should be possible to run the TLS Pool under a personal account with a personalised configuration. Although possible, it's not pretty and ill-advised to do so. For this to work properly, one TLS Pool should prepare to serve multiple users in such a way that they are logically and securely separated, yet managed together.

Management for multiple users in one stroke involves the configuration over SteamWorks. It involves one or more databases, though it may differ between sites which databases are shared and which are not. And finally, a multi-user daemon should support one configuration file that is properly parameterised to support direct paths to standardised places under home directories, and/or to name files by their user, and so on.

Where multiple users come into play, we should also think about aliases / pseudonyms and about groups / roles. The alias / pseudonym angle should be dealt with in the disclose.db and localid.db together with the LID entry API, but group access has not been considered yet. Especially when group access can be as smooth and per-connection regulated as already done for individual identities, it is going to be a highly practical enhancement of the user experience. Maybe groups differ from person identities, maybe not.

A profound area of concern in multi-user support lies in the separation of various users, while retaining co-ordination and some forms of sharing between users. In addition, a point of care is establishing a local user's identity -- where UNIX domain sockets are helpful in passing file descriptors, they are not so useful that the other end's identity can be retrieved as with SysV IPC mechanisms.

The same mechanism as for multi-user support, except perhaps with multiple access sockets, could be used to run the TLS Pool in a host environment, independently of a virtual machine or container / jail. We should consider if this is a viable alternative use case when we consider multi-user support.

On Windows, a multi-user mode is likely to translate to a Windows Service which, like POSIX services, runs only once for the benefit of many users.

vanrein commented 7 years ago

Remote TLS Pool

TLS Pool is designed as a local dæmon, but it is destined to be a netwerk service. Certainly in the IdentityHub, where components may be distributed between hosts.

The TLS Pool exchanges two types of information:

The former can be resolved with a packet-based carrier, the latter is more compelling. The following changes are now needed:

TLS Pool as Network Component

The TLS Pool listens on a network port. This is an SCTP port, possibly also over UDP. SCTP is useful because it packetises commands (over stream 0) and can use extra streams for TLS handshaking.

The client connects to the TLS Pool. It opens a least two streams, more if it wants to. The number of streams beyond 0 determines how many TLS handshakes can be run concurrently. The client of the TLS Pool can use this to size their use of the network service.

The TLS handshake can be all that is done over these extra streams. When so requested, the TLS Pool can send back the negotiated keys. The client uses these to implement the TLS bulk traffic. It may decide to return to the TLS Pool if ever it wants to renegotiate the TLS connection (or it may refuse to partake in it).

It is probably desirable to use ASN.1 and DER encoding for the command packets. The TLS Pool can still apply limitations on packet sizes, and thus translate back to the usual structure. In due course, we may decide to abolish these packets on the API to the TLS Pool and end up with a TLS Pool that can be sized more easily. More importantly, ASN.1 is helpful in making the format extensible.

Authorisation of TLS Pool Clients

The simplest form of authorisation is simply allowing everything, and thus relying on access control through network-level filtering and routing.

Authorisation to use the TLS Pool is by itself not so important, as the TLS Pool is in many ways just a protocol implementation for handshaking. The one thing that needs to be protected is access to PKCS #11 credentials (and perhaps their listing in databases).

To accommodate bulk handling, the client may represent a large number of identities, such as an entire domain name or even a multi-tenant service. Authorisation indicates that this client has performed reasonable access limitations on local identities that it wants to use. Note that the TLS Pool frames contain a field with the local identifier to use.

Encryption of TLS Pool Connections

Since the connection to the TLS Pool can carry key material, there is a need to protect the carrier. This is easily established with ECDHE while binding the public keys into the authorisation step. Or, alternatively, the use of Kerberos5 can help to have a session key.

Only stream 0 needs to be protected, as the other streams carry TLS handshakes which are meant to be suitable for plaintext transport. (Variations may apply to renegotiation though.)

TLS Connection Key Material

Key material can be sent back in two ways. When only the master key is sent back, all derivations from it must be made in the TLS Pool client, meaning that it must have a fair bit of protocol knowledge, especially incorporating the various protocol variants.

On the other hand, if the master key is not sent back, then the TLS Pool must keep state on the connection, which it may in fact do as long as stream 0 remains open; this is the already-existing mechanism for a controlling file descriptor.

Multi-User and Multi-Tenant Service

Given the the TLS Pool client authenticates, it is possible to have it function in multi-user mode. The Kerberos ticket of the user's login can be used to derive the appropriate ticket to address the TLS Pool (or the PKCS #11 repository).

A similar thing applies to the multi-tenant variation, where there can simply be more identities under reach of the authenticated identity.

One thing that may have to change to make this work, is how we search through the localid.db as that is currently treated as a global database; it may make more sense to have one such database per authenticated client of the TLS Pool. Or we may incorporate the authenticated client identity in the search key, or have a field declaring the welcomed client identities.

The disclose.db is something to think about. Do we still want to map local identities to remote identities, even when multiple authenticated clients may be authorised to use the same local identity? This is probably a good basic assumption to start with, considering that such sharing usually expresses a shared identity for a group or role, and it would be awkward if clients each have to setup their own idea of disclosure.

Concerning the trust.db, there is no reason for distinguishing between clients at all. Trust is assumed to be provisioned (using SteamWorks Pulley) and the same information is assumed to be shared by all parties.

vanrein commented 7 years ago

Note: The discussion above is triggered by inclusion of the TLS Pool as a component in the IdentityHub. It wonders whether the TLS Pool can be run in an independent Docker container, and what the result would be. As is shown, the symmetric-key aspects are then implemented by the various end points and only the handshake is arranged by the TLS Pool. This may not be a bad idea at all.

vanrein commented 7 years ago

See #60 for another angle on this; Diameter can be a very useful carrier for the TLS connections.

vanrein commented 7 years ago

Most of the problems are resolved through client authentication, which is a necessity in any multi-tenant solution.

Client Authentication: When clients authenticate, we can treat them separately, even over network links. Once again, Diameter #60 feels like the right protocol for remote access, because it was built to support authentication. We might use a simple password, or embed an interaction into EAP-TTLS or even EAP-Kerberos, depending on how far or remote a user actually is. Local transports such as UNIX domain sockets and SysV IPC mechanisms can be controlled based on local userid as well, to allow straightforward local access.

AuthzID versus AuthnID: Clients will authenticate to the TLS Pool using their AuthnID, and this is what its disclose.db entries start from. The AuthzID is actually found through the entries in disclose.db so they are no real concern to the TLS Pool. Users who switch to a pseudonym would re-authenticate (possibly using a free-upgrade credential) and end up in another section of the disclose.db. This assumes a different index in the disclose.db for each AuthnID.

Separation of Credentials: The PKCS#11 store used should be different for each client, and it should not allow identities from one to be available in another. This means that a separate PKCS#11 connection should be setup for each client session. That really is different. It also means that the PKCS#11 PIN may have to be entered multiple times (assuming it cannot be replaced by other contextual information, such as a Kerberos ticket).

Servers and multi-tenancy: It is a bad idea to combine clients and servers in the same TLS Pool. But how about combining multiple servers? There is no problem with this, as long as it is clear that they all share the same set of names, and credentials. The disclose.db is not used for the server-side of TLS and so every identity in localid.db is available to the requesting client. This is normal; though we now mix multiple services. In general, consider splitting the TLS Pool runs between servers, even when they might still be run on the same machine, but then on a different IP address or port.

GnuTLS and PKCS#11: There is one potential problem; GnuTLS treats PKCS#11 settings as global settings, so in fact GnuTLS is unsuitable for multi-tenancy. This need not be a surprise, since GnuTLS was not designed to be pooled. VITAL

TODO:

vanrein commented 7 years ago

Nikos @ GnuTLS helped out saying:

You may want to check gnutls_pkcs11_privkey_t handling. Only the shared module should be global on its handling. Everything else is local to the object.

That's helpful.

  1. Modules are in the globals providers / #active_providers in pkcs11.c
  2. A global _gnutls_pin_func is set from gnutls_pkcs11_set_pin_function(), or a per-privkey pin_info is set from gnutls_pkcs11_privkey_set_pin_function()

In short, limiting visibility of modules to clients remains one concern (mostly one of privacy) but the major concern (of security) through visibility of the PIN between clients can probably be achieved already.

Good, now I know where and how to scratch my head over this design issue.