mirror of
https://github.com/yrutschle/sslh.git
synced 2026-10-25 01:06:20 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d39388d1ed | ||
|
|
70a776fb9a | ||
|
|
9fadd60eed | ||
|
|
83c9465c37 | ||
|
|
1137779b9c | ||
|
|
4d3aa6c6e0 | ||
|
|
579baae363 | ||
|
|
e2d01a7440 | ||
|
|
ecc76071c1 | ||
|
|
e5d7cd0575 | ||
|
|
d288125ff3 | ||
|
|
2cc1e1f1d9 | ||
|
|
77dc76a0f2 | ||
|
|
86188cdd28 | ||
|
|
c29081f71d | ||
|
|
8d58164fdd | ||
|
|
894eacfb42 | ||
|
|
6310e6fc9c |
No files matched your search
@@ -1,3 +1,32 @@
|
||||
v2.3.1:
|
||||
Fix segmentation fault in sslh-fork.
|
||||
|
||||
Add file access when using Landlock and Libwrap
|
||||
together.
|
||||
|
||||
Mixed IPv4/IPv6 proxyprotocol setups will move both
|
||||
addresses to IPv6 format on the server side.
|
||||
|
||||
Add CAP_NET_RAW to systemd config so transparent
|
||||
proxying can work. Removed sslh-select systemd file:
|
||||
don't repeat the same configuration file!
|
||||
|
||||
Changed SNI matching log messages from
|
||||
`msg_probe_error` to `msg_probe_info`: beware if you
|
||||
happen to use them...
|
||||
|
||||
Close unused file descriptors upon forking a worker
|
||||
for a connection (in sslh-ev and sslh-select). In
|
||||
particular, this prevents forked processes to
|
||||
inherit the listen sockets and block a new main
|
||||
process from starting.
|
||||
|
||||
More configuration sanity checks (e.g. using regex
|
||||
or proxyprotocol options if libraries are not compiled in)
|
||||
|
||||
-V now outputs compile-time options so it is
|
||||
possible to know what is compiled in.
|
||||
|
||||
v2.3.0:
|
||||
Added `max_connections` setting to `listen` and
|
||||
`protocol` configuration; see the
|
||||
|
||||
+26
-3
@@ -3,8 +3,7 @@ ARG TARGET_ARCH="library"
|
||||
|
||||
FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION} AS build
|
||||
|
||||
WORKDIR /sslh
|
||||
|
||||
# Install build dependencies for sslh and libproxyprotocol
|
||||
RUN apk add --no-cache \
|
||||
'gcc' \
|
||||
'libconfig-dev' \
|
||||
@@ -12,15 +11,39 @@ RUN apk add --no-cache \
|
||||
'musl-dev' \
|
||||
'pcre2-dev' \
|
||||
'perl' \
|
||||
'git' \
|
||||
'autoconf' \
|
||||
'automake' \
|
||||
'libtool' \
|
||||
;
|
||||
|
||||
# Build libproxyprotocol
|
||||
WORKDIR /tmp
|
||||
RUN git clone https://github.com/kosmas-valianos/libproxyprotocol.git && \
|
||||
cd libproxyprotocol && \
|
||||
make && \
|
||||
mkdir -p /usr/local/include/ && \
|
||||
mv libs/libproxyprotocol.so /usr/local/lib/ && \
|
||||
mv src/* /usr/local/include/
|
||||
|
||||
# Set environment variables for sslh to find libproxyprotocol
|
||||
ENV C_INCLUDE_PATH=/usr/local/include
|
||||
ENV LIBRARY_PATH=/usr/local/lib
|
||||
ENV LD_LIBRARY_PATH=/usr/local/lib
|
||||
|
||||
WORKDIR /sslh
|
||||
COPY . /sslh
|
||||
|
||||
# Configure and build sslh
|
||||
# The configure script should automatically detect libproxyprotocol
|
||||
RUN ./configure && make sslh-select && strip sslh-select
|
||||
|
||||
FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION}
|
||||
|
||||
# Copy libproxyprotocol.so from the build stage
|
||||
COPY --from=build /usr/local/lib/libproxyprotocol.so* /usr/local/lib/
|
||||
COPY --from=build "/sslh/sslh-select" "/usr/local/bin/sslh"
|
||||
|
||||
RUN apk add --no-cache \
|
||||
'libconfig' \
|
||||
'pcre2' \
|
||||
@@ -35,4 +58,4 @@ COPY "./container-entrypoint.sh" "/init"
|
||||
ENTRYPOINT [ "/init" ]
|
||||
|
||||
# required for updating iptables
|
||||
USER root:root
|
||||
USER root:root
|
||||
+2
-2
@@ -22,11 +22,11 @@ MAN=sslh.8.gz # man page name
|
||||
# itself
|
||||
|
||||
ifneq ($(strip $(ENABLE_SANITIZER)),)
|
||||
CFLAGS_SAN=-fsanitize=address -fsanitize=leak -fsanitize=undefined -fsanitize=alignment
|
||||
CFLAGS_SAN=-DENABLE_SANITIZER -fsanitize=address -fsanitize=leak -fsanitize=undefined -fsanitize=alignment
|
||||
endif
|
||||
|
||||
ifneq ($(strip $(COV_TEST)),)
|
||||
CFLAGS_COV=-fprofile-arcs -ftest-coverage
|
||||
CFLAGS_COV=-DCOV_TEST -fprofile-arcs -ftest-coverage
|
||||
endif
|
||||
|
||||
CC ?= gcc
|
||||
|
||||
+10
-2
@@ -28,6 +28,7 @@
|
||||
/* Info to keep track of all connections */
|
||||
struct cnx_collection {
|
||||
gap_array* fd2cnx; /* Array indexed by file descriptor to things in cnx[] */
|
||||
int max_fd;
|
||||
};
|
||||
|
||||
/* Allocates and initialises a new collection of connections with at least
|
||||
@@ -42,6 +43,7 @@ cnx_collection* collection_init(int len)
|
||||
memset(collection, 0, sizeof(*collection));
|
||||
|
||||
collection->fd2cnx = gap_init(len);
|
||||
collection->max_fd = 0;
|
||||
|
||||
return collection;
|
||||
}
|
||||
@@ -54,10 +56,17 @@ void collection_destroy(cnx_collection* collection)
|
||||
free(collection);
|
||||
}
|
||||
|
||||
int collection_max_fd(cnx_collection* collection)
|
||||
{
|
||||
return collection->max_fd;
|
||||
}
|
||||
|
||||
/* Points the file descriptor to the specified connection index */
|
||||
int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd)
|
||||
{
|
||||
gap_set(collection->fd2cnx, fd, cnx);
|
||||
if (fd > collection->max_fd)
|
||||
collection->max_fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -74,8 +83,7 @@ struct connection* collection_alloc_cnx_from_fd(struct cnx_collection* collectio
|
||||
cnx->state = ST_PROBING;
|
||||
cnx->probe_timeout = time(NULL) + cfg.timeout;
|
||||
|
||||
gap_set(collection->fd2cnx, fd, cnx);
|
||||
|
||||
collection_add_fd(collection, cnx, fd);
|
||||
return cnx;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ void collection_destroy(cnx_collection* collection);
|
||||
|
||||
struct connection* collection_alloc_cnx_from_fd(cnx_collection* collection, int fd);
|
||||
int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd);
|
||||
int collection_max_fd(cnx_collection* collection);
|
||||
|
||||
/* Remove a connection from the collection */
|
||||
int collection_remove_cnx(cnx_collection* collection, struct connection *cnx);
|
||||
|
||||
@@ -422,9 +422,9 @@ static int connect_inet(struct connection *cnx, int fd_from, connect_blocking bl
|
||||
cnx->proto->port);
|
||||
}
|
||||
for (a = cnx->proto->saddr; a; a = a->ai_next) {
|
||||
/* When transparent or using proxyprotocol, make sure both
|
||||
* connections use the same address family (e.g. IP4 on both sides) */
|
||||
if ((transparent || cnx->proto->proxyprotocol_is_present) && (a->ai_family != from.ai_addr->sa_family))
|
||||
/* When transparent, make sure both connections use
|
||||
* the same address family (e.g. IP4 on both sides) */
|
||||
if (transparent && (a->ai_family != from.ai_addr->sa_family))
|
||||
continue;
|
||||
print_message(msg_connections_try, "trying to connect to %s family %d len %d\n",
|
||||
sprintaddr(buf, sizeof(buf), a),
|
||||
@@ -899,7 +899,7 @@ int check_access_rights(int in_socket, const char* service)
|
||||
}
|
||||
|
||||
if (!hosts_ctl((char*)service, host, addr_str, STRING_UNKNOWN)) {
|
||||
print_message(msg_connections, "connection from %s(%s): access denied", host, addr_str);
|
||||
print_message(msg_connections, "connection from %s(%s): access denied\n", host, addr_str);
|
||||
close(in_socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ extern int hosts_ctl();
|
||||
#endif
|
||||
|
||||
/* sslh-fork.c */
|
||||
void start_shoveler(int);
|
||||
void main_inetd(void);
|
||||
|
||||
void main_loop(struct listen_endpoint *listen_sockets, int num_addr_listen);
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ protocols:
|
||||
(
|
||||
{ name: "ssh"; host: "localhost"; port: "2222"; },
|
||||
{ name: "tls"; host: "localhost"; port: "8080"; proxyprotocol: 2; }
|
||||
);
|
||||
);
|
||||
```
|
||||
|
||||
@@ -77,7 +76,6 @@ protocols:
|
||||
(
|
||||
{ name: "ssh"; host: "localhost"; port: "2222"; proxyprotocol: 0; },
|
||||
{ name: "tls"; host: "localhost"; port: "8080"; }
|
||||
);
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -118,7 +118,7 @@ static int add_resolv(int ruleset_fd)
|
||||
static int add_libwrap(int ruleset_fd)
|
||||
{
|
||||
/* Files for libwrap */
|
||||
#ifdef LIBWRAP
|
||||
#ifdef HAVE_LIBWRAP
|
||||
add_path_ro(ruleset_fd, LL_FILE, "/etc/hosts.allow");
|
||||
add_path_ro(ruleset_fd, LL_FILE, "/etc/hosts.deny");
|
||||
#endif
|
||||
|
||||
+2
-1
@@ -196,7 +196,7 @@ static int next_power_of_two(int n)
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
void loop_init(struct loop_info* loop, int num_addr_listen)
|
||||
void loop_init(struct loop_info* loop, struct listen_endpoint* listen_sockets, int num_addr_listen)
|
||||
{
|
||||
memset(loop, 0, sizeof(*loop));
|
||||
loop->collection = collection_init(0);
|
||||
@@ -206,6 +206,7 @@ void loop_init(struct loop_info* loop, int num_addr_listen)
|
||||
udp_init(loop);
|
||||
tcp_init();
|
||||
|
||||
loop->listen_sockets = listen_sockets;
|
||||
loop->num_addr_listen = num_addr_listen;
|
||||
|
||||
int max_forks = next_power_of_two(max_forking_connections());
|
||||
|
||||
+5
-1
@@ -23,6 +23,8 @@ struct loop_info {
|
||||
hash* pid2proto; /* to follow which forked PID is processing what protocol for connection count */
|
||||
|
||||
watchers* watchers;
|
||||
|
||||
struct listen_endpoint* listen_sockets;
|
||||
int num_addr_listen; /* How many listen endpoints do we have here */
|
||||
|
||||
cnx_collection* collection; /* Collection of connections linked to this loop */
|
||||
@@ -32,7 +34,7 @@ void cnx_read_process(struct loop_info* fd_info, int fd);
|
||||
struct connection* cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket);
|
||||
|
||||
int tidy_connection(struct connection *cnx, struct loop_info* fd_info);
|
||||
void loop_init(struct loop_info* loop, int num_addr_listen);
|
||||
void loop_init(struct loop_info* loop, struct listen_endpoint* listen_sockets, int num_addr_listen);
|
||||
|
||||
void remember_child_data(struct loop_info* fd_info,
|
||||
struct connection* cnx, pid_t pid);
|
||||
@@ -47,4 +49,6 @@ void watchers_del_write(watchers* w, int fd);
|
||||
|
||||
void watcher_sigchld(struct loop_info* fd_info, struct connection* cnx, pid_t pid);
|
||||
|
||||
void close_listen_endpoints(struct loop_info* loop);
|
||||
|
||||
#endif
|
||||
+2
-2
@@ -112,7 +112,7 @@ int pp_write_header(int pp_version, struct connection* cnx)
|
||||
if (res == -1) return -1;
|
||||
pp_info_in_v1.address_family = family_to_pp(addr.ai_addr->sa_family);
|
||||
|
||||
res = get_info(cnx->q[1].fd, SOCK,
|
||||
res = get_info(cnx->q[0].fd, SOCK,
|
||||
&addr,
|
||||
&pp_info_in_v1.dst_addr,
|
||||
&pp_info_in_v1.dst_port
|
||||
@@ -122,7 +122,7 @@ int pp_write_header(int pp_version, struct connection* cnx)
|
||||
uint8_t *pp1_hdr = pp_create_hdr(pp_version, &pp_info_in_v1, &pp1_hdr_len, &error);
|
||||
|
||||
if (!pp1_hdr) {
|
||||
print_message(msg_system_error, "pp_create_hrd:%d:%s\n", error, pp_strerror(error));
|
||||
print_message(msg_system_error, "pp_create_hdr:%d:%s\n", error, pp_strerror(error));
|
||||
return -1;
|
||||
}
|
||||
defer_write_before(&cnx->q[1], pp1_hdr, pp1_hdr_len);
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
[Unit]
|
||||
Description=SSL/SSH multiplexer (select mode) for %I
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
EnvironmentFile=/etc/default/sslh
|
||||
ExecStart=/usr/sbin/sslh-select -F/etc/sslh/%I.cfg -f $DAEMON_OPTS
|
||||
KillMode=process
|
||||
#Hardening
|
||||
PrivateTmp=true
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
SecureBits=noroot-locked
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ProtectKernelModules=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectControlGroups=true
|
||||
MountFlags=private
|
||||
NoNewPrivileges=true
|
||||
PrivateDevices=true
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
||||
MemoryDenyWriteExecute=true
|
||||
DynamicUser=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,5 +1,5 @@
|
||||
[Unit]
|
||||
Description=SSL/SSH multiplexer (fork mode) for %I
|
||||
Description=SSL/SSH multiplexer for %I
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
@@ -8,8 +8,8 @@ ExecStart=/usr/sbin/sslh -F/etc/sslh/%I.cfg -f $DAEMON_OPTS
|
||||
KillMode=process
|
||||
#Hardening
|
||||
PrivateTmp=true
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=CAP_SETGIT CAP_SETUID CAP_NET_BIND_SERVICE CAP_NET_RAW
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_RAW
|
||||
SecureBits=noroot-locked
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
|
||||
@@ -207,13 +207,12 @@ void watcher_sigchld(struct loop_info* fd_info, struct connection* cnx, pid_t pi
|
||||
ev_child_start(EV_DEFAULT_ cw);
|
||||
}
|
||||
|
||||
|
||||
void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
{
|
||||
struct loop_info ev_info;
|
||||
loop = EV_DEFAULT;
|
||||
|
||||
loop_init(&ev_info, num_addr_listen);
|
||||
loop_init(&ev_info, listen_sockets, num_addr_listen);
|
||||
|
||||
watchers_init(&ev_info.watchers, listen_sockets, num_addr_listen);
|
||||
ev_set_userdata(EV_A_ &ev_info);
|
||||
@@ -221,7 +220,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
ev_run(EV_A_ 0);
|
||||
}
|
||||
|
||||
void start_shoveler(int listen_socket) {
|
||||
void main_inetd(void) {
|
||||
print_message(msg_config_error, "inetd mode is not supported in libev mode\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
+19
-2
@@ -69,7 +69,7 @@ int shovel(struct connection *cnx)
|
||||
|
||||
/* Child process that finds out what to connect to and proxies
|
||||
*/
|
||||
void start_shoveler(int in_socket)
|
||||
static void start_shoveler(int in_socket, struct listen_endpoint* endpoint)
|
||||
{
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
@@ -79,6 +79,7 @@ void start_shoveler(int in_socket)
|
||||
|
||||
init_cnx(&cnx);
|
||||
cnx.q[0].fd = in_socket;
|
||||
cnx.endpoint = endpoint;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(in_socket, &fds);
|
||||
@@ -130,6 +131,22 @@ void start_shoveler(int in_socket)
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
void main_inetd(void)
|
||||
{
|
||||
struct listen_endpoint endpoint = {0};
|
||||
struct sslhcfg_listen_item endpoint_cfg = {0};
|
||||
|
||||
/* Empty configuration: no connection limits, not proxyprotocol... */
|
||||
endpoint.endpoint_cfg = &endpoint_cfg;
|
||||
|
||||
close(fileno(stderr)); /* Make sure no error will go to client */
|
||||
tcp_init();
|
||||
start_shoveler(0, &endpoint);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
static pid_t *listener_pid;
|
||||
static int listener_pid_number = 0;
|
||||
|
||||
@@ -252,7 +269,7 @@ void tcp_listener(struct listen_endpoint* endpoint, int num_endpoints, int activ
|
||||
/* Shoveler processes don't need to hog file descriptors */
|
||||
for (i = 0; i < num_endpoints; i++)
|
||||
close(endpoint[i].socketfd);
|
||||
start_shoveler(in_socket);
|
||||
start_shoveler(in_socket, endpoint);
|
||||
exit(0);
|
||||
|
||||
default: /* In parent process */
|
||||
|
||||
+101
-5
@@ -220,6 +220,62 @@ static void config_protocols()
|
||||
}
|
||||
}
|
||||
|
||||
static void config_sanity_regex(const struct sslhcfg_protocols_item* prot)
|
||||
{
|
||||
if (prot->regex_patterns_len > 0) {
|
||||
if (strcmp(prot->name, "regex") != 0) {
|
||||
print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": "
|
||||
"regex_patterns setting is only applicable to `regex' probe. Ignoring setting.\n",
|
||||
prot->name, prot->host, prot->port);
|
||||
}
|
||||
#ifndef ENABLE_REGEX
|
||||
print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": "
|
||||
"Uses regex_patterns, but libpcre2 support was not compiled in. Ignoring setting.\n",
|
||||
prot->name, prot->host, prot->port);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* If user specified proxyprotocol but it is not compiled in, it should not be
|
||||
* ignored as the resulting configuration will most likely not work. In that
|
||||
* case, die. */
|
||||
static void config_sanity_proxyprotocol(const struct sslhcfg_protocols_item* prot)
|
||||
{
|
||||
#ifndef HAVE_PROXYPROTOCOL
|
||||
if (prot->proxyprotocol_is_present) {
|
||||
print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": "
|
||||
"Uses proxyprotocol, but libproxyprotocol support was not compiled in.\n",
|
||||
prot->name, prot->host, prot->port);
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void config_sanity_listen_proxyprotocol(const struct sslhcfg_listen_item* endpoint)
|
||||
{
|
||||
#ifndef HAVE_PROXYPROTOCOL
|
||||
if (endpoint->proxyprotocol) {
|
||||
print_message(msg_config_error, "listen on host: \"%s\"; port: \"%s\": "
|
||||
"Uses proxyprotocol, but libproxyprotocol support was not compiled in.\n",
|
||||
endpoint->host, endpoint->port);
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* If user specifies libwrap setting but it is not compiled in, it should
|
||||
* result in a working configuration, but not what they expected, so warn */
|
||||
static void config_sanity_libwrap(const struct sslhcfg_protocols_item* prot)
|
||||
{
|
||||
#ifndef HAVE_LIBWRAP
|
||||
if (prot->service_is_present) {
|
||||
print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": "
|
||||
"WARNING: `service' specified but libwrap not compiled in. Ignoring setting.\n",
|
||||
prot->name, prot->host, prot->port);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void config_sanity_check(struct sslhcfg_item* cfg)
|
||||
{
|
||||
@@ -233,6 +289,10 @@ void config_sanity_check(struct sslhcfg_item* cfg)
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = 0; i < cfg->listen_len; i++) {
|
||||
config_sanity_listen_proxyprotocol(&cfg->listen[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < cfg->protocols_len; ++i) {
|
||||
if (strcmp(cfg->protocols[i].name, "tls") != 0) {
|
||||
if (cfg->protocols[i].sni_hostnames_len) {
|
||||
@@ -249,6 +309,10 @@ void config_sanity_check(struct sslhcfg_item* cfg)
|
||||
}
|
||||
}
|
||||
|
||||
config_sanity_regex(&cfg->protocols[i]);
|
||||
config_sanity_proxyprotocol(&cfg->protocols[i]);
|
||||
config_sanity_libwrap(&cfg->protocols[i]);
|
||||
|
||||
if (cfg->protocols[i].is_udp) {
|
||||
if (cfg->protocols[i].tfo_ok) {
|
||||
print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": "
|
||||
@@ -284,6 +348,41 @@ void close_std(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void print_version(void)
|
||||
{
|
||||
printf("%s %s\n", server_type, VERSION);
|
||||
#ifdef ENABLE_SANITIZER
|
||||
printf("ENABLE_SANITIZER\n");
|
||||
#endif
|
||||
#ifdef ENABLE_REGEX
|
||||
printf("ENABLE_REGEX\n");
|
||||
#endif
|
||||
#ifdef LIBCONFIG
|
||||
printf("LIBCONFIG\n");
|
||||
#endif
|
||||
#ifdef SYSTEMD
|
||||
printf("SYSTEMD\n");
|
||||
#endif
|
||||
#ifdef COV_TEST
|
||||
printf("COV_TEST\n");
|
||||
#endif
|
||||
#ifdef HAVE_LIBWRAP
|
||||
printf("HAVE_LIBWRAP\n");
|
||||
#endif
|
||||
#ifdef HAVE_LANDLOCK
|
||||
printf("HAVE_LANDLOCK\n");
|
||||
#endif
|
||||
#ifdef HAVE_PROXYPROTOCOL
|
||||
printf("HAVE_PROXYPROTOCOL\n");
|
||||
#endif
|
||||
#ifdef HAVE_LIBCAP
|
||||
printf("HAVE_LIBCAP\n");
|
||||
#endif
|
||||
#ifdef HAVE_LIBBSD
|
||||
printf("HAVE_LIBBSD\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[], char* envp[])
|
||||
{
|
||||
int res, num_addr_listen;
|
||||
@@ -299,7 +398,7 @@ int main(int argc, char *argv[], char* envp[])
|
||||
config_finish(&cfg);
|
||||
|
||||
if (cfg.version) {
|
||||
printf("%s %s\n", server_type, VERSION);
|
||||
print_version();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -308,10 +407,7 @@ int main(int argc, char *argv[], char* envp[])
|
||||
|
||||
if (cfg.inetd)
|
||||
{
|
||||
close(fileno(stderr)); /* Make sure no error will go to client */
|
||||
tcp_init();
|
||||
start_shoveler(0);
|
||||
exit(0);
|
||||
main_inetd(); /* Does not return */
|
||||
}
|
||||
|
||||
printsettings();
|
||||
|
||||
+2
-2
@@ -204,7 +204,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
int i, res;
|
||||
|
||||
setup_sigalrm();
|
||||
loop_init(&fd_info, num_addr_listen);
|
||||
loop_init(&fd_info, listen_sockets, num_addr_listen);
|
||||
|
||||
watchers_init(&fd_info.watchers, listen_sockets, num_addr_listen);
|
||||
|
||||
@@ -292,7 +292,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
}
|
||||
|
||||
|
||||
void start_shoveler(int listen_socket) {
|
||||
void main_inetd(void) {
|
||||
print_message(msg_config_error, "inetd mode is not supported in select mode\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
# ./run # run all tests
|
||||
# ./run -l # list all tests
|
||||
# ./run 1 3 5 # run specified tests
|
||||
# ./run --binary sslh-ev # run only one binary
|
||||
|
||||
use strict;
|
||||
use IO::Socket::INET6;
|
||||
@@ -17,9 +18,13 @@ use Test::More qw/no_plan/;
|
||||
use Conf::Libconfig 1.0.3;
|
||||
use Getopt::Long;
|
||||
|
||||
my ($coverage, $list_tests);
|
||||
# --cover: request test coverage analysis (not implemented)
|
||||
# --binary: specify which binary to run (default: all)
|
||||
# --list: list all available tests (do not run any)
|
||||
my ($coverage, $list_tests, $param_binary);
|
||||
GetOptions(
|
||||
'cover' => \$coverage,
|
||||
'binary=s' => \$param_binary,
|
||||
'list' => \$list_tests,
|
||||
);
|
||||
|
||||
@@ -192,7 +197,8 @@ sub run_ending_test
|
||||
|
||||
# Runs one test for one probe. Start echosrv's if required.
|
||||
# Connect to port specified in the test, otherwise to the
|
||||
# first port in the sslh configuration.
|
||||
# first port in the sslh configuration; to host specified in
|
||||
# the test, otherwise to localhost
|
||||
# run sslh, connect, write the test pattern, read the result,
|
||||
# check it connected to the right echosrv, check the data
|
||||
# was transfered ok.
|
||||
@@ -206,9 +212,10 @@ sub run_test_probe
|
||||
|
||||
my $expected = $test->{expected};
|
||||
|
||||
my $sslh_host = $test->{host} // "localhost";
|
||||
my $sslh_port = $test->{port} // $conf->value("listen")->[0]->{port};
|
||||
print "test_probe [$expected] $sslh_port\n";
|
||||
my $cnx = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
|
||||
print "test_probe [$expected] $sslh_host:$sslh_port\n";
|
||||
my $cnx = new IO::Socket::INET(PeerHost => "$sslh_host:$sslh_port");
|
||||
warn "t: $!\n" unless $cnx;
|
||||
return unless $cnx;
|
||||
|
||||
@@ -316,6 +323,7 @@ my @tests = (
|
||||
data_expected => "^PROXY TCP4 127.0.0.1 127.0.0.1 \\d+ \\d+\x0D\x0AINPUT_DATA",
|
||||
},
|
||||
|
||||
|
||||
# check socks5 does not
|
||||
{
|
||||
desc => "Server-side ProxyProtocol not active",
|
||||
@@ -325,6 +333,55 @@ my @tests = (
|
||||
expected => "socks5",
|
||||
},
|
||||
|
||||
|
||||
# Check proxyprotocol from IPv4 to IPv4 works.
|
||||
{
|
||||
desc => "Server-side IPv4 to IPv4 ProxyProtocol active",
|
||||
host => "ip4-localhost",
|
||||
run => \&run_test_probe,
|
||||
cfg => "test_pp3.cfg",
|
||||
data => "SSH-2.0 hello",
|
||||
expected => "ssh",
|
||||
data_expected => "^PROXY TCP4 127.0.0.1 127.0.0.1 \\d+ 8080\x0D\x0AINPUT_DATA",
|
||||
},
|
||||
|
||||
|
||||
# Check pp with IPv6 to IPv6 works.
|
||||
{
|
||||
desc => "Server-side IPv6 to IPv6 ProxyProtocol active",
|
||||
host => "ip6-localhost",
|
||||
run => \&run_test_probe,
|
||||
cfg => "test_pp4.cfg",
|
||||
data => "SSH-2.0 hello",
|
||||
expected => "ssh",
|
||||
data_expected => "^PROXY TCP6 ::ffff:127.0.0.1 ::ffff:127.0.0.1 \\d+ 8080\x0D\x0AINPUT_DATA",
|
||||
},
|
||||
|
||||
|
||||
# Check pp with IPv4 to IPv6 works.
|
||||
{
|
||||
desc => "Server-side IPv4 to IPv6 ProxyProtocol active",
|
||||
host => "ip4-localhost",
|
||||
run => \&run_test_probe,
|
||||
cfg => "test_pp5.cfg",
|
||||
data => "SSH-2.0 hello",
|
||||
expected => "ssh",
|
||||
data_expected => "^PROXY TCP6 ::ffff:127.0.0.1 ::ffff:127.0.0.1 \\d+ 8080\x0D\x0AINPUT_DATA",
|
||||
},
|
||||
|
||||
|
||||
# Check pp with IPv6 to IPv4 works.
|
||||
{
|
||||
desc => "Server-side IPv6 to IPv4 ProxyProtocol active",
|
||||
host => "ip6-localhost",
|
||||
run => \&run_test_probe,
|
||||
cfg => "test_pp5.cfg",
|
||||
data => "SSH-2.0 hello",
|
||||
expected => "ssh",
|
||||
data_expected => "^PROXY TCP6 ::ffff:127.0.0.1 ::ffff:127.0.0.1 \\d+ 8080\x0D\x0AINPUT_DATA",
|
||||
},
|
||||
|
||||
|
||||
# test_pp2.cfg has proxyprotocol on port 8080
|
||||
{
|
||||
desc => "Client-side ProxyProtocol active",
|
||||
@@ -388,10 +445,15 @@ if ($list_tests) {
|
||||
|
||||
################################################################################
|
||||
# Run selected tests
|
||||
foreach my $test (@tests) {
|
||||
warn "Running test: $test->{desc}\n";
|
||||
my $binary = 'sslh-ev';
|
||||
my $code = ($test->{run})->($binary, $test);
|
||||
|
||||
my @binaries = qw/sslh-fork sslh-select sslh-ev/;
|
||||
@binaries = $param_binary if defined $param_binary;
|
||||
|
||||
foreach my $binary (@binaries) {
|
||||
foreach my $test (@tests) {
|
||||
warn "Running test: $test->{desc}\n";
|
||||
my $code = ($test->{run})->($binary, $test);
|
||||
}
|
||||
}
|
||||
|
||||
stop_echosrv();
|
||||
|
||||
+4
-4
@@ -29,15 +29,15 @@ verbose-int-error: 1; # internal errors, the kind that should never happen
|
||||
# Options:
|
||||
listen:
|
||||
(
|
||||
{ host: "localhost"; port: "8080"; keepalive: true; }
|
||||
{ host: "ip4-localhost"; port: "8080"; keepalive: true; }
|
||||
);
|
||||
|
||||
|
||||
protocols:
|
||||
(
|
||||
{ name: "ssh"; host: "localhost"; port: "9000"; proxyprotocol: 1; },
|
||||
{ name: "socks5"; host: "localhost"; port: "9001"; },
|
||||
{ name: "anyprot"; host: "localhost"; port: "9099"; }
|
||||
{ name: "ssh"; host: "ip4-localhost"; port: "9000"; proxyprotocol: 1; },
|
||||
{ name: "socks5"; host: "ip4-localhost"; port: "9001"; },
|
||||
{ name: "anyprot"; host: "ip4-localhost"; port: "9099"; }
|
||||
);
|
||||
|
||||
on_timeout: "ssh";
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Testing for server-side ProxyProtocol, IPv4 to IPv4
|
||||
|
||||
foreground: true;
|
||||
inetd: false;
|
||||
numeric: true;
|
||||
transparent: false;
|
||||
timeout: 10; # Probe test writes slowly
|
||||
pidfile: "/tmp/sslh_test.pid";
|
||||
|
||||
syslog_facility: "auth";
|
||||
|
||||
# Logging configuration
|
||||
# Value: 1: stdout; 2: syslog; 3: both
|
||||
# Defaults should be sensible. Generally, you want *-error
|
||||
# to be always enabled, to know if something is going wrong.
|
||||
verbose-config: 1; # print configuration at startup
|
||||
verbose-config-error: 1; # print configuration errors
|
||||
verbose-connections: 1; # trace established incoming address to forward address
|
||||
verbose-connections-error: 1; # connection errors
|
||||
verbose-connections-try: 1; # connection attempts towards targets
|
||||
verbose-fd: 1; # file descriptor activity, open/close/whatnot
|
||||
verbose-packets: 1; # hexdump packets on which probing is done
|
||||
verbose-probe-info: 1; # what's happening during the probe process
|
||||
verbose-probe-error: 1; # failures and problems during probing
|
||||
verbose-system-error: 1; # system call problem, i.e. malloc, fork, failing
|
||||
verbose-int-error: 1; # internal errors, the kind that should never happen
|
||||
|
||||
# List of interfaces on which we should listen
|
||||
# Options:
|
||||
listen:
|
||||
(
|
||||
{ host: "ip4-localhost"; port: "8080"; keepalive: true; }
|
||||
);
|
||||
|
||||
|
||||
protocols:
|
||||
(
|
||||
{ name: "ssh"; host: "ip4-localhost"; port: "9000"; proxyprotocol: 1; },
|
||||
{ name: "socks5"; host: "localhost"; port: "9001"; },
|
||||
{ name: "anyprot"; host: "localhost"; port: "9099"; }
|
||||
);
|
||||
|
||||
on_timeout: "ssh";
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Testing for server-side ProxyProtocol, IPv6 to IPv6
|
||||
|
||||
foreground: true;
|
||||
inetd: false;
|
||||
numeric: true;
|
||||
transparent: false;
|
||||
timeout: 10; # Probe test writes slowly
|
||||
pidfile: "/tmp/sslh_test.pid";
|
||||
|
||||
syslog_facility: "auth";
|
||||
|
||||
# Logging configuration
|
||||
# Value: 1: stdout; 2: syslog; 3: both
|
||||
# Defaults should be sensible. Generally, you want *-error
|
||||
# to be always enabled, to know if something is going wrong.
|
||||
verbose-config: 1; # print configuration at startup
|
||||
verbose-config-error: 1; # print configuration errors
|
||||
verbose-connections: 1; # trace established incoming address to forward address
|
||||
verbose-connections-error: 1; # connection errors
|
||||
verbose-connections-try: 1; # connection attempts towards targets
|
||||
verbose-fd: 1; # file descriptor activity, open/close/whatnot
|
||||
verbose-packets: 1; # hexdump packets on which probing is done
|
||||
verbose-probe-info: 1; # what's happening during the probe process
|
||||
verbose-probe-error: 1; # failures and problems during probing
|
||||
verbose-system-error: 1; # system call problem, i.e. malloc, fork, failing
|
||||
verbose-int-error: 1; # internal errors, the kind that should never happen
|
||||
|
||||
# List of interfaces on which we should listen
|
||||
# Options:
|
||||
listen:
|
||||
(
|
||||
{ host: "ip6-localhost"; port: "8080"; keepalive: true; }
|
||||
);
|
||||
|
||||
|
||||
protocols:
|
||||
(
|
||||
{ name: "ssh"; host: "ip6-localhost"; port: "9000"; proxyprotocol: 1; },
|
||||
{ name: "socks5"; host: "ip6-localhost"; port: "9001"; },
|
||||
{ name: "anyprot"; host: "ip6-localhost"; port: "9099"; }
|
||||
);
|
||||
|
||||
on_timeout: "ssh";
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Testing for server-side ProxyProtocol, IPv4 to IPv6
|
||||
|
||||
foreground: true;
|
||||
inetd: false;
|
||||
numeric: true;
|
||||
transparent: false;
|
||||
timeout: 10; # Probe test writes slowly
|
||||
pidfile: "/tmp/sslh_test.pid";
|
||||
|
||||
syslog_facility: "auth";
|
||||
|
||||
# Logging configuration
|
||||
# Value: 1: stdout; 2: syslog; 3: both
|
||||
# Defaults should be sensible. Generally, you want *-error
|
||||
# to be always enabled, to know if something is going wrong.
|
||||
verbose-config: 1; # print configuration at startup
|
||||
verbose-config-error: 1; # print configuration errors
|
||||
verbose-connections: 1; # trace established incoming address to forward address
|
||||
verbose-connections-error: 1; # connection errors
|
||||
verbose-connections-try: 1; # connection attempts towards targets
|
||||
verbose-fd: 1; # file descriptor activity, open/close/whatnot
|
||||
verbose-packets: 1; # hexdump packets on which probing is done
|
||||
verbose-probe-info: 1; # what's happening during the probe process
|
||||
verbose-probe-error: 1; # failures and problems during probing
|
||||
verbose-system-error: 1; # system call problem, i.e. malloc, fork, failing
|
||||
verbose-int-error: 1; # internal errors, the kind that should never happen
|
||||
|
||||
# List of interfaces on which we should listen
|
||||
# Options:
|
||||
listen:
|
||||
(
|
||||
{ host: "ip4-localhost"; port: "8080"; keepalive: true; }
|
||||
);
|
||||
|
||||
|
||||
protocols:
|
||||
(
|
||||
{ name: "ssh"; host: "ip6-localhost"; port: "9000"; proxyprotocol: 1; },
|
||||
{ name: "socks5"; host: "ip6-localhost"; port: "9001"; },
|
||||
{ name: "anyprot"; host: "ip6-localhost"; port: "9099"; }
|
||||
);
|
||||
|
||||
on_timeout: "ssh";
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Testing for server-side ProxyProtocol, IPv6 to IPv4
|
||||
|
||||
foreground: true;
|
||||
inetd: false;
|
||||
numeric: true;
|
||||
transparent: false;
|
||||
timeout: 10; # Probe test writes slowly
|
||||
pidfile: "/tmp/sslh_test.pid";
|
||||
|
||||
syslog_facility: "auth";
|
||||
|
||||
# Logging configuration
|
||||
# Value: 1: stdout; 2: syslog; 3: both
|
||||
# Defaults should be sensible. Generally, you want *-error
|
||||
# to be always enabled, to know if something is going wrong.
|
||||
verbose-config: 1; # print configuration at startup
|
||||
verbose-config-error: 1; # print configuration errors
|
||||
verbose-connections: 1; # trace established incoming address to forward address
|
||||
verbose-connections-error: 1; # connection errors
|
||||
verbose-connections-try: 1; # connection attempts towards targets
|
||||
verbose-fd: 1; # file descriptor activity, open/close/whatnot
|
||||
verbose-packets: 1; # hexdump packets on which probing is done
|
||||
verbose-probe-info: 1; # what's happening during the probe process
|
||||
verbose-probe-error: 1; # failures and problems during probing
|
||||
verbose-system-error: 1; # system call problem, i.e. malloc, fork, failing
|
||||
verbose-int-error: 1; # internal errors, the kind that should never happen
|
||||
|
||||
# List of interfaces on which we should listen
|
||||
# Options:
|
||||
listen:
|
||||
(
|
||||
{ host: "ip6-localhost"; port: "8080"; keepalive: true; }
|
||||
);
|
||||
|
||||
|
||||
protocols:
|
||||
(
|
||||
{ name: "ssh"; host: "ip4-localhost"; port: "9000"; proxyprotocol: 1; },
|
||||
{ name: "socks5"; host: "ip6-localhost"; port: "9001"; },
|
||||
{ name: "anyprot"; host: "ip6-localhost"; port: "9099"; }
|
||||
);
|
||||
|
||||
on_timeout: "ssh";
|
||||
|
||||
+33
-2
@@ -296,6 +296,37 @@ static void connect_proxy(struct connection *cnx)
|
||||
}
|
||||
|
||||
|
||||
/* This ensures the kernel will close the listening socket when the
|
||||
* parent process closes; otherwise, the listening socket is kept
|
||||
* around by the inactive file descriptor in the child */
|
||||
void close_listen_endpoints(struct loop_info* loop)
|
||||
{
|
||||
struct listen_endpoint* listen_sockets = loop->listen_sockets;
|
||||
watchers* w = loop->watchers;
|
||||
|
||||
for (int i = 0; i < loop->num_addr_listen; i++) {
|
||||
int fd = listen_sockets[i].socketfd;
|
||||
watchers_del_read(w, fd);
|
||||
watchers_del_write(w, fd);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Close all connections except specified.
|
||||
* This saves file descriptors and allocated memory when forking */
|
||||
void tidy_other_connections(struct loop_info* fd_info, struct connection* keep_cnx)
|
||||
{
|
||||
struct connection* cnx;
|
||||
cnx_collection* cnx_coll = fd_info->collection;
|
||||
|
||||
for (int i = 0; i < collection_max_fd(cnx_coll); i++) {
|
||||
cnx = collection_get_cnx_from_fd(cnx_coll, i);
|
||||
if (cnx && (cnx != keep_cnx))
|
||||
tidy_connection(cnx, fd_info);
|
||||
}
|
||||
}
|
||||
|
||||
/* Forks a shoveler process for one protocol */
|
||||
void fork_shoveling_process(struct loop_info* fd_info, struct connection* cnx) {
|
||||
pid_t pid;
|
||||
@@ -307,8 +338,8 @@ void fork_shoveling_process(struct loop_info* fd_info, struct connection* cnx) {
|
||||
|
||||
switch (pid = fork()) {
|
||||
case 0: /* child */
|
||||
/* TODO: close all file descriptors except 2 */
|
||||
/* free(cnx); */
|
||||
close_listen_endpoints(fd_info);
|
||||
tidy_other_connections(fd_info, cnx);
|
||||
connect_proxy(cnx);
|
||||
exit(0);
|
||||
case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno));
|
||||
|
||||
@@ -277,7 +277,7 @@ has_match(const char** list, size_t list_len, const char* name, size_t name_len)
|
||||
|
||||
for (i = 0; i < list_len; i++) {
|
||||
item = &list[i];
|
||||
print_message(msg_probe_error, "matching [%.*s] with [%s]\n", (int)name_len, name, *item);
|
||||
print_message(msg_probe_info, "matching [%.*s] with [%s]\n", (int)name_len, name, *item);
|
||||
if(!fnmatch(*item, name_nullterminated, 0)) {
|
||||
free(name_nullterminated);
|
||||
return 1;
|
||||
|
||||
Reference in new issue
Block a user