mirror of
https://github.com/yrutschle/sslh.git
synced 2026-10-22 18:12:10 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44f02ddf39 | ||
|
|
0658982705 | ||
|
|
b965d735b8 | ||
|
|
3386a64a4d |
No files matched your search
@@ -0,0 +1,65 @@
|
||||
v1.7: 01FEB2010
|
||||
Added CentOS init.d script (Andre Krajnik).
|
||||
|
||||
Fixed default ssl address inconsistancy, now
|
||||
defaults to "localhost:443" and fixed documentation
|
||||
accordingly (pointed by Markus Schalke).
|
||||
|
||||
Children no longer bind to the listen socket, so
|
||||
parent server can be stopped without killing an
|
||||
active child (pointed by Matthias Buecher).
|
||||
|
||||
Inetd support (Dima Barsky).
|
||||
|
||||
v1.6: 25APR2009
|
||||
Added -V, version option.
|
||||
|
||||
Install target directory configurable in Makefile
|
||||
|
||||
Changed syslog prefix in auth.log to "sslh[%pid]"
|
||||
|
||||
Man page
|
||||
|
||||
new 'make install' and 'make install-debian' targets
|
||||
|
||||
PID file now specified using -P command line option
|
||||
|
||||
Actually fixed zombie generation (the v1.5 patch got
|
||||
lost, doh!)
|
||||
|
||||
|
||||
v1.5: 10DEC2008
|
||||
Fixed zombie generation.
|
||||
|
||||
Added support scripts (), Makefile.
|
||||
|
||||
Changed all 'connexions' to 'connections' to please
|
||||
pesky users. Damn users.
|
||||
|
||||
v1.4: 13JUL2008
|
||||
Added libwrap support for ssh service (Christian Weinberger)
|
||||
Only SSH is libwraped, not SSL.
|
||||
|
||||
v1.3: 14MAY2008
|
||||
Added parsing for local interface to listen on
|
||||
|
||||
Changed default SSL connection to port 442 (443 doesn't make
|
||||
sense as a default as we're already listening on 443)
|
||||
|
||||
Syslog incoming connections
|
||||
|
||||
v1.2: 12MAY2008
|
||||
Fixed compilation warning for AMD64 (Thx Daniel Lange)
|
||||
|
||||
v1.1: 21MAY2007
|
||||
Making sslhc more like a real daemon:
|
||||
* If $PIDFILE is defined, write first PID to it upon startup
|
||||
* Fork at startup (detach from terminal)
|
||||
(thanks to http://www.enderunix.org/docs/eng/daemon.php -- good checklist)
|
||||
* Less memory usage (?)
|
||||
|
||||
v1.0:
|
||||
Basic functionality: privilege dropping, target hostnames and ports
|
||||
configurable.
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Configuration
|
||||
|
||||
VERSION="v1.7a"
|
||||
USELIBWRAP=1 # Use libwrap?
|
||||
PREFIX=/usr/local
|
||||
|
||||
MAN=sslh.8.gz # man page name
|
||||
|
||||
# End of configuration -- the rest should take care of
|
||||
# itself
|
||||
|
||||
CC = gcc
|
||||
CFLAGS=-Wall
|
||||
|
||||
#LIBS=-lnet
|
||||
LIBS=
|
||||
|
||||
ifneq ($(strip $(USELIBWRAP)),)
|
||||
LIBS:=$(LIBS) -lwrap
|
||||
CFLAGS:=$(CFLAGS) -DLIBWRAP
|
||||
endif
|
||||
|
||||
all: sslh $(MAN)
|
||||
|
||||
sslh: sslh.c Makefile
|
||||
$(CC) $(CFLAGS) -D'VERSION=$(VERSION)' -o sslh sslh.c $(LIBS)
|
||||
strip sslh
|
||||
|
||||
$(MAN): sslh.pod Makefile
|
||||
pod2man --section=8 --release=$(VERSION) --center=" " sslh.pod | gzip -9 - > $(MAN)
|
||||
|
||||
# generic install: install binary and man page
|
||||
install: sslh $(MAN)
|
||||
install -D sslh $(PREFIX)/sbin/sslh
|
||||
install -D -m 0644 $(MAN) $(PREFIX)/share/man/man8/$(MAN)
|
||||
|
||||
# "extended" install for Debian: install startup script
|
||||
install-debian: install sslh $(MAN)
|
||||
sed -e "s+^PREFIX=+PREFIX=$(PREFIX)+" scripts/etc.init.d.sslh > /etc/init.d/sslh
|
||||
chmod 755 /etc/init.d/sslh
|
||||
cp scripts/etc.default.sslh /etc/default/sslh
|
||||
update-rc.d sslh defaults
|
||||
|
||||
uninstall:
|
||||
rm -f $(PREFIX)/sbin/sslh $(PREFIX)/share/man/man8/$(MAN) /etc/init.d/sslh /etc/default/sslh
|
||||
update-rc.d sslh remove
|
||||
|
||||
clean:
|
||||
rm -f sslh $(MAN)
|
||||
@@ -0,0 +1,115 @@
|
||||
===== sslh -- A ssl/ssh multiplexer. =====
|
||||
|
||||
sslh lets one accept both HTTPS and SSH connections on the
|
||||
same port. It makes it possible to connect to an SSH server
|
||||
on port 443 (e.g. from inside a corporate firewall) while
|
||||
still serving HTTPS on that port.
|
||||
|
||||
==== Compile and install ====
|
||||
|
||||
If you're lucky, the Makefile will work for you:
|
||||
|
||||
make install
|
||||
|
||||
(see below for configuration hints)
|
||||
|
||||
|
||||
Otherwise:
|
||||
|
||||
Compilation instructions (the binary produced won't contain
|
||||
the version number, which is stored only in the Makefile)
|
||||
|
||||
Solaris:
|
||||
cc -o sslh sslh.c -lresolv -lsocket -lnsl
|
||||
|
||||
LynxOS:
|
||||
gcc -o tcproxy tcproxy.c -lnetinet
|
||||
|
||||
Linux:
|
||||
cc -o sslh sslh.c -lnet
|
||||
or:
|
||||
cc -o sslh sslh.c
|
||||
|
||||
To compile with libwrap support:
|
||||
cc -o sslh -DLIBWRAP sslh.c -lwrap
|
||||
|
||||
To install:
|
||||
|
||||
make
|
||||
cp sslh /usr/local/sbin
|
||||
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 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.
|
||||
|
||||
==== IP_TPROXY 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.
|
||||
|
||||
This is not, and won't be, implemented in sslh for the
|
||||
following reasons (in increasing order of importance):
|
||||
|
||||
* 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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Comments? questions? sslh@rutschle.net
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
LISTEN=ifname:443
|
||||
SSH=localhost:22
|
||||
SSL=localhost:443
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#! /bin/sh
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: sslh
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 1
|
||||
# Short-Description: sslh proxy ssl & ssh connections
|
||||
### END INIT INFO
|
||||
|
||||
set -e
|
||||
tag=sslh
|
||||
facility=user.info
|
||||
|
||||
# /etc/init.d/sslh: start and stop the sslh proxy daemon
|
||||
|
||||
# Defaults -- can be overridden in /etc/default/sslh
|
||||
LISTEN=thelonious:443
|
||||
SSH=localhost:22
|
||||
SSL=localhost:443
|
||||
|
||||
if test -f /etc/default/sslh; then
|
||||
. /etc/default/sslh
|
||||
fi
|
||||
|
||||
# The prefix is normally filled by make install. If
|
||||
# installing by hand, fill it in yourself!
|
||||
PREFIX=
|
||||
DAEMON=$PREFIX/sbin/sslh
|
||||
|
||||
start()
|
||||
{
|
||||
echo "Start services: sslh"
|
||||
$DAEMON -u nobody -p ${LISTEN} -s ${SSH} -l ${SSL}
|
||||
logger -t ${tag} -p ${facility} -i 'Started sslh'
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
echo "Stop services: sslh"
|
||||
killall $DAEMON
|
||||
logger -t ${tag} -p ${facility} -i 'Stopped sslh'
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
sleep 5
|
||||
start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: /etc/init.d/sslh {start|stop|restart}" >&2
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# /etc/rc.d/init.d/sslh
|
||||
# sslh This shell script takes care of starting and stopping
|
||||
# sslh - a daemon switching incoming connection between SSH and SSL/HTTPS servers
|
||||
#
|
||||
# Author: Andre Krajnik akrajnik@gmail.com
|
||||
#
|
||||
# chkconfig: 2345 13 87
|
||||
# description: sslh - a daemon switching incoming connection between SSH and SSL/HTTPS servers
|
||||
|
||||
# Source function library.
|
||||
. /etc/init.d/functions
|
||||
|
||||
# ./sslh -p 0.0.0.0:8443 -l 127.0.0.1:443 -s 127.0.0.1:22
|
||||
|
||||
SSLH='/usr/local/sbin/sslh'
|
||||
PIDFILE='/var/run/sslh'
|
||||
|
||||
OPTIONS='-p 0.0.0.0:8443 -l 127.0.0.1:443 -s 127.0.0.1:22 -P $PIDFILE'
|
||||
|
||||
if [ -f /etc/sysconfig/sslh ]; then
|
||||
. /etc/sysconfig/sslh
|
||||
fi
|
||||
|
||||
|
||||
start() {
|
||||
echo -n "Starting SSL-SSH-Switch: "
|
||||
if [ -f $PIDFILE ]; then
|
||||
PID=`cat $PIDFILE`
|
||||
echo sslh already running: $PID
|
||||
exit 2;
|
||||
elif [ -f $PIDFILE ]; then
|
||||
PID=`cat $PIDFILE`
|
||||
echo sslh already running: $PID
|
||||
exit 2;
|
||||
else
|
||||
cd $SLAPD_DIR
|
||||
daemon $SSLH $OPTIONS
|
||||
RETVAL=$?
|
||||
echo
|
||||
[ $RETVAL -eq 0 ] && touch $PIDFILE
|
||||
return $RETVAL
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
stop() {
|
||||
echo -n "Shutting down SSL-SSH-Switch: "
|
||||
echo
|
||||
killproc sslh
|
||||
echo
|
||||
rm -f $PIDFILE
|
||||
return 0
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
status)
|
||||
status sslh
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: {start|stop|status|restart}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit $?
|
||||
|
||||
@@ -18,36 +18,9 @@
|
||||
# The full text for the General Public License is here:
|
||||
# http://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Comments? questions? sslh@rutschle.net
|
||||
|
||||
Compilation instructions:
|
||||
|
||||
Solaris:
|
||||
cc -o sslh sslh.c -lresolv -lsocket -lnsl
|
||||
|
||||
LynxOS:
|
||||
gcc -o tcproxy tcproxy.c -lnetinet
|
||||
|
||||
HISTORY
|
||||
|
||||
v1.2: 12MAY2008
|
||||
Fixed compilation warning for AMD64 (Thx Daniel Lange)
|
||||
|
||||
v1.1: 21MAY2007
|
||||
Making sslhc more like a real daemon:
|
||||
* If $PIDFILE is defined, write first PID to it upon startup
|
||||
* Fork at startup (detach from terminal)
|
||||
(thanks to http://www.enderunix.org/docs/eng/daemon.php -- good checklist)
|
||||
* Less memory usage (?)
|
||||
|
||||
v1.0:
|
||||
* Basic functionality: privilege dropping, target hostnames and ports
|
||||
configurable.
|
||||
|
||||
*/
|
||||
|
||||
#define VERSION "1.2"
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
@@ -61,6 +34,17 @@ v1.0:
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <pwd.h>
|
||||
#include <syslog.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#ifdef LIBWRAP
|
||||
#include <tcpd.h>
|
||||
int allow_severity =0, deny_severity = 0;
|
||||
#endif
|
||||
|
||||
#ifndef VERSION
|
||||
#define VERSION "v?"
|
||||
#endif
|
||||
|
||||
#define CHECK_RES_DIE(res, str) \
|
||||
if (res == -1) { \
|
||||
@@ -69,19 +53,27 @@ if (res == -1) { \
|
||||
}
|
||||
|
||||
#define USAGE_STRING \
|
||||
"sslh v" VERSION "\n" \
|
||||
"sslh " VERSION "\n" \
|
||||
"usage:\n" \
|
||||
"\texport PIDFILE=/var/run/sslhc.pid\n" \
|
||||
"\tsslh [-t <timeout>] -u <username> -p <listenport> -s [sshhost:]port -l [sslhost:]port [-v]\n"
|
||||
"\tsslh [-t <timeout>] -u <username> -p [listenaddr:]<listenport> \n" \
|
||||
"\t\t-s [sshhost:]port -l [sslhost:]port [-P pidfile] [-v] [-i] [-V]\n\n" \
|
||||
"-v: verbose\n" \
|
||||
"-V: version\n" \
|
||||
"-p: address and port to listen on. default: 0.0.0.0:443\n" \
|
||||
"-s: SSH address: where to connect an SSH connection. default: localhost:22\n" \
|
||||
"-l: SSL address: where to connect an SSL connection.\n" \
|
||||
"-P: PID file. Default: /var/run/sslh.pid.\n" \
|
||||
"-i: Run as a inetd service.\n" \
|
||||
""
|
||||
|
||||
int verbose = 0; /* That's really quite global */
|
||||
|
||||
/* Starts a listening socket on specified port.
|
||||
/* Starts a listening socket on specified address.
|
||||
Returns file descriptor
|
||||
*/
|
||||
int start_listen_socket(int port)
|
||||
int start_listen_socket(struct sockaddr *addr)
|
||||
{
|
||||
struct sockaddr_in saddr;
|
||||
struct sockaddr_in *saddr = (struct sockaddr_in*)addr;
|
||||
int sockfd, res, reuse;
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
@@ -91,10 +83,7 @@ int start_listen_socket(int port)
|
||||
res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));
|
||||
CHECK_RES_DIE(res, "setsockopt");
|
||||
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
saddr.sin_port = htons(port);
|
||||
|
||||
res = bind (sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
|
||||
res = bind (sockfd, (struct sockaddr*)saddr, sizeof(*saddr));
|
||||
CHECK_RES_DIE(res, "bind");
|
||||
|
||||
res = listen (sockfd, 5);
|
||||
@@ -178,10 +167,8 @@ char* sprintaddr(char* buf, size_t size, struct sockaddr* s)
|
||||
/* turns a "hostname:port" string into a struct sockaddr;
|
||||
sock: socket address to which to copy the addr
|
||||
fullname: input string -- it gets clobbered
|
||||
serv: default service/port
|
||||
(defaults don't work yet)
|
||||
*/
|
||||
void resolve_name(struct sockaddr *sock, char* fullname, int port) {
|
||||
void resolve_name(struct sockaddr *sock, char* fullname) {
|
||||
struct addrinfo *addr, hint;
|
||||
char *serv, *host;
|
||||
int res;
|
||||
@@ -191,7 +178,7 @@ void resolve_name(struct sockaddr *sock, char* fullname, int port) {
|
||||
if (!sep) /* No separator: parameter is just a port */
|
||||
{
|
||||
serv = fullname;
|
||||
fprintf(stderr, "names must be fully specified as hostname:port for the moment\n");
|
||||
fprintf(stderr, "names must be fully specified as hostname:port\n");
|
||||
exit(1);
|
||||
}
|
||||
else {
|
||||
@@ -206,7 +193,7 @@ void resolve_name(struct sockaddr *sock, char* fullname, int port) {
|
||||
|
||||
res = getaddrinfo(host, serv, &hint, &addr);
|
||||
if (res) {
|
||||
fprintf(stderr, "%s\n", gai_strerror(res));
|
||||
fprintf(stderr, "%s `%s'\n", gai_strerror(res), fullname);
|
||||
if (res == EAI_SERVICE)
|
||||
fprintf(stderr, "(Check you have specified all ports)\n");
|
||||
exit(1);
|
||||
@@ -215,15 +202,61 @@ void resolve_name(struct sockaddr *sock, char* fullname, int port) {
|
||||
memcpy(sock, addr->ai_addr, sizeof(*sock));
|
||||
}
|
||||
|
||||
/* syslogs who connected to where */
|
||||
void log_connection(int socket, char* target)
|
||||
{
|
||||
struct sockaddr peeraddr;
|
||||
socklen_t size = sizeof(peeraddr);
|
||||
char buf[64];
|
||||
int res;
|
||||
|
||||
res = getpeername(socket, &peeraddr, &size);
|
||||
CHECK_RES_DIE(res, "getpeername");
|
||||
|
||||
syslog(LOG_INFO, "connection from %s forwarded to %s\n",
|
||||
sprintaddr(buf, sizeof(buf), &peeraddr), target);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Settings that depend on the command line. That's less global than verbose * :-)
|
||||
* They're set in main(), but also used in start_shoveler(), and it'd be heavy-handed
|
||||
* to pass it all as parameters
|
||||
*/
|
||||
int timeout = 2;
|
||||
int listen_port = 443;
|
||||
struct sockaddr addr_listen;
|
||||
struct sockaddr addr_ssl, addr_ssh;
|
||||
|
||||
/* libwrap (tcpd): check the ssh connection is legal. This is necessary because
|
||||
* the actual sshd will only see a connection coming from localhost and can't
|
||||
* apply the rules itself.
|
||||
*/
|
||||
void check_access_rights(int in_socket)
|
||||
{
|
||||
#ifdef LIBWRAP
|
||||
struct sockaddr peeraddr;
|
||||
socklen_t size = sizeof(peeraddr);
|
||||
char addr_str[1024];
|
||||
struct hostent *host;
|
||||
struct in_addr addr;
|
||||
int res;
|
||||
|
||||
res = getpeername(in_socket, &peeraddr, &size);
|
||||
CHECK_RES_DIE(res, "getpeername");
|
||||
inet_ntop(AF_INET, &((struct sockaddr_in*)&peeraddr)->sin_addr, addr_str, sizeof(addr_str));
|
||||
|
||||
addr.s_addr = inet_addr(addr_str);
|
||||
host = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
|
||||
|
||||
if (!hosts_ctl("sshd", (host ? host->h_name : STRING_UNKNOWN), addr_str, STRING_UNKNOWN)) {
|
||||
if (verbose)
|
||||
fprintf(stderr, "access denied\n");
|
||||
log_connection(in_socket, "access denied");
|
||||
close(in_socket);
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Child process that finds out what to connect to and proxies
|
||||
*/
|
||||
@@ -234,6 +267,7 @@ void start_shoveler(int in_socket)
|
||||
struct sockaddr *saddr;
|
||||
int res;
|
||||
int out_socket;
|
||||
char *target;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(in_socket, &fds);
|
||||
@@ -247,15 +281,18 @@ void start_shoveler(int in_socket)
|
||||
if (FD_ISSET(in_socket, &fds)) {
|
||||
/* The client wrote something to the socket: it's an SSL connection */
|
||||
saddr = &addr_ssl;
|
||||
if (verbose)
|
||||
fprintf(stderr, "Forwarding to SSL\n");
|
||||
target = "SSL";
|
||||
} else {
|
||||
/* The client hasn't written anything and we timed out: connect to SSH */
|
||||
saddr = &addr_ssh;
|
||||
if (verbose)
|
||||
fprintf(stderr, "Forwarding to SSH\n");
|
||||
target = "SSH";
|
||||
|
||||
/* do hosts_access check if built with libwrap support */
|
||||
check_access_rights(in_socket);
|
||||
}
|
||||
|
||||
log_connection(in_socket, target);
|
||||
|
||||
/* Connect the target socket */
|
||||
out_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
res = connect(out_socket, saddr, sizeof(addr_ssl));
|
||||
@@ -274,25 +311,31 @@ void start_shoveler(int in_socket)
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* SIGCHLD handling:
|
||||
* we need to reap our children
|
||||
*/
|
||||
void child_handler(int signo)
|
||||
{
|
||||
signal(SIGCHLD, &child_handler);
|
||||
wait(NULL);
|
||||
}
|
||||
void setup_signals(void)
|
||||
{
|
||||
void* res;
|
||||
int res;
|
||||
struct sigaction action;
|
||||
|
||||
res = signal(SIGCHLD, &child_handler);
|
||||
if (res == SIG_ERR) {
|
||||
perror("signal");
|
||||
exit(1);
|
||||
}
|
||||
/* Request no SIGCHLD is sent upon termination of
|
||||
* the children */
|
||||
memset(&action, 0, sizeof(action));
|
||||
action.sa_handler = NULL;
|
||||
action.sa_flags = SA_NOCLDWAIT;
|
||||
res = sigaction(SIGCHLD, &action, NULL);
|
||||
CHECK_RES_DIE(res, "sigaction");
|
||||
}
|
||||
|
||||
/* Open syslog connection with appropriate banner;
|
||||
* banner is made up of basename(bin_name)+"[pid]" */
|
||||
void setup_syslog(char* bin_name) {
|
||||
char *name1, *name2;
|
||||
|
||||
name1 = strdup(bin_name);
|
||||
asprintf(&name2, "%s[%d]", basename(name1), getpid());
|
||||
openlog(name2, LOG_CONS, LOG_AUTH);
|
||||
free(name1);
|
||||
/* Don't free name2, as openlog(3) uses it (at least in glibc) */
|
||||
}
|
||||
|
||||
/* We don't want to run as root -- drop priviledges if required */
|
||||
void drop_privileges(char* user_name)
|
||||
@@ -312,15 +355,11 @@ void drop_privileges(char* user_name)
|
||||
CHECK_RES_DIE(res, "setuid");
|
||||
}
|
||||
|
||||
/* Writes my PID if $PIDFILE is defined */
|
||||
void write_pid_file(void)
|
||||
/* Writes my PID */
|
||||
void write_pid_file(char* pidfile)
|
||||
{
|
||||
char *pidfile = getenv("PIDFILE");
|
||||
FILE *f;
|
||||
|
||||
if (!pidfile)
|
||||
return;
|
||||
|
||||
f = fopen(pidfile, "w");
|
||||
if (!f) {
|
||||
perror(pidfile);
|
||||
@@ -342,7 +381,7 @@ void printsettings(void)
|
||||
timeout
|
||||
);
|
||||
fprintf(stderr, "SSH addr: %s\n", sprintaddr(buf, sizeof(buf), &addr_ssh));
|
||||
fprintf(stderr, "listening on port %d\n", listen_port);
|
||||
fprintf(stderr, "listening on %s\n", sprintaddr(buf, sizeof(buf), &addr_listen));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@@ -356,13 +395,17 @@ int main(int argc, char *argv[])
|
||||
|
||||
/* Init defaults */
|
||||
char *user_name = "nobody";
|
||||
char listen_str[] = "0.0.0.0:443";
|
||||
char ssl_str[] = "localhost:443";
|
||||
char ssh_str[] = "localhost:22";
|
||||
char *pid_file = "/var/run/sslh.pid";
|
||||
char inetd = 0;
|
||||
|
||||
resolve_name(&addr_ssl, ssl_str, 443);
|
||||
resolve_name(&addr_ssh, ssh_str, 22);
|
||||
resolve_name(&addr_listen, listen_str);
|
||||
resolve_name(&addr_ssl, ssl_str);
|
||||
resolve_name(&addr_ssh, ssh_str);
|
||||
|
||||
while ((c = getopt(argc, argv, "t:l:s:p:vu:")) != EOF) {
|
||||
while ((c = getopt(argc, argv, "t:l:s:p:P:ivVu:")) != EOF) {
|
||||
switch (c) {
|
||||
|
||||
case 't':
|
||||
@@ -370,41 +413,60 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
listen_port = atoi(optarg);
|
||||
resolve_name(&addr_listen, optarg);
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
resolve_name(&addr_ssl, optarg, 443);
|
||||
resolve_name(&addr_ssl, optarg);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
resolve_name(&addr_ssh, optarg, 22);
|
||||
resolve_name(&addr_ssh, optarg);
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
inetd = 1;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verbose += 1;
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
printf("sslh %s\n", VERSION);
|
||||
exit(0);
|
||||
|
||||
case 'u':
|
||||
user_name = optarg;
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
pid_file = optarg;
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, USAGE_STRING);
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
if(inetd)
|
||||
{
|
||||
verbose = 0;
|
||||
start_shoveler(0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
printsettings();
|
||||
|
||||
setup_signals();
|
||||
|
||||
listen_socket = start_listen_socket(listen_port);
|
||||
listen_socket = start_listen_socket(&addr_listen);
|
||||
|
||||
if (fork() > 0) exit(0); /* Detach */
|
||||
|
||||
write_pid_file();
|
||||
write_pid_file(pid_file);
|
||||
|
||||
drop_privileges(user_name);
|
||||
|
||||
@@ -412,6 +474,9 @@ int main(int argc, char *argv[])
|
||||
res = setsid();
|
||||
CHECK_RES_DIE(res, "setsid: already process leader");
|
||||
|
||||
/* Open syslog connection */
|
||||
setup_syslog(argv[0]);
|
||||
|
||||
/* Main server loop: accept connections, find what they are, fork shovelers */
|
||||
while (1)
|
||||
{
|
||||
@@ -420,6 +485,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (!fork())
|
||||
{
|
||||
close(listen_socket);
|
||||
start_shoveler(in_socket);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
# I'm just not gonna write troff :-)
|
||||
|
||||
=head1 NAME
|
||||
|
||||
sslh - ssl/ssh multiplexer
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
sslh [ B<-t> I<num> ] [B<-p> I<listening address>] [B<-l> I<target address for SSL>] [B<-s> I<target address for SSH>] [B<-u> I<username>] [B<-P> I<pidfile>] [-v] [-i] [-V]
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
B<sslh> lets one accept both HTTPS and SSH connections on
|
||||
the same port. It makes it possible to connect to an SSH
|
||||
server on port 443 (e.g. from inside a corporate firewall,
|
||||
which almost never block port 443) while still serving HTTPS
|
||||
on that port.
|
||||
|
||||
The idea is to have B<sslh> listen to the external 443 port,
|
||||
accept the incoming connections, work out what type of
|
||||
connection it is, and then fordward to the appropriate
|
||||
server.
|
||||
|
||||
=head2 Protocol detection
|
||||
|
||||
The protocol detection is made based on a small difference
|
||||
between SSL and SSH: an SSL client connecting to a server
|
||||
speaks first, whereas an SSH client expects the SSH server
|
||||
to speak first (announcing itself with a banner). B<sslh>
|
||||
waits for some time for the incoming connection to send data.
|
||||
If it does before the timeout occurs, it is supposed to be
|
||||
an SSL connection. Otherwise, it is supposed to be an SSH
|
||||
connection.
|
||||
|
||||
=head2 Libwrap support
|
||||
|
||||
One drawback of B<sslh> is that the B<ssh> and B<httpd>
|
||||
servers do not see the original IP address of the client
|
||||
anymore, as the connection is forwarded through B<sslh>.
|
||||
B<sslh> provides enough logging to circumvent that problem.
|
||||
However it is common to limit access to B<ssh> using
|
||||
B<libwrap> or B<tcpd>. For this reason, B<sslh> can be
|
||||
compiled to check SSH accesses against SSH access lists as
|
||||
defined in F</etc/hosts.allow> and F</etc/hosts.deny>.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<-t> I<num>
|
||||
|
||||
Timeout before a connection is considered to be SSH. Default
|
||||
is 2s.
|
||||
|
||||
=item B<-p> I<listening address>
|
||||
|
||||
Interface and port on which to listen, e.g. I<foobar:443>,
|
||||
where I<foobar> is the name of an interface (typically the
|
||||
IP address on which the Internet connection ends up).
|
||||
|
||||
Defaults to I<0.0.0.0:443> (listen to port 443 on all
|
||||
available interfaces).
|
||||
|
||||
=item B<-l> I<target address for SSL>
|
||||
|
||||
Interface and port on which to forward SSL connection,
|
||||
typically I<localhost:443>.
|
||||
|
||||
Defaults to I<localhost:443> (this assumes you would
|
||||
configure your B<httpd> process to listen to port 443).
|
||||
|
||||
Note that you can set B<sslh> to listen on I<ext_ip:443> and
|
||||
B<httpd> to listen on I<localhost:443>: this allows clients
|
||||
inside your network to just connect directly to B<httpd>.
|
||||
|
||||
=item B<-s> I<target address for SSH>
|
||||
|
||||
Interface and port on which to forward SSH connection,
|
||||
defaults to I<localhost:22>.
|
||||
|
||||
=item B<-v>
|
||||
|
||||
Increase verboseness.
|
||||
|
||||
=item B<-V>
|
||||
|
||||
Prints B<sslh> version.
|
||||
|
||||
=item B<-u> I<username>
|
||||
|
||||
Requires to run under the specified username. Defaults to
|
||||
I<nobody> (which is not perfect -- ideally B<sslh> should
|
||||
run under its own UID).
|
||||
|
||||
=item B<-P> I<pidfile>
|
||||
|
||||
Specifies the file in which to write the PID of the main
|
||||
server. Defaults to I</var/run/sslh.pid>.
|
||||
|
||||
=item B<-i>
|
||||
|
||||
Runs as an I<inetd> server. Options B<-P> (PID file), B<-p>
|
||||
(listen address), B<-U> (user) are ignored.
|
||||
|
||||
=back
|
||||
|
||||
=head1 FILES
|
||||
|
||||
=over 4
|
||||
|
||||
=item F</etc/init.d/sslh>
|
||||
|
||||
Start-up script. The standard actions B<start>, B<stop> and
|
||||
B<restart> are supported.
|
||||
|
||||
=item F</etc/default/sslh>
|
||||
|
||||
Server configuration. These are environment variables
|
||||
loaded by the start-up script and passed to B<sslh> as
|
||||
command-line arguments. Refer to the OPTIONS section for a
|
||||
detailed explanation of the variables used by B<sslh>.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Last version available from
|
||||
L<http://www.rutschle.net/tech/sslh>, and can be tracked
|
||||
from L<http://freshmeat.net/projects/sslh/>.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Written by Yves Rutschle
|
||||
Reference in new issue
Block a user