mirror of
https://github.com/moparisthebest/sslh
synced 2024-11-28 20:12:24 -05:00
Implement libcap support
Use libcap for saving CAP_NET_ADMIN (if --transparent is given) over a setuid(). We don’t need CAP_NET_BIND_SERVICE as the listening sockets are established before dropping root.
This commit is contained in:
parent
3f386b6541
commit
009faa64b7
6
Makefile
6
Makefile
@ -3,6 +3,7 @@
|
|||||||
VERSION=$(shell ./genver.sh -r)
|
VERSION=$(shell ./genver.sh -r)
|
||||||
USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files)
|
USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files)
|
||||||
USELIBWRAP= # Use libwrap?
|
USELIBWRAP= # Use libwrap?
|
||||||
|
USELIBCAP= # Use libcap?
|
||||||
COV_TEST= # Perform test coverage?
|
COV_TEST= # Perform test coverage?
|
||||||
PREFIX=/usr/local
|
PREFIX=/usr/local
|
||||||
|
|
||||||
@ -31,6 +32,11 @@ ifneq ($(strip $(USELIBCONFIG)),)
|
|||||||
CPPFLAGS+=-DLIBCONFIG
|
CPPFLAGS+=-DLIBCONFIG
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq ($(strip $(USELIBCAP)),)
|
||||||
|
LIBS:=$(LIBS) -lcap
|
||||||
|
CPPFLAGS+=-DLIBCAP
|
||||||
|
endif
|
||||||
|
|
||||||
all: sslh $(MAN) echosrv
|
all: sslh $(MAN) echosrv
|
||||||
|
|
||||||
.c.o: *.h
|
.c.o: *.h
|
||||||
|
31
README
31
README
@ -157,16 +157,21 @@ in inetd mode.
|
|||||||
sslh options: -i for inetd mode, --http to forward http
|
sslh options: -i for inetd mode, --http to forward http
|
||||||
connexions to port 80, and SSH connexions to port 22.
|
connexions to port 80, and SSH connexions to port 22.
|
||||||
|
|
||||||
==== capapbilities support ====
|
==== capabilities support ====
|
||||||
|
|
||||||
On Linux (only?), you can use POSIX capabilities to reduce a
|
On Linux (only?), you can compile sslh with USELIBCAP=1 to
|
||||||
server's capabilities to the minimum it needs (see
|
make use of POSIX capabilities; this will save the required
|
||||||
capabilities(8). For sslh, this is CAP_NET_ADMIN (to
|
capabilities needed for transparent proxying for unprivileged
|
||||||
perform transparent proxy-ing) and CAP_NET_BIND_SERVICE (to
|
processes.
|
||||||
bind to port 443 without being root).
|
|
||||||
|
|
||||||
The simplest way to use capabilities is to give them to the
|
Alternatively, you may use filesystem capabilities instead
|
||||||
executable as root:
|
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
|
# setcap cap_net_bind_service,cap_net_admin+pe sslh-select
|
||||||
|
|
||||||
@ -174,13 +179,9 @@ Then you can run sslh-select as an unpriviledged user, e.g.:
|
|||||||
|
|
||||||
$ sslh-select -p myname:443 --ssh localhost:22 --ssl localhost:443
|
$ sslh-select -p myname:443 --ssh localhost:22 --ssl localhost:443
|
||||||
|
|
||||||
This has 2 advantages over starting as root with -u:
|
|
||||||
- You no longer start as root (duh)
|
|
||||||
- This enables transparent proxying.
|
|
||||||
|
|
||||||
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
|
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 ====
|
==== Transparent proxy support ====
|
||||||
|
|
||||||
@ -192,8 +193,8 @@ simplifies IP-based access control (or makes it possible at
|
|||||||
all).
|
all).
|
||||||
|
|
||||||
sslh needs extended rights to perform this: you'll need to
|
sslh needs extended rights to perform this: you'll need to
|
||||||
give it cap_net_admin capabilities (see appropriate chapter)
|
give it cap_net_admin capabilities manually or enable libcap
|
||||||
or run it as root (but don't do that).
|
support (see appropriate chapter).
|
||||||
|
|
||||||
The firewalling tables also need to be adjusted as follow
|
The firewalling tables also need to be adjusted as follow
|
||||||
(example to connect to https on 4443 -- adapt to your needs
|
(example to connect to https on 4443 -- adapt to your needs
|
||||||
|
59
common.c
59
common.c
@ -37,6 +37,10 @@ struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */
|
|||||||
int allow_severity =0, deny_severity = 0;
|
int allow_severity =0, deny_severity = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef LIBCAP
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/capability.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* check result and die, printing the offending address and error */
|
/* check result and die, printing the offending address and error */
|
||||||
void check_res_dumpdie(int res, struct addrinfo *addr, char* syscall)
|
void check_res_dumpdie(int res, struct addrinfo *addr, char* syscall)
|
||||||
@ -509,6 +513,56 @@ void setup_syslog(const char* bin_name) {
|
|||||||
log_message(LOG_INFO, "%s %s started\n", server_type, VERSION);
|
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 */
|
/* We don't want to run as root -- drop privileges if required */
|
||||||
void drop_privileges(const char* user_name)
|
void drop_privileges(const char* user_name)
|
||||||
{
|
{
|
||||||
@ -521,10 +575,15 @@ void drop_privileges(const char* user_name)
|
|||||||
if (verbose)
|
if (verbose)
|
||||||
fprintf(stderr, "turning into %s\n", user_name);
|
fprintf(stderr, "turning into %s\n", user_name);
|
||||||
|
|
||||||
|
set_keepcaps(1);
|
||||||
|
|
||||||
res = setgid(pw->pw_gid);
|
res = setgid(pw->pw_gid);
|
||||||
CHECK_RES_DIE(res, "setgid");
|
CHECK_RES_DIE(res, "setgid");
|
||||||
res = setuid(pw->pw_uid);
|
res = setuid(pw->pw_uid);
|
||||||
CHECK_RES_DIE(res, "setuid");
|
CHECK_RES_DIE(res, "setuid");
|
||||||
|
|
||||||
|
set_capabilities();
|
||||||
|
set_keepcaps(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Writes my PID */
|
/* Writes my PID */
|
||||||
|
Loading…
Reference in New Issue
Block a user