Lukko

What is Lukko?

Lukko is a SSH server.

It is not (yet) a drop-in for other SSH implementations such as OpenSSH. Lukko is currently used for shared hosting environments with a large number of user accounts where each session runs in a separate container. Maybe it will evolve into a general-purpose SSH server eventually, but that is not a priority.

Configuration

Lukko loads the configuration file /etc/cm4all/lukko/lukko.conf.

The following top-level settings are recognized:

  • translation_server: consult this translation server to configure child processes; must start with / (absolute path) or @ (abstract socket).

  • populate_io_buffers: yes populates all I/O buffers on startup. This reduces waits for Linux kernel VM compaction/migration.

Listener

The listener section describes how Lukko listens for incoming SSH connections. Example:

listener {
  bind "*:22"
  #interface "eth0"
  #zeroconf_service "lukko"
}

Known attributes:

  • bind: an adddress to bind to. May be the wildcard * or an IPv4/IPv6 address followed by a port. IPv6 addresses should be enclosed in square brackets to disambiguate the port separator. Local sockets start with a slash /, and abstract sockets start with the symbol @.

  • interface: limit this listener to the given network interface.

  • mode: for local socket files, this specifies the octal file mode.

  • mptcp: yes enables Multi-Path TCP

  • ack_timeout: close the connection if transmitted data remains unacknowledged by the client for this number of seconds. By default, dead connections can remain open for up to 20 minutes.

  • keepalive: yes enables the socket option SO_KEEPALIVE. This causes some traffic for the keepalive probes, but allows detecting disappeared clients even when there is no traffic.

  • v6only: no disables IPv4 support on IPv6 listeners (IPV6_V6ONLY). The default is yes.

  • reuse_port: yes enables the socket option SO_REUSEPORT, which allows multiple sockets to bind to the same port.

  • zeroconf_service: if specified, then register this listener as Zeroconf service in the local Avahi daemon.

  • zeroconf_domain (optional): The name of the Zeroconf domain.

  • zeroconf_interface: publish the Zeroconf service only on the given interface.

  • zeroconf_protocol (optional): Publish only protocol inet or inet6.

  • zeroconf_weight: publish the Zeroconf service with the specified “weight”, i.e. ask a load balancer to use this weight when choosing nodes. The value is a decimal number; the implied default value is 1.0. For example, if you specify 0.5, you expect this node to get only half as many connections as others.

  • tag: a string sent to the translation server in a LISTENER_TAG packet.

  • max_connections_per_ip: specifies the maximum number of connections from each IP address.

  • tarpit: yes enables a naive denial-of-service protection: clients that connect too often or fail authentication get delayed responses. The exact conditions and the delay is currently hard-coded. The default is no.

  • verbose_errors: yes sends internal error messages to the client on stderr.

  • host_key_file: Load a SSH host (secret) key from a file. This option may appear more than once to load keys from multiple files to load multiple host keys (with different key algorithms). If no host key is loaded, then the default host key files are used (host_ed25519_key, host_ecdsa_key, host_rsa_key); this option is only needed if certain listeners need a special non-standard host key.

  • authorized_host_key_file: Enable hostbased authentication on this listener and load public keys from a text file that will be authorized unconditionally (see Host-Based Authentication). This option may appear more than once to load keys from multiple files.

  • exec_reject_stderr: yes means when an exec request on a session channel is rejected (e.g. for SFTP-only accounts), Lukko pretends the request has succeeded, but prints an error message on stderr. This is a slight protocol violation but may be less confusing for users than the normal OpenSSH client error message “shell request failed on channel 0”.

  • pond_server: send log messages to this Pond server.

  • proxy_to: act as proxy, forward all incoming connections after the authentication phase to the specified target. See Proxy Mode Configuration for details.

  • proxy_to_zeroconf: act as proxy, forward all incoming connections after the authentication phase to the specified Zeroconf cluster.

  • accept_client_address: If enabled, then accept the real client address from the client during hostbased authentication. This way, Lukko shows the real client address in the log and in :env:`$SSH_CLIENT`.

Proxy Mode Configuration

When acting as a proxy (with the proxy_to setting), Lukko forward all incoming connections after the authentication phase to another server. The connection to this target is authenticated using the SSH host key with the “hostbased” method.

Individual target hosts can be configured with a target_host section:

target_host "foo" {
  address "192.168.1.100"
}

Known attributes:

  • address: The host’s address. If not specified, then the name of the section is parsed instead.

  • host_key_file: A text file containing the host public key (or many keys, one per line). This option may appear more than once.

  • send_client_address: If enabled, then send the real client address to the target server. This requires enable accept_client_address on its listener.

Zeroconf cluster

The zeroconf_cluster section describes a destination for the proxy_to_zeroconf setting:

zeroconf_cluster "name" {
  zeroconf_service "lukko-internal"
  zeroconf_interface "internal"
}

Known attributes:

  • zeroconf_service: The name of the Zeroconf service.

  • zeroconf_domain (optional): The name of the Zeroconf service.

  • zeroconf_interface (optional): Look up only on this network interface.

  • zeroconf_protocol (optional): Limit lookups to inet or inet6.

  • host_key_file: A text file containing the host public key (or many keys, one per line). This option may appear more than once.

  • send_client_address: If enabled, then send the real client address to the target server. This requires enable accept_client_address on its listener.

Control Listener

The control section creates a listener for control datagrams that can be used to control certain behavior at runtime. Example:

control {
  bind "@lukko-control"
}

control {
  bind "*"
  interface "eth1"
  multicast_group "224.0.0.123"
}

Known attributes:

  • bind: an adddress to bind to. May be the wildcard * or an IPv4/IPv6 address followed by a port. IPv6 addresses should be enclosed in square brackets to disambiguate the port separator. Local sockets start with a slash /, and abstract sockets start with the symbol @.

  • multicast_group: join this multicast group, which allows receiving multicast commands. Value is a multicast IPv4/IPv6 address. IPv6 addresses may contain a scope identifier after a percent sign (%).

  • interface: limit this listener to the given network interface.

The protocol is defined here: https://github.com/CM4all/libcommon/blob/master/src/net/control/Protocol.hxx

Lukko implements only a subset of the commands:

  • VERBOSE

  • DISABLE_ZEROCONF

  • ENABLE_ZEROCONF

  • TERMINATE_CHILDREN

Prometheus Exporter

The prometheus_exporter section is optional and can describe a simple HTTP listener which exposes statistics in the Prometheus format. Example:

prometheus_exporter {
  bind "*:8022"
  interface "eth1"
}

prometheus_exporter {
  bind "/run/cm4all/lukko/prometheus_exporter.socket"
}

Known attributes (same meaning as in a listener block):

  • bind

  • interface

  • mode

  • v6only

  • reuse_port

Translation Server

Lukko can delegate certain decisions (user database, how to execute commands) to a different process running on the same computer, called a “translation server”. This translation server may, for example, consult a database to look up user accounts instead of reading /etc/passwd and can make complex decicions based on that data. Only the translation server has access to all of Lukko’s process spawner features, which includes a light-weight container engine.

Information about the translation protocol can be found here:

Authentication

Public Key Authentication

Public keys in ~/.ssh/authorized_keys and /etc/cm4all/lukko/authorized_keys are allowed to log in. Lukko supports the OpenSSH file format and implements the following options:

  • command: Forced command.

  • port-forwarding, no-port-forwarding: Allow or disallow port forwarding.

  • pty, no-pty: Allow or disallow tty allocation.

  • restrict: Enable all restrictions, i.e. is an alias for no-port-forwarding and no-pty.

  • home-read-only: Mount the home directory read-only.

The following OpenSSH options are not implemented and are ignored silently:

  • user-rc, no-user-rc

  • agent-forwarding, no-agent-forwarding

  • X11-forwarding, no-X11-forwarding

Password Authentication

Passwords are verified by the translation server, therefore this authentication method is only available if a translation server is configured.

Host-Based Authentication

Host-based authentication can be enabled on a listener with the authorized_host_key_file setting. Authentication requests using one of these configured host keys are allowed to log in.