mirror of
https://github.com/yrutschle/sslh.git
synced 2026-10-25 01:06:20 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c02e2d7aee | ||
|
|
59c9be54ad | ||
|
|
e3159409c0 | ||
|
|
536f7dee83 | ||
|
|
2781c75ff9 | ||
|
|
d02ffcd154 |
No files matched your search
@@ -1,3 +1,18 @@
|
||||
v1.15: 27JUL2013
|
||||
Added --transparent option for transparent proxying.
|
||||
See README for iptables magic and capability
|
||||
management.
|
||||
|
||||
Fixed bug in sslh-select: if number of opened file
|
||||
descriptor became bigger than FD_SETSIZE, bad things
|
||||
would happen.
|
||||
|
||||
Fixed bug in sslh-select: if socket dropped while
|
||||
defered_data was present, sslh-select would crash.
|
||||
|
||||
Increased FD_SETSIZE for Cygwin, as the default 64
|
||||
is too low for even moderate load.
|
||||
|
||||
v1.14: 21DEC2012
|
||||
Corrected OpenVPN probe to support pre-shared secret
|
||||
mode (OpenVPN port-sharing code is... wrong). Thanks
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Configuration
|
||||
|
||||
VERSION="1.14"
|
||||
VERSION="1.15"
|
||||
USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files)
|
||||
USELIBWRAP= # Use libwrap?
|
||||
COV_TEST= # Perform test coverage?
|
||||
@@ -56,9 +56,7 @@ $(MAN): sslh.pod Makefile
|
||||
# Create release: export clean tree and tag current
|
||||
# configuration
|
||||
release:
|
||||
svn export . /tmp/sslh-$(VERSION)
|
||||
( cd /tmp; tar zcvf /tmp/sslh-$(VERSION).tar.gz sslh-$(VERSION) )
|
||||
( cd .. ; svn copy trunk tags/sslh-$(VERSION) ; cd tags/sslh-$(VERSION) ; make clean )
|
||||
git archive master --prefix="sslh-$(VERSION)/" | gzip > /tmp/sslh-$(VERSION).tar.gz
|
||||
|
||||
# generic install: install binary and man page
|
||||
install: sslh $(MAN)
|
||||
|
||||
@@ -157,31 +157,65 @@ in inetd mode.
|
||||
sslh options: -i for inetd mode, --http to forward http
|
||||
connexions to port 80, and SSH connexions to port 22.
|
||||
|
||||
==== IP_TPROXY support ====
|
||||
==== capapbilities support ====
|
||||
|
||||
There is a netfilter patch that adds an option to the Linux
|
||||
TCP/IP stack to allow a program to set the source address
|
||||
of an IP packet that it sends. This could let sslh set the
|
||||
address of packets to that of the actual client, so that
|
||||
sshd would see and log the IP address of the client, making
|
||||
sslh transparent.
|
||||
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).
|
||||
|
||||
This is not, and won't be, implemented in sslh for the
|
||||
following reasons (in increasing order of importance):
|
||||
The simplest way to use capabilities is to give them to the
|
||||
executable as root:
|
||||
|
||||
* It's not vital: the real connecting IP address can be
|
||||
found in logs. Little gain.
|
||||
* It's Linux only: it means increased complexity for no
|
||||
gain to some users.
|
||||
* It's a patch: it means it'd only be useful to Linux
|
||||
users who compile their own kernel.
|
||||
* Only root can use the feature: that's a definite no-no.
|
||||
Sslh should not, must not, will never run as root.
|
||||
# setcap cap_net_bind_service,cap_net_admin+pe sslh-select
|
||||
|
||||
This isn't to mean that it won't eventually get implemented,
|
||||
when/if the feature finds its way into the main kernel and
|
||||
it becomes usuable by non-root processes.
|
||||
Then you can run sslh-select as an unpriviledged user, e.g.:
|
||||
|
||||
$ 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.
|
||||
configuring the interface. If you're not going to use
|
||||
transparent proxying, just don't use it.
|
||||
|
||||
==== 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 (see appropriate chapter)
|
||||
or run it as root (but don't do that).
|
||||
|
||||
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? ====
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ int probing_timeout = 2;
|
||||
int inetd = 0;
|
||||
int foreground = 0;
|
||||
int background = 0;
|
||||
int transparent = 0;
|
||||
int numeric = 0;
|
||||
const char *user_name, *pid_file;
|
||||
|
||||
@@ -96,9 +97,35 @@ int start_listen_sockets(int *sockfd[], struct addrinfo *addr_list)
|
||||
return num_addr;
|
||||
}
|
||||
|
||||
/* Transparent proxying: bind the peer address of fd to the peer address of
|
||||
* fd_from */
|
||||
#define IP_TRANSPARENT 19
|
||||
int bind_peer(int fd, int fd_from)
|
||||
{
|
||||
struct addrinfo from;
|
||||
struct sockaddr_storage ss;
|
||||
int res, trans = 1;
|
||||
|
||||
memset(&from, 0, sizeof(from));
|
||||
from.ai_addr = (struct sockaddr*)&ss;
|
||||
from.ai_addrlen = sizeof(ss);
|
||||
|
||||
res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen);
|
||||
CHECK_RES_DIE(res, "getpeername");
|
||||
res = setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &trans, sizeof(trans));
|
||||
CHECK_RES_DIE(res, "setsockopt");
|
||||
res = bind(fd, from.ai_addr, from.ai_addrlen);
|
||||
CHECK_RES_RETURN(res, "bind");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Connect to first address that works and returns a file descriptor, or -1 if
|
||||
* none work. cnx_name points to the name of the service (for logging) */
|
||||
int connect_addr(struct addrinfo *addr, const char* cnx_name)
|
||||
* none work.
|
||||
* If transparent proxying is on, use fd_from peer address on external address
|
||||
* of new file descriptor.
|
||||
* cnx_name points to the name of the service (for logging) */
|
||||
int connect_addr(struct addrinfo *addr, int fd_from, const char* cnx_name)
|
||||
{
|
||||
struct addrinfo *a;
|
||||
char buf[NI_MAXHOST];
|
||||
@@ -113,6 +140,8 @@ int connect_addr(struct addrinfo *addr, const char* cnx_name)
|
||||
if (fd == -1) {
|
||||
log_message(LOG_ERR, "forward to %s failed:socket: %s\n", cnx_name, strerror(errno));
|
||||
} else {
|
||||
if (transparent)
|
||||
bind_peer(fd, fd_from);
|
||||
res = connect(fd, a->ai_addr, a->ai_addrlen);
|
||||
if (res == -1) {
|
||||
log_message(LOG_ERR, "forward to %s failed:connect: %s\n",
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
#ifndef __COMMON_H_
|
||||
#define __COMMON_H_
|
||||
|
||||
/* FD_SETSIZE is 64 on Cygwin, which is really low. Just redefining it is
|
||||
* enough for the macros to adapt (http://support.microsoft.com/kb/111855)
|
||||
*/
|
||||
#ifdef __CYGWIN__
|
||||
#define FD_SETSIZE 4096
|
||||
#endif
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/types.h>
|
||||
@@ -34,7 +40,7 @@
|
||||
|
||||
#define CHECK_RES_RETURN(res, str) \
|
||||
if (res == -1) { \
|
||||
log_message(LOG_CRIT, "%s: %d\n", str, errno); \
|
||||
log_message(LOG_CRIT, "%s:%d:%s\n", str, errno, strerror(errno)); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
@@ -80,7 +86,7 @@ struct connection {
|
||||
|
||||
/* common.c */
|
||||
void init_cnx(struct connection *cnx);
|
||||
int connect_addr(struct addrinfo *addr, const char* cnx_name);
|
||||
int connect_addr(struct addrinfo *addr, int fd_from, const char* cnx_name);
|
||||
int fd2fd(struct queue *target, struct queue *from);
|
||||
char* sprintaddr(char* buf, size_t size, struct addrinfo *a);
|
||||
void resolve_name(struct addrinfo **out, char* fullname);
|
||||
@@ -100,7 +106,8 @@ int start_listen_sockets(int *sockfd[], struct addrinfo *addr_list);
|
||||
int defer_write(struct queue *q, void* data, int data_size);
|
||||
int flush_defered(struct queue *q);
|
||||
|
||||
extern int probing_timeout, verbose, inetd, foreground, background, numeric;
|
||||
extern int probing_timeout, verbose, inetd, foreground,
|
||||
background, transparent, numeric;
|
||||
extern struct sockaddr_storage addr_ssl, addr_ssh, addr_openvpn;
|
||||
extern struct addrinfo *addr_listen;
|
||||
extern const char* USAGE_STRING;
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ void start_shoveler(int in_socket)
|
||||
}
|
||||
|
||||
/* Connect the target socket */
|
||||
out_socket = connect_addr(saddr, prot->description);
|
||||
out_socket = connect_addr(saddr, in_socket, prot->description);
|
||||
CHECK_RES_DIE(out_socket, "connect");
|
||||
|
||||
cnx.q[1].fd = out_socket;
|
||||
|
||||
@@ -58,6 +58,7 @@ static struct option const_options[] = {
|
||||
{ "inetd", no_argument, &inetd, 1 },
|
||||
{ "foreground", no_argument, &foreground, 1 },
|
||||
{ "background", no_argument, &background, 1 },
|
||||
{ "transparent", no_argument, &transparent, 1 },
|
||||
{ "numeric", no_argument, &numeric, 1 },
|
||||
{ "verbose", no_argument, &verbose, 1 },
|
||||
{ "user", required_argument, 0, 'u' },
|
||||
|
||||
+22
-8
@@ -72,6 +72,16 @@ int tidy_connection(struct connection *cnx, fd_set *fds, fd_set *fds2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if fd becomes higher than FD_SETSIZE, things won't work so well with FD_SET
|
||||
* and FD_CLR. Need to drop connections if we go above that limit */
|
||||
int fd_is_in_range(int fd) {
|
||||
if (fd >= FD_SETSIZE) {
|
||||
log_message(LOG_ERR, "too many open file descriptor to monitor them all -- dropping connection\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Accepts a connection from the main socket and assigns it to an empty slot.
|
||||
* If no slots are available, allocate another few. If that fails, drop the
|
||||
* connexion */
|
||||
@@ -83,6 +93,9 @@ int accept_new_connection(int listen_socket, struct connection *cnx[], int* cnx_
|
||||
in_socket = accept(listen_socket, 0, 0);
|
||||
CHECK_RES_RETURN(in_socket, "accept");
|
||||
|
||||
if (!fd_is_in_range(in_socket))
|
||||
return -1;
|
||||
|
||||
res = set_nonblock(in_socket);
|
||||
if (res == -1) return -1;
|
||||
|
||||
@@ -122,8 +135,8 @@ int connect_queue(struct connection *cnx, struct addrinfo *addr,
|
||||
{
|
||||
struct queue *q = &cnx->q[1];
|
||||
|
||||
q->fd = connect_addr(addr, cnx_name);
|
||||
if (q->fd != -1) {
|
||||
q->fd = connect_addr(addr, cnx->q[0].fd, cnx_name);
|
||||
if ((q->fd != -1) && fd_is_in_range(q->fd)) {
|
||||
log_connection(cnx);
|
||||
set_nonblock(q->fd);
|
||||
flush_defered(q);
|
||||
@@ -261,12 +274,13 @@ void main_loop(int listen_sockets[], int num_addr_listen)
|
||||
tidy_connection(&cnx[i], &fds_r, &fds_w);
|
||||
if (verbose)
|
||||
fprintf(stderr, "closed slot %d\n", i);
|
||||
}
|
||||
/* If no defered data is left, stop monitoring the fd
|
||||
* for write, and restart monitoring the other one for reads*/
|
||||
if (!cnx[i].q[j].defered_data_size) {
|
||||
FD_CLR(cnx[i].q[j].fd, &fds_w);
|
||||
FD_SET(cnx[i].q[1-j].fd, &fds_r);
|
||||
} else {
|
||||
/* If no defered data is left, stop monitoring the fd
|
||||
* for write, and restart monitoring the other one for reads*/
|
||||
if (!cnx[i].q[j].defered_data_size) {
|
||||
FD_CLR(cnx[i].q[j].fd, &fds_w);
|
||||
FD_SET(cnx[i].q[1-j].fd, &fds_r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user