Years after years, OpenSSH became the default SSH daemon on most of UNIX environments as well as other systems. It offers a lot of features which make it terribly customizable and powerful.

For a while, the ‘Match” keyword appeared in the list of directives. It allows conditional blocks of configuration directives. If the criteria(s) defined are matched, new directives may overwrite the default ones. This is extremely useful to build complex configurations.

Examples: Users connecting from an internal subnet can use their password but must use their RSA key to authenticate when connecting from unknown hosts. Port forwarding can be restricted to users of a specific group.

The current version of OpenSSH supports four criteria with the “Match” directive:

User – To match one or more local users

Group – To match one or more local groups

Host – To match a FQDN or domain name

Address – To match a single address or subnet.

Each condition may contain a single or comma-separated values and support patterns:

Host *.rootshell.be

User john, leo, !chris

Address 192.168.1.16/28

When a condition is met, all the directives below will be applied until the next “Match” section or the end-of-file. This is why, they must be defined at the end of your sshd_config file. Let’s have a look at the following example:

# Allow administrators to use the agent & X11 forwarding Match Group admin AllowAgentForwarding yes X11Forwarding yes # Regular users cannot forward TCP sessions and cannot use a password Match Group users AllowTcpForwarding no PasswordAuthentication no Banner banner.users # Users from the DMZ are chroot'd Match Address 192.168.0.0/24 ChrootDirectory /var/sandbox

This is very powerful! But when you start to play with such nice features, you quickly become frustrated by the limited criteria, only four in the current version. This afternoon, I discussed with my friend Chris John Riley about the “Match” feature. He wished to be able to use different authentication types depending on the port used to connect to a host. It looks indeed a cool feature. Let’s dive into the OpenSSH source code.

First, it’s good to know that OpenSSH allow multiple instances of the directives “Port” and “ListenAddress”. In my sshd_config file, I use:

Port 22 Port 443

Then, I wrote a small patch which implements a new “Match” criteria called “Localport“. In the source code, a function already exists to return the local port used:

/* Returns remote/local port number for the current connection. */ static int get_port(int local) { /* * If the connection is not a socket, return 65535. This is * intentionally chosen to be an unprivileged port number. */ if (!packet_connection_is_on_socket()) return 65535; /* Get socket and return the port number. */ return get_sock_port(packet_get_connection_in(), local); }

It’s easy to use it and compare the returned value with the port(s) defined in the “Match” condition. Now, I can use the following conditions:

Match LocalPort 22 PasswordAuthentication yes RSAAuthentication no Match LocalPort 443 PasswordAuthentication no KerberosAuthentication yes AllowTcpForwarding no

Etc… Interesting isn’t? And, by using different ports, it is possible to increase the server security. It is also possible to play with QoS. Port 22 can be assigned to regular users and port 443 (with reserved bandwidth) to administrators. This way, you will be able to reach your server even in case of heavy network load.

Remark: not all directives can be used in conditional blocks! Check out the sshd_config man page for the list of supported directives.

My OpenSSH patch is available here. It is based on the source code of OpenSSH 5.5p1. As usual, there is warranty at all. Use it at your own risks.