Merge branch 'proxyprotocol'

This commit is contained in:
Yves Rutschle committed 2025-09-10 15:31:43 +02:00
commit 6f3dcfb365
23 files changed
+824 -21

No files matched your search

+2
View File
@@ -6,6 +6,8 @@ vNEXT:
accept() is now disabled for some time when ulimit -n is reached.
Support proxyprotocol on incoming connections.
Fix proxyprotocol target field (Thanks to Github user 404-Not-Found)
Fix memory leak in regex probe.
+2 -1
View File
@@ -60,7 +60,8 @@ The same result can be achieved more easily by using
`proxyprotocol` if the backend server supports it. This is a
simple setting to add to the `sslh` protocol configuration,
usually with an equivalently simple setting to add in
the backend server configuration, so try that first.
the backend server configuration, so try that first. This is
explained in a [separate document](doc/proxyprotocol.md).
This means services behind `sslh` (Apache, `sshd` and so on)
will see the external IP and ports as if the external world
+22 -4
View File
@@ -514,10 +514,21 @@ void connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking
cnx->q[1].fd = fd;
if (cnx->proto->proxyprotocol_is_present) {
pp_write_header(cnx->proto->proxyprotocol, cnx);
/* If pp_write_header() fails, it already logs a message and there is
* nothing much we can do. The server side will probably close the
* connection */
if (cnx->proto->proxyprotocol) {
pp_write_header(cnx->proto->proxyprotocol, cnx);
/* If pp_write_header() fails, it already logs a message and there is
* nothing much we can do. The server side will probably close the
* connection */
} else {
/* proxyprotocol == 0 means we remove a proxyprotocol header from
* the client-side, and log PP information (as it will be lost
* after sslh) */
int pp_len = pp_log_connection(cnx->q[1].begin_deferred_data,
cnx->q[1].deferred_data_size);
if (pp_len > 0) {
defer_skip(&cnx->q[1], pp_len);
}
}
}
}
@@ -540,6 +551,13 @@ int defer_write(struct queue *q, void* data, ssize_t data_size)
return 0;
}
/* skip (remove) some data from the head of the queue */
void defer_skip(struct queue *q, ssize_t data_size)
{
q->deferred_data += data_size;
q->deferred_data_size -= data_size;
}
/* Store some data to write *before* what's already in the queue */
int defer_write_before(struct queue *q, void* data, ssize_t data_size)
{
+1
View File
@@ -184,6 +184,7 @@ int resolve_split_name(struct addrinfo **out, char* hostname, char* port);
int start_listen_sockets(struct listen_endpoint *sockfd[]);
int defer_write(struct queue *q, void* data, ssize_t data_size);
void defer_skip(struct queue *q, ssize_t data_size);
int defer_write_before(struct queue *q, void* data, ssize_t data_size);
int flush_deferred(struct queue *q);
+120
View File
@@ -0,0 +1,120 @@
Using Proxyprotocol with sslh
=============================
HAProxy defined the [Proxy protocol](https://www.haproxy.com/documentation/haproxy-configuration-tutorials/proxying-essentials/client-ip-preservation/enable-proxy-protocol/), where a proxy adds external connection information directly inside the TCP connection, for the backend server to get. This achieves the same goal as transparent proxying, while requiring no networking knowledge and no administration rights.
Instead, the protocol needs to be supported in the servers.
Many do, e.g. Apache (with the `RemoteIPProxyProtocol` setting).
`sslh` supports this protocol both on the client and the
server side.
Proxyprotocol on the server side
--------------------------------
Presumably that's the most common setup, with `sslh` being
the client-facing proxy in front of the backend server.
Support is enabled by adding the `proxyprotocol: [1|2]`
setting in the target protocol:
```
listen:
(
{ host: "localhost"; port: "443"; }
);
protocols:
(
{ name: "ssh"; host: "localhost"; port: "2222"; },
{ name: "tls"; host: "localhost"; port: "8080"; proxyprotocol: 2; }
);
);
```
Here `sslh` listens on port 443. It forwards `ssh`
connections to port 2222 without touching them (which means sshd does not see the external data) (Apparently openssh does not as of september 2025 support Proxyprotocol).
It forwards `tls` to port 8080, adding a proxy-protocol v2
header which allows Apache (configured with RemoteIPProxyProtocol) to log the external IP and port.
```
203.0.113.4:443 :2222
Client -----------------> sslh -------------> sshd
198.51.100.42:2342 | logs: 127.0.0.1:<local port>
|
| :8080
\--pp v2-------> apache
logs: 198.51.100.42:2342
```
The `proxyprotocol` setting can be set to 1 or 2, which
selects the appropriate protocol version. Version 2 being
more efficient, this is what you should use unless you have
good reasons to use version 1 (e.g. your backend server only
supports v1, an unlikely scenario).
Proxyprotocol on the client side
--------------------------------
`sslh` also supports Proxy-protocol on incoming connections.
These are so-called "client side", but you should not enable
proxy-protocol on an Internet-facing server. Instead, this
can be useful if `sslh` is located behind another proxy that
adds proxy-protocol headers, e.g. HAProxy.
This is enabled by adding `proxyprotocol: true` to a
`listen` statement:
```
listen:
(
{ host: "localhost"; port: "443"; proxyprotocol: true; }
);
protocols:
(
{ name: "ssh"; host: "localhost"; port: "2222"; proxyprotocol: 0; },
{ name: "tls"; host: "localhost"; port: "8080"; }
);
);
```
In that case, `sslh` expects to find a proxyprotocol header
on all incoming connections. It ignores the header to
perform its usual protocol detection.
If the target protocol
specified nothing, the connection is passed untouched, i.e.
the proxyprotocol header is forwarded as usual: here, Apache
on 8080 will receive the proxyprotocol header.
If the target protocol specifies `proxyprotocol: 0`, then
`sslh` removes the proxyprotocol header, and logs an
additional message with the connection information extracted
from the header. Here, `sslh` logs the external information
for ssh connections, removes the header, and forwards the
connection to sshd. `sshd` does not support proxyprotocol,
and logs a connection coming from the local IP address.
```
203.0.113.4:443 :2222
Client ---------> HaProxy ----pp v2---------> sslh -------------> sshd
198.51.100.42:2342 | logs: 127.0.0.1:<local port>
| sslh logs: 198.51.100.42:2342 to localhost:2222
|
| :8080
\--pp v2-------> apache
logs: 198.51.100.42:2342
sslh logs: local connection
```
`sslh` always logs the 'real' network information, here the
local addresses, and only logs the proxyprotocol information
when it removes the header. It is not necessary to log the
proxyprotocol information when the target server will
receive it (as it is then the target server's job to log the
external IP address).
+1 -1
View File
@@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Wed Jul 16 17:59:23 2025.
* on Fri Aug 29 20:43:29 2025.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2024 Yves Rutschle
+1 -1
View File
@@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Wed Jul 16 17:59:23 2025.
* on Fri Aug 29 20:43:29 2025.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2024 Yves Rutschle
-3
View File
@@ -465,9 +465,6 @@ int probe_buffer(char* buf, int len,
struct sslhcfg_protocols_item* p;
int i, res, again = 0;
print_message(msg_packets, "hexdump of incoming packet:\n");
hexdump(msg_packets, buf, len);
*proto_out = NULL;
for (i = 0; i < proto_len; i++) {
char* probe_str[3] = {"PROBE_NEXT", "PROBE_MATCH", "PROBE_AGAIN"};
+1
View File
@@ -6,6 +6,7 @@
#include "common.h"
#include "tls.h"
#include "log.h"
#include "proxyprotocol.h"
typedef enum {
PROBE_NEXT, /* Enough data, probe failed -- it's some other protocol */
+33
View File
@@ -133,4 +133,37 @@ int pp_write_header(int pp_version, struct connection* cnx)
return 0;
}
/* Logs the connection data in the pp header and return header length */
int pp_log_connection(char* buffer, ssize_t buffer_len)
{
pp_info_t pp_info;
int header_len = pp_parse_hdr((uint8_t*)buffer, buffer_len, &pp_info);
if (header_len > 0) {
print_message(msg_connections, "client had proxyprotocol info: %s:%d to %s:%d not forwarded to server\n",
pp_info.src_addr, pp_info.src_port,
pp_info.dst_addr, pp_info.dst_port);
} else {
print_message(msg_connections, "no proxyprotocol data found on client side, forwarding as is\n");
header_len = 0;
}
return header_len;
}
int pp_header_len(char* buffer, int buffer_len)
{
pp_info_t pp_info;
int header_len = pp_parse_hdr((uint8_t*)buffer, buffer_len, &pp_info);
print_message(msg_probe_info, "proxyprotocol header %d bytes found\n", header_len);
if (header_len < 0) header_len = 0;
return header_len;
}
#endif /* HAVE_PROXYPROTOCOL */
+6
View File
@@ -3,12 +3,18 @@
#if HAVE_PROXYPROTOCOL
int pp_write_header(int pp_version, struct connection* cnx);
int pp_log_connection(char* buffer, ssize_t buffer_len);
int pp_header_len(char* buffer, int len);
#else /* HAVE_PROXYPROTOCOL */
static inline int pp_write_header(int pp_version, struct connection* cnx) { return 0; }
static int pp_log_connection(char* buffer, ssize_t buffer_len) { return 0; }
static inline int pp_header_len(char*, int) { return 0; }
#endif /* HAVE_PROXYPROTOCOL */
+21 -2
View File
@@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Mon Aug 4 18:25:44 2025.
* on Fri Aug 29 20:43:29 2025.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2024 Yves Rutschle
@@ -809,7 +809,7 @@ static struct config_desc table_sslhcfg_protocols[] = {
},
{ 0 }
};
static struct config_desc table_sslhcfg_listen[] = {
@@ -908,6 +908,22 @@ static struct config_desc table_sslhcfg_listen[] = {
/* optional */ 0,
/* default_val*/ .default_val.def_bool = 0
},
{
/* name */ "proxyprotocol",
/* type */ CFG_BOOL,
/* sub_group*/ NULL,
/* arg_cl */ NULL,
/* base_addr */ NULL,
/* offset */ offsetof(struct sslhcfg_listen_item, proxyprotocol),
/* offset_len */ 0,
/* offset_present */ 0,
/* size */ sizeof(int),
/* array_type */ -1,
/* mandatory */ 0,
/* optional */ 0,
/* default_val*/ .default_val.def_bool = 0
},
{ 0 }
};
@@ -2513,6 +2529,9 @@ static void sslhcfg_listen_fprint(
indent(out, depth);
fprintf(out, "keepalive: %d", sslhcfg_listen->keepalive);
fprintf(out, "\n");
indent(out, depth);
fprintf(out, "proxyprotocol: %d", sslhcfg_listen->proxyprotocol);
fprintf(out, "\n");
}
void sslhcfg_fprint(
+2 -1
View File
@@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Mon Aug 4 18:25:44 2025.
* on Fri Aug 29 20:43:29 2025.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2024 Yves Rutschle
@@ -48,6 +48,7 @@ struct sslhcfg_listen_item {
int is_udp;
int is_unix;
int keepalive;
int proxyprotocol;
};
struct sslhcfg_protocols_item {
+3 -2
View File
@@ -87,7 +87,7 @@ static void printsettings(void)
}
}
print_message(msg_config,
"%s addr: %s. max_cnx: %d libwrap service: %s log_level: %d [%s] [%s] [%s]\n",
"%s addr: %s. max_cnx: %d libwrap service: %s log_level: %d [%s] [%s] [%s] pp:%d\n",
p->name,
buf,
p->max_connections,
@@ -95,7 +95,8 @@ static void printsettings(void)
p->log_level,
p->keepalive ? "keepalive" : "",
p->fork ? "fork" : "",
p->transparent ? "transparent" : ""
p->transparent ? "transparent" : "",
p->proxyprotocol_is_present ? p->proxyprotocol : -1
);
}
print_message(msg_config,
+2 -1
View File
@@ -100,7 +100,8 @@ config: {
{ name: "max_connections"; type: "int"; optional: true; },
{ name: "is_udp"; type: "bool"; default: false },
{ name: "is_unix"; type: "bool"; default: false },
{ name: "keepalive"; type: "bool"; default: false; }
{ name: "keepalive"; type: "bool"; default: false; },
{ name: "proxyprotocol"; type: "bool"; default: false; }
)
},
Executable
+415
View File
@@ -0,0 +1,415 @@
#! /usr/bin/perl -w
# Test suite for sslh
# Uses Conf::Libconfig to read sslh config file: install
# with:
# cpan Conf::Libconfig
# Usage:
# ./run # run all tests
# ./run -l # list all tests
# ./run 1 3 5 # run specified tests
use strict;
use IO::Socket::INET6;
use Test::More qw/no_plan/;
use Conf::Libconfig 1.0.3;
use Getopt::Long;
my ($coverage, $list_tests);
GetOptions(
'cover' => \$coverage,
'list' => \$list_tests,
);
use Data::Dumper;
################################################################################
# Global setup
################################################################################
my $no_listen = 8083; # Port on which no-one listens
my $user = (getpwuid $<)[0]; # Run under current username
################################################################################
# Helper functions
################################################################################
sub get_conf {
my ($filename) = @_;
my $conf = new Conf::Libconfig;
$conf->read_file($filename) or die "$filename: $!";
return $conf;
}
# We want to keep track of tests to print a report at the
# end, so we centralise all calls to Test::More::is here
my $cnt = 1; # test counter
my @results;
sub my_is {
my ($a, $b, $desc) = @_;
my $res = is($a, $b, $desc);
push @results, [$cnt++, $desc, $res];
}
sub my_like {
my ($a, $b, $desc) = @_;
my $res = like($a, $b, $desc);
push @results, [$cnt++, $desc, $res];
}
sub verbose_exec
{
my ($cmd) = @_;
warn "$cmd\n";
if (!fork) {
exec $cmd;
}
}
# Returns a 'host:port' address targetting ssh from the configuration file
sub ssh_address {
my ($conf) = @_;
my $ssh_conf = (grep { $_->{name} eq "ssh" } @{$conf->value("protocols")})[0];
return $ssh_conf->{host} . ":" . $ssh_conf->{port};
}
# Returns the last TLS target from the configuration file
sub ssl_address {
my ($conf) = @_;
my $ssl_conf = (grep { $_->{name} eq "tls" } @{$conf->value ("protocols")})[-1];
return $ssl_conf->{host} . ":" . $ssl_conf->{port};
}
# For SNI/ALPN, build a protocol name as such:
# tls:sni1,sni2,...;alpn1,alpn2,...
# input: a protocol entry from Libconfig
sub make_sni_alpn_name {
my ($prot) = @_;
return "tls:" . (join ",", @{$prot->{sni_hostnames} // []})
. ";" . (join ",", @{$prot->{alpn_protocols} // [] });
}
# Start an echoserver for each service
my ($started_echosrvs);
sub start_echosrv
{
my ($conf) = @_;
return if defined $started_echosrvs;
foreach my $s (@{$conf->value("protocols")}) {
my $prefix = $s->{name};
$prefix =~ s/^ssl/tls/;
if ($s->{sni_hostnames} or $s->{alpn_protocols}) {
$prefix = make_sni_alpn_name($s);
}
verbose_exec "../echosrv --listen $s->{host}:$s->{port} --prefix '$prefix: '";
}
$started_echosrvs = 1;
}
sub stop_echosrv
{
`killall echosrv` if $started_echosrvs;
undef $started_echosrvs;
}
# Runs sslh, restart it if configuration has changed
my ($sslh_pid, $sslh_conf);
sub start_sslh
{
my ($exe, $conf) = @_;
stop_sslh() if (defined $sslh_conf and ($conf ne $sslh_conf));
return if defined $sslh_pid;
$sslh_conf = $conf;
my ($valgrind);
if (!($sslh_pid = fork)) {
my $user = (getpwuid $<)[0]; # Run under current username
my $cmd = "../$exe -F $conf";
print "****CMDLINE [$cmd]\n";
#$valgrind = 1;
#$cmd = "valgrind --leak-check=full $cmd";
verbose_exec $cmd;
exit 0;
}
sleep 1;
warn "spawned $sslh_pid\n";
sleep 5 if $valgrind; # valgrind can be heavy -- wait 5 seconds
}
#stop sslh
sub stop_sslh
{
if (defined $sslh_pid) {
kill TERM => $sslh_pid or warn "kill process: $!\n";
`killall sslh-ev sslh-select sslh-fork`;
undef $sslh_pid;
}
}
################################################################################
# Test methods
################################################################################
# runs a test that ends in sslh exiting early
# $cmdline: which command line to run
# $test: test object
# Returns: sslh exit code
sub run_ending_test
{
my ($exe, $params, $test) = @_;
my $conf = get_conf($test->{cfg});
my $ssh_address = ssh_address($conf);
my $ssl_address = ssl_address($conf);
my $pidfile = $conf->lookup_value("pidfile");
my $sslh_pid;
if (!($sslh_pid = fork)) {
my $cmdline = "../$exe $params";
warn "eval `$cmdline`\n";
$cmdline = eval "\"$cmdline\"";
warn "running `$cmdline`\n";
exec "$cmdline";
}
warn "spawned $sslh_pid\n";
waitpid $sslh_pid, 0;
my $code = $? >> 8;
warn "exited with $code\n";
my_is($code, $test->{exit}, "$exe: $test->{desc}");
return $code;
}
# 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.
# run sslh, connect, write the test pattern, read the result,
# check it connected to the right echosrv, check the data
# was transfered ok.
sub run_test_probe
{
my ($exe, $test) = @_;
my $conf = get_conf($test->{cfg});
start_echosrv($conf);
start_sslh($exe, $test->{cfg});
my $expected = $test->{expected};
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");
warn "t: $!\n" unless $cnx;
return unless $cnx;
syswrite $cnx, $test->{data};
my $data;
my $n = sysread $cnx, $data, 1024;
$data =~ /^(.*?): /;
my $prefix = $1;
$data =~ s/$prefix: //g;
print "Received $n bytes: protocol $prefix data [$data]\n";
close $cnx;
my_is($prefix, $expected, "$exe:$test->{desc}:probe");
my $data_expected = $test->{data_expected} // "^$test->{data}\$";
$data_expected =~ s/INPUT_DATA/$test->{data}/;
my_like($data, qr/$data_expected/s, "$exe:$test->{desc}:shoveled");
}
################################################################################
# Test bodies
# Functions here each perform one test, and will be called with:
# - exe: path the executable to test
# - test: test object
################################################################################
sub test_no_host
{
my ($exe, $test) = @_;
run_ending_test(
$exe,
"-v 3 -f -u $user --listen $no_listen --ssh \$ssh_address --tls \$ssl_address -P \$pidfile",
$test,
);
}
sub test_wrong_user
{
my ($exe, $test) = @_;
run_ending_test(
$exe,
"-F $test->{cfg} -u ${user}_doesnt_exist --listen localhost:$no_listen --ssh \$ssh_address --tls \$ssl_address -P \$pidfile",
$test,
);
}
################################################################################
# Each test is decribed with an entry containing the
# following fields:
#
# * `run`: which test method to use
#
# * `expected`: protocol prompt added by the echosrv (should be the protocol name)
#
# * `data`: what is sent to sslh, as a client
#
# * `data_expected` is a regex for the expected return (which can be
# different when using Proxyprotocol). INPUT_DATA gets
# replaced with $_->{data}, i.e. data sent originally,
# before matching. Use `data` if not specified.
#
my @tests = (
{
desc => "No hostname in address",
run => \&test_no_host,
cfg => "test1.cfg",
exit => 6,
},
{
desc => "Changing to non-existant username",
run => \&test_wrong_user,
cfg => "test1.cfg",
exit => 2,
},
{
desc => "ssh probe",
run => \&run_test_probe,
cfg => "test1.cfg",
data => "SSH-2.0 Tester banner\n", # Sent to echosrv
expected => "ssh", # prefix expected from echosrv
},
{
desc => "socks5 probe",
run => \&run_test_probe,
cfg => "test1.cfg",
data => "\x05\x04\x01\x02\x03\x04",
expected => "socks5", # prefix expected from echosrv
},
# test_pp1.cfg has proxyprotocol on ssh but not socks5.
# Check ssh has pp
{
desc => "Server-side ProxyProtocol active",
run => \&run_test_probe,
cfg => "test_pp1.cfg",
data => "SSH-2.0 hello",
expected => "ssh",
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",
run => \&run_test_probe,
cfg => "test_pp1.cfg",
data => "\x05\x04\x01\x02\x03\x04",
expected => "socks5",
},
# test_pp2.cfg has proxyprotocol on port 8080
{
desc => "Client-side ProxyProtocol active",
run => \&run_test_probe,
cfg => "test_pp2.cfg",
data => "PROXY TCP4 127.0.0.1 127.0.0.1 8080 8081\x0D\x0ASSH-2.0 hello",
expected => "ssh",
data_expected => "^PROXY TCP4 127.0.0.1 127.0.0.1 \\d+ \\d+\x0D\x0ASSH-2.0 hello",
},
# test_pp2.cfg has no proxyprotocol on port 8081, so ssh
# should pass untouched
{
desc => "Client-side ProxyProtocol not active",
run => \&run_test_probe,
cfg => "test_pp2.cfg",
port => 8081,
data => "SSH-2.0 hello",
expected => "ssh",
},
# test_pp2.cfg has proxyprotocol on port 8080 but
# removes it towards "regex_remove_pp" tarbet
# TODO: Log the addresses!
{
desc => "Client-side ProxyProtocol active but removed",
run => \&run_test_probe,
cfg => "test_pp2.cfg",
data => "PROXY TCP4 127.0.0.1 127.0.0.1 8080 8081\x0D\x0Aregex_remove_pp hello",
expected => "regex",
data_expected => "^regex_remove_pp hello",
},
);
# Number tests
my $i = 1;
map { $_->{'index'} = $i++ } @tests;
# Only keep selected tests
@tests = @tests[ map { $_ - 1 } @ARGV ] if @ARGV;
################################################################################
#####List all tests
format test_list_top =
ID | Description
----+-------------------------------------------------------------------------
.
format test_list =
@>> | @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$_->{'index'}, $_->{'desc'};
.
if ($list_tests) {
format_name STDOUT "test_list";
format_top_name STDOUT "test_list_top";
map { write; } @tests;
exit 0;
}
#####/list
################################################################################
# Run selected tests
foreach my $test (@tests) {
warn "Running test: $test->{desc}\n";
my $binary = 'sslh-ev';
my $code = ($test->{run})->($binary, $test);
}
stop_echosrv();
stop_sslh();
done_testing();
format test_results_top =
ID | Description | Status
----+-------------------------------------------------------------------+-------
.
format test_results =
@>> | @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | @>>
$_->[0], $_->[1], $_->[2] ? "OK" : "NOK"
.
format_name STDOUT "test_results";
format_top_name STDOUT "test_results_top";
map { write; } @results;
+79
View File
@@ -0,0 +1,79 @@
# Configuration file for testing (use both by sslh under
# test and the test script `t`)
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: 0; # 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: "localhost"; port: "8080"; keepalive: true; proxyprotocol: true; },
{ host: "localhost"; port: "8081"; keepalive: true; },
{ host: "ip4-localhost"; is_udp: true; port: "8086"; },
{ host: "/tmp/sslh.sock"; is_unix: true; port: ""; }
);
# Tester beware: when using fork, the forked process loses
# track of buffers of other, concurrent connections. Memory
# leak tools thus complain each time a forked process stops.
protocols:
(
{ name: "ssh"; host: "localhost"; port: "9000"; fork: true; transparent: true; resolve_on_forward: true; },
{ name: "socks5"; host: "localhost"; port: "9001"; },
{ name: "http"; host: "localhost"; port: "80"; },
{ name: "tinc"; host: "localhost"; port: "9003"; },
{ name: "openvpn"; host: "localhost"; port: "9004"; },
{ name: "xmpp"; host: "localhost"; port: "9009"; },
{ name: "adb"; host: "localhost"; port: "9010"; },
{ name: "syslog"; host: "localhost"; port: "9013"; },
{ name: "regex"; host: "ip4-localhost"; is_udp: true; port: "9020";
udp_timeout: 30;
regex_patterns: [ "^foo" ];
resolve_on_forward: true;
},
{ name: "regex"; host: "localhost"; port: "9011";
regex_patterns: [ "^foo", "^bar" ];
minlength: 4;
test_patterns: ( # this is used by the test script, not by sslh
{ pattern: "foo"; result: "ssh"; }, # After timeout
{ pattern: "fooo"; result: "regex"; },
{ pattern: "bar"; result: "ssh"; },
{ pattern: "barr"; result: "regex"; },
{ pattern: "barrrr"; result: "regex"; }
);
},
{ name: "tls"; host: "localhost"; port: "9021"; alpn_protocols: [ "alpn1", "alpn2" ]; sni_hostnames: [ "sni1" ]; },
{ name: "tls"; host: "localhost"; port: "9022"; alpn_protocols: [ "alpn1", "alpn2" ]; sni_hostnames: [ "sni2", "sni3" ]; },
{ name: "tls"; host: "localhost"; port: "9023"; alpn_protocols: [ "alpn3" ]; },
{ name: "tls"; host: "localhost"; port: "9024"; sni_hostnames: [ "sni3" ]; },
{ name: "tls"; host: "localhost"; port: "9025"; },
{ name: "anyprot"; host: "localhost"; port: "9099"; }
);
on_timeout: "ssh";
+44
View File
@@ -0,0 +1,44 @@
# Testing for server-side ProxyProtocol
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: 0; # 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: "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"; }
);
on_timeout: "ssh";
+47
View File
@@ -0,0 +1,47 @@
# Testing for client-side ProxyProtocol
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: 0; # 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: "localhost"; port: "8080"; keepalive: true; proxyprotocol: true; },
{ host: "localhost"; port: "8081"; keepalive: true; proxyprotocol: false; }
);
protocols:
(
{ name: "ssh"; host: "localhost"; port: "9000"; },
{ name: "socks5"; host: "localhost"; port: "9001"; },
{ name: "regex"; host: "localhost"; port: "9002";
regex_patterns: [ "^regex_remove_pp" ]; proxyprotocol: 0; },
{ name: "anyprot"; host: "localhost"; port: "9099"; }
);
on_timeout: "ssh";
View File
File renamed without changes.
+13 -2
View File
@@ -45,8 +45,19 @@ int probe_client_protocol(struct connection *cnx)
if (n > 0) {
defer_write(&cnx->q[1], buffer, n);
return probe_buffer(cnx->q[1].begin_deferred_data,
cnx->q[1].deferred_data_size,
print_message(msg_packets, "hexdump of incoming packet:\n");
hexdump(msg_packets, cnx->q[1].begin_deferred_data, cnx->q[1].deferred_data_size);
size_t pp_len = 0;
if (cnx->endpoint->endpoint_cfg->proxyprotocol) {
pp_len = pp_header_len(cnx->q[1].begin_deferred_data,
cnx->q[1].deferred_data_size);
}
return probe_buffer(cnx->q[1].begin_deferred_data + pp_len,
cnx->q[1].deferred_data_size - pp_len,
tcp_protocols, tcp_protocols_len,
&cnx->proto
);
+4 -3
View File
@@ -21,7 +21,7 @@ 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: 0; # what's happening during the probe process
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
@@ -30,7 +30,8 @@ verbose-int-error: 1; # internal errors, the kind that should never happen
# Options:
listen:
(
{ host: "localhost"; port: "8080"; keepalive: true; max_connections: 2; },
{ host: "localhost"; port: "8080"; keepalive: true;
max_connections: 2; proxyprotocol: true; },
{ host: "localhost"; port: "8081"; keepalive: true; },
# { host: "ip4-localhost"; is_udp: true; port: "8086"; },
{ host: "/tmp/sslh.sock"; is_unix: true; port: ""; }
@@ -45,7 +46,7 @@ protocols:
(
{ name: "ssh"; host: "localhost"; port: "9000"; fork: true; transparent: true; resolve_on_forward: true; max_connections: 4; },
{ name: "socks5"; host: "localhost"; port: "9001"; },
{ name: "http"; host: "localhost"; port: "80"; proxyprotocol: 2; },
{ name: "http"; host: "localhost"; port: "80"; },
{ name: "tinc"; host: "localhost"; port: "9003"; },
{ name: "openvpn"; host: "localhost"; port: "9004"; },
{ name: "xmpp"; host: "localhost"; port: "9009"; },
+5
View File
@@ -0,0 +1,5 @@
#ifndef VERSION_H
#define VERSION_H
#define VERSION "v2.2.4-52-geea50a9-dirty"
#endif