Merged libcap patch

This commit is contained in:
Yves Rutschle 2014-02-09 20:34:26 +01:00
commit 4dfa694e8a
4 changed files with 310 additions and 15 deletions

View File

@ -3,6 +3,7 @@
VERSION=$(shell ./genver.sh -r)
USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files)
USELIBWRAP= # Use libwrap?
USELIBCAP= # Use libcap?
COV_TEST= # Perform test coverage?
PREFIX=/usr/local
@ -31,6 +32,11 @@ ifneq ($(strip $(USELIBCONFIG)),)
CPPFLAGS+=-DLIBCONFIG
endif
ifneq ($(strip $(USELIBCAP)),)
LIBS:=$(LIBS) -lcap
CPPFLAGS+=-DLIBCAP
endif
all: sslh $(MAN) echosrv
.c.o: *.h

229
README Normal file
View File

@ -0,0 +1,229 @@
===== sslh -- A ssl/ssh multiplexer. =====
Sslh accepts connections on specified ports, and forwards
them further based on tests performed on the first data
packet sent by the remote client.
Probes for HTTP, SSL, SSH, OpenVPN, tinc, XMPP are
implemented, and any other protocol that can be tested using
a regular expression, can be recognised. A typical use case
is to allow serving several services on port 443 (e.g. to
connect to ssh from inside a corporate firewall, which
almost never block port 443) while still serving HTTPS on
that port.
Hence sslh acts as a protocol demultiplexer, or a
switchboard. Its name comes from its original function to
serve SSH and HTTPS on the same port.
==== Compile and install ====
sslh uses libconfig (http://www.hyperrealm.com/libconfig/)
and libwrap.
For Debian, these are contained in packages libwrap0-dev and
libconfig8-dev.
For OpenSUSE, these are contained in packages libconfig9 and
libconfig-dev in repository
http://download.opensuse.org/repositories/multimedia:/libs/openSUSE_12.1/
For Fedora, you'll need packages libconfig and
libconfig-devel:
yum install libconfig libconfig-devel
If you can't find libconfig, or just don't want a
configuration file, set 'USELIBCONFIG=' in the Makefile.
After this, the Makefile should work:
make install
There are a couple of configuration options at the beginning
of the Makefile:
USELIBWRAP compiles support for host access control (see
hosts_access(3)), you will need libwrap headers and
library to compile (libwrap0-dev in Debian).
USELIBCONFIG compiles support for the configuration
file. You will need libconfig headers to compile
(libconfig8-dev in Debian).
The Makefile produces two different executables: sslh-fork
and sslh-select.
sslh-fork forks a new process for each incoming connection.
It is well-tested and very reliable, but incurs the overhead
of many processes. sslh-select uses only one thread, which
monitors all connections at once. It is more recent and less
tested, but only incurs a 16 byte overhead per connection.
Also, if it stops, you'll lose all connections, which means
you can't upgrade it remotely.
If you are going to use sslh for a "small" setup (less than
a dozen ssh connections and a low-traffic https server) then
sslh-fork is probably more suited for you. If you are going
to use sslh on a "medium" setup (a few thousand ssh
connections, and another few thousand ssl connections),
sslh-select will be better. If you have a very large site
(tens of thousands of connections), you'll need a vapourware
version that would use libevent or something like that.
To install:
make
cp sslh-fork /usr/local/sbin/sslh
cp scripts/etc.default.sslh /etc/default/sslh
For Debian:
cp scripts/etc.init.d.sslh /etc/init.d/sslh
For CentOS:
cp scripts/etc.rc.d.init.d.sslh /etc/rc.d/init.d/sslh
and probably create links in /etc/rc<x>.d so that the server
start automatically at boot-up, e.g. under Debian:
update-rc.d sslh defaults
==== Configuration ====
You can edit settings in /etc/default/sslh:
LISTEN=ifname:443
SSH=localhost:22
SSL=localhost:443
A good scheme is to use the external name of the machine in
$LISTEN, and bind httpd to localhost:443 (instead of all
binding to all interfaces): that way, https connections
coming from inside your network don't need to go through
sslh, and sslh is only there as a frontal for connections
coming from the internet.
Note that 'external name' in this context refers to the
actual IP address of the machine as seen from your network,
i.e. that that is not 127.0.0.1 in the output of
ifconfig(8).
==== Libwrap support ====
Sslh can optionnaly perform libwrap checks for the sshd
service: because the connection to sshd will be coming
locally from sslh, sshd cannot determine the IP of the
client.
==== OpenVPN support ====
OpenVPN clients connecting to OpenVPN running with
-port-share reportedly take more than one second between
the time the TCP connexion is established and the time they
send the first data packet. This results in sslh with
default settings timing out and assuming an SSH connexion.
To support OpenVPN connexions reliably, it is necessary to
increase sslh's timeout to 5 seconds.
Instead of using OpenVPN's port sharing, it is more reliable
to use sslh's -o option to get sslh to do the port sharing.
==== Using proxytunnel with sslh ====
If you are connecting through a proxy that checks that the
outgoing connection really is SSL and rejects SSH, you can
encapsulate all your traffic in SSL using proxytunnel (this
should work with corkscrew as well). On the server side you
receive the traffic with stunnel to decapsulate SSL, then
pipe through sslh to switch HTTP on one side and SSL on the
other.
In that case, you end up with something like this:
ssh -> proxytunnel -e --------ssh/ssl------> stunnel ---ssh---> sslh --> sshd
Web browser --------http/ssl------> stunnel ---http---> sslh --> http:80
Configuration goes like this:
On the server side, using stunnel3:
stunnel -f -p mycert.pem -d thelonious:443 -l /usr/local/sbin/sslh -- sslh -i --http localhost:80 --ssh localhost:22
stunnel options: -f for foreground/debugging, -p specifies
the key + certificate, -d specifies which interface and port
we're listening to for incoming connexions, -l summons sslh
in inetd mode.
sslh options: -i for inetd mode, --http to forward http
connexions to port 80, and SSH connexions to port 22.
==== capabilities support ====
On Linux (only?), you can compile sslh with USELIBCAP=1 to
make use of POSIX capabilities; this will save the required
capabilities needed for transparent proxying for unprivileged
processes.
Alternatively, you may use filesystem capabilities instead
of starting sslh as root and asking it to drop privileges.
You will need CAP_NET_BIND_SERVICE for listening on port 443
and CAP_NET_ADMIN for transparent proxying (see
capabilities(7)).
You can use the setcap(8) utility to give these capabilities
to the executable:
# setcap cap_net_bind_service,cap_net_admin+pe sslh-select
Then you can run sslh-select as an unpriviledged user, e.g.:
$ sslh-select -p myname:443 --ssh localhost:22 --ssl localhost:443
Caveat: CAP_NET_ADMIN does give sslh too many rights, e.g.
configuring the interface. If you're not going to use
transparent proxying, just don't use it (or use the libcap method).
==== Transparent proxy support ====
On Linux (only?) you can use the --transparent option to
request transparent proying. This means services behind sslh
(Apache, sshd and so on) will see the external IP and ports
as if the external world connected directly to them. This
simplifies IP-based access control (or makes it possible at
all).
sslh needs extended rights to perform this: you'll need to
give it cap_net_admin capabilities manually or enable libcap
support (see appropriate chapter).
The firewalling tables also need to be adjusted as follow
(example to connect to https on 4443 -- adapt to your needs
(I don't think it is possible to have httpd listen to 443 in
this scheme -- let me know if you manage that))):
# iptables -t mangle -N SSLH
# iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 22 --jump SSLH
# iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 --sport 4443 --jump SSLH
# iptables -t mangle -A SSLH --jump MARK --set-mark 0x1
# iptables -t mangle -A SSLH --jump ACCEPT
# ip rule add fwmark 0x1 lookup 100
# ip route add local 0.0.0.0/0 dev lo table 100
This will only work if sslh does not use any loopback
addresses (no 127.0.0.1 or localhost), you'll need to use
explicit IP addresses (or names):
sslh --listen 192.168.0.1:443 --ssh 192.168.0.1:22 --ssl 192.168.0.1:4443
This will not work:
sslh --listen 192.168.0.1:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:4443
==== Comments? Questions? ====
You can subscribe to the sslh mailing list here:
http://rutschle.net/cgi-bin/mailman/listinfo/sslh
This mailing list should be used for discussion, feature
requests, and will be the prefered channel for
announcements.

View File

@ -189,28 +189,29 @@ Configuration goes like this on the server side, using `stunnel3`:
Capabilities support
--------------------
On Linux (only?), you can use POSIX capabilities to reduce a
server's capabilities to the minimum it needs (see
`capabilities(8)`. For sslh, this is `CAP_NET_ADMIN` (to
perform transparent proxy-ing) and `CAP_NET_BIND_SERVICE` (to
bind to port 443 without being root).
On Linux (only?), you can compile sslh with USELIBCAP=1 to
make use of POSIX capabilities; this will save the required
capabilities needed for transparent proxying for unprivileged
processes.
The simplest way to use capabilities is to give them to the
executable as root:
Alternatively, you may use filesystem capabilities instead
of starting sslh as root and asking it to drop privileges.
You will need CAP_NET_BIND_SERVICE for listening on port 443
and CAP_NET_ADMIN for transparent proxying (see
capabilities(7)).
# setcap cap_net_bind_service,cap_net_admin+pe sslh-select
You can use the setcap(8) utility to give these capabilities
to the executable:
Then you can run `sslh-select` as an unpriviledged user, e.g.:
# setcap cap_net_bind_service,cap_net_admin+pe sslh-select
$ sslh-select -p myname:443 --ssh localhost:22 --ssl localhost:443
Then you can run sslh-select as an unpriviledged user, e.g.:
This has 2 advantages over starting as root with `-u`:
- You no longer start as root (duh)
- This enables transparent proxying.
$ sslh-select -p myname:443 --ssh localhost:22 --ssl localhost:443
Caveat: `CAP_NET_ADMIN` does give `sslh` too many rights, e.g.
Caveat: CAP_NET_ADMIN does give sslh too many rights, e.g.
configuring the interface. If you're not going to use
transparent proxying, just don't use it.
transparent proxying, just don't use it (or use the libcap method).
Transparent proxy support
-------------------------

View File

@ -37,6 +37,10 @@ struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */
int allow_severity =0, deny_severity = 0;
#endif
#ifdef LIBCAP
#include <sys/prctl.h>
#include <sys/capability.h>
#endif
/* check result and die, printing the offending address and error */
void check_res_dumpdie(int res, struct addrinfo *addr, char* syscall)
@ -514,6 +518,56 @@ void setup_syslog(const char* bin_name) {
log_message(LOG_INFO, "%s %s started\n", server_type, VERSION);
}
/* Ask OS to keep capabilities over a setuid(nonzero) */
void set_keepcaps(int val) {
#ifdef LIBCAP
int res;
res = prctl(PR_SET_KEEPCAPS, val, 0, 0, 0);
if (res) {
perror("prctl");
exit(1);
}
#endif
}
/* set needed capabilities for effective and permitted, clear rest */
void set_capabilities(void) {
#ifdef LIBCAP
int res;
cap_t caps;
cap_value_t cap_list[10];
int ncap = 0;
if (transparent)
cap_list[ncap++] = CAP_NET_ADMIN;
caps = cap_init();
#define _cap_set_flag(flag) do { \
res = cap_clear_flag(caps, flag); \
CHECK_RES_DIE(res, "cap_clear_flag(" #flag ")"); \
if (ncap > 0) { \
res = cap_set_flag(caps, flag, ncap, cap_list, CAP_SET); \
CHECK_RES_DIE(res, "cap_set_flag(" #flag ")"); \
} \
} while(0)
_cap_set_flag(CAP_EFFECTIVE);
_cap_set_flag(CAP_PERMITTED);
#undef _cap_set_flag
res = cap_set_proc(caps);
CHECK_RES_DIE(res, "cap_set_proc");
res = cap_free(caps);
if (res) {
perror("cap_free");
exit(1);
}
#endif
}
/* We don't want to run as root -- drop privileges if required */
void drop_privileges(const char* user_name)
{
@ -526,10 +580,15 @@ void drop_privileges(const char* user_name)
if (verbose)
fprintf(stderr, "turning into %s\n", user_name);
set_keepcaps(1);
res = setgid(pw->pw_gid);
CHECK_RES_DIE(res, "setgid");
res = setuid(pw->pw_uid);
CHECK_RES_DIE(res, "setuid");
set_capabilities();
set_keepcaps(0);
}
/* Writes my PID */