Initial commit
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
Copyright 2012 Bump Technologies, Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are
|
||||
permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
of conditions and the following disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY BUMP TECHNOLOGIES, INC. ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BUMP TECHNOLOGIES, INC. OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those of the
|
||||
authors and should not be interpreted as representing official policies, either expressed
|
||||
or implied, of Bump Technologies, Inc.
|
||||
@@ -0,0 +1,52 @@
|
||||
# [g]make USE_xxxx=1
|
||||
#
|
||||
# USE_SHARED_CACHE : enable/disable a shared session cache (disabled by default)
|
||||
|
||||
DESTDIR =
|
||||
PREFIX = /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
MANDIR = $(PREFIX)/share/man
|
||||
|
||||
CFLAGS = -O2 -g -std=c99 -fno-strict-aliasing -Wall -W -D_GNU_SOURCE -I/usr/local/include
|
||||
LDFLAGS = -lssl -lcrypto -lev -L/usr/local/lib
|
||||
OBJS = stud.o ringbuffer.o configuration.o
|
||||
|
||||
all: realall
|
||||
|
||||
# Shared cache feature
|
||||
ifneq ($(USE_SHARED_CACHE),)
|
||||
CFLAGS += -DUSE_SHARED_CACHE -DUSE_SYSCALL_FUTEX
|
||||
OBJS += shctx.o ebtree/libebtree.a
|
||||
ALL += ebtree
|
||||
|
||||
ebtree/libebtree.a: $(wildcard ebtree/*.c)
|
||||
make -C ebtree
|
||||
ebtree:
|
||||
@[ -d ebtree ] || ( \
|
||||
echo "*** Download libebtree at http://1wt.eu/tools/ebtree/" ; \
|
||||
echo "*** Untar it and make a link named 'ebtree' to point on it"; \
|
||||
exit 1 )
|
||||
endif
|
||||
|
||||
# No config file support?
|
||||
ifneq ($(NO_CONFIG_FILE),)
|
||||
CFLAGS += -DNO_CONFIG_FILE
|
||||
endif
|
||||
|
||||
ALL += stud
|
||||
realall: $(ALL)
|
||||
|
||||
stud: $(OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
install: $(ALL)
|
||||
install -d $(DESTDIR)$(BINDIR)
|
||||
install stud $(DESTDIR)$(BINDIR)
|
||||
install -d $(DESTDIR)$(MANDIR)/man8
|
||||
install -m 644 stud.8 $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
clean:
|
||||
rm -f stud $(OBJS)
|
||||
|
||||
|
||||
.PHONY: all realall
|
||||
@@ -0,0 +1,191 @@
|
||||
STATUS
|
||||
======
|
||||
|
||||
Stud is now officially abandonware, thanks for playing.
|
||||
|
||||
Recommended alternative: https://github.com/varnish/hitch
|
||||
|
||||
More info: https://blog.jamwt.com/2016/03/21/stud-no-more/
|
||||
|
||||
stud - The Scalable TLS Unwrapping Daemon
|
||||
=========================================
|
||||
|
||||
`stud` is a network proxy that terminates TLS/SSL connections and forwards the
|
||||
unencrypted traffic to some backend. It's designed to handle 10s of thousands of
|
||||
connections efficiently on multicore machines.
|
||||
|
||||
It follows a process-per-core model; a parent process spawns N children who
|
||||
each `accept()` on a common socket to distribute connected clients among them.
|
||||
Within each child, asynchronous socket I/O is conducted across the local
|
||||
connections using `libev` and `OpenSSL`'s nonblocking API. By default,
|
||||
`stud` has an overhead of ~200KB per connection--it preallocates
|
||||
some buffer space for data in flight between frontend and backend.
|
||||
|
||||
`stud` has very few features--it's designed to be paired with an intelligent
|
||||
backend like haproxy or nginx. It maintains a strict 1:1 connection pattern
|
||||
with this backend handler so that the backend can dictate throttling behavior,
|
||||
maxmium connection behavior, availability of service, etc.
|
||||
|
||||
`stud` will optionally write the client IP address as the first few octets
|
||||
(depending on IPv4 or IPv6) to the backend--or provide that information
|
||||
using HAProxy's PROXY protocol. When used with the PROXY protocol, `stud` can
|
||||
also transparently pass an existing PROXY header to the cleartext stream. This
|
||||
is especially useful if a TCP proxy is used in front of `stud`. Using either of
|
||||
these techniques, backends who care about the client IP can still access it even
|
||||
though `stud` itself appears to be the connected client.
|
||||
|
||||
Thanks to a contribution from Emeric at Exceliance (the folks behind HAProxy),
|
||||
a special build of `stud` can be made that utilitizes shared memory to
|
||||
use a common session cache between all child processes. This can speed up
|
||||
large `stud` deployments by avoiding client renegotiation.
|
||||
|
||||
Releases
|
||||
---------
|
||||
|
||||
Please be aware of the policy regarding releases, code stability, and security:
|
||||
|
||||
* In git, the tip of the master branch should always build on Linux and
|
||||
FreeBSD, and is likely to be as stable as any other changeset. A
|
||||
careful review of patches is conducted before being pushed to github.
|
||||
* Periodically, a version tag will be pushed to github for an old(er)
|
||||
changeset--0.1, 0.2, etc. These tags mark a particular release of
|
||||
`stud` that has seen heavy testing and several weeks of production
|
||||
stability. Conservative users are advised to use a tag.
|
||||
* `stud` has an optional build that utilizes shared memory-based SSL contexts
|
||||
and UDP peer communication to keep a session cache between many child processes
|
||||
running on many machines. The use of this build can dramatically speed
|
||||
up SSL handshakes on many-core and/or clustered deployments.
|
||||
However, it's important to acknowledge the inevitable theoretical
|
||||
security tradeoffs associated with the use of this (substantially more
|
||||
complex) binary. Therefore, the deeply paranoid are advised to use
|
||||
only the standard `stud` binary at the cost of some performance.
|
||||
|
||||
Requirements and Limitations
|
||||
----------------------------
|
||||
|
||||
`stud` requires:
|
||||
|
||||
libev >= 4
|
||||
openssl (recent, >=1.0.0 recommended)
|
||||
|
||||
Stud currently works on Linux, OpenBSD, FreeBSD, and MacOSX.
|
||||
It has been tested the most heavily on Linux/x86_64.
|
||||
|
||||
While porting it to other POSIX platforms is likely trivial, it hasn't be done
|
||||
yet. Patches welcome!
|
||||
|
||||
If you're handling a large number of connections, you'll
|
||||
probably want to raise `ulimit -n` before running `stud`.
|
||||
It's very strongly recommended to not run `stud` as root; ideally, it would
|
||||
be run as a user ("stud", perhaps) that does nothing but run `stud`. Stud
|
||||
will setuid (using -u) after binding if you need to bind to a low port (< 1024).
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
To install `stud`:
|
||||
|
||||
$ make
|
||||
$ sudo make install
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The only required argument is a path to a PEM file that contains the certificate
|
||||
(or a chain of certificates) and private key. If multiple certificates are
|
||||
given, `stud` will attempt to perform SNI (Server Name Indication) on new
|
||||
connections, by comparing the indicated name with the names on each of the
|
||||
certificates, in order. The first certificate that matches will be used. If none
|
||||
of the certificates matches, the last certificate will be used as the default.
|
||||
|
||||
Detail about the entire set of options can be found by invoking `stud -h`:
|
||||
|
||||
CONFIGURATION:
|
||||
|
||||
--config=FILE Load configuration from specified file.
|
||||
--default-config Prints default configuration to stdout.
|
||||
|
||||
ENCRYPTION METHODS:
|
||||
|
||||
--tls TLSv1 (default)
|
||||
--ssl SSLv3 (implies no TLSv1)
|
||||
-c --ciphers=SUITE Sets allowed ciphers (Default: "")
|
||||
-e --ssl-engine=NAME Sets OpenSSL engine (Default: "")
|
||||
-O --prefer-server-ciphers Prefer server list order
|
||||
|
||||
SOCKET:
|
||||
|
||||
-b --backend=HOST,PORT Backend [connect] (default is "[127.0.0.1]:8000")
|
||||
-f --frontend=HOST,PORT Frontend [bind] (default is "[*]:8443")
|
||||
|
||||
PERFORMANCE:
|
||||
|
||||
-n --workers=NUM Number of worker processes (Default: 1)
|
||||
-B --backlog=NUM Set listen backlog size (Default: 100)
|
||||
-k --keepalive=SECS TCP keepalive on client socket (Default: 3600)
|
||||
|
||||
SECURITY:
|
||||
|
||||
-r --chroot=DIR Sets chroot directory (Default: "")
|
||||
-u --user=USER Set uid/gid after binding the socket (Default: "")
|
||||
-g --group=GROUP Set gid after binding the socket (Default: "")
|
||||
|
||||
LOGGING:
|
||||
-q --quiet Be quiet; emit only error messages
|
||||
-s --syslog Send log message to syslog in addition to stderr/stdout
|
||||
--syslog-facility=FACILITY Syslog facility to use (Default: "daemon")
|
||||
|
||||
OTHER OPTIONS:
|
||||
--daemon Fork into background and become a daemon (Default: off)
|
||||
--write-ip Write 1 octet with the IP family followed by the IP
|
||||
address in 4 (IPv4) or 16 (IPv6) octets little-endian
|
||||
to backend before the actual data
|
||||
(Default: off)
|
||||
--write-proxy Write HaProxy's PROXY (IPv4 or IPv6) protocol line
|
||||
before actual data
|
||||
(Default: off)
|
||||
--proxy-proxy Proxy HaProxy's PROXY (IPv4 or IPv6) protocol line
|
||||
before actual data
|
||||
(Default: off)
|
||||
|
||||
-t --test Test configuration and exit
|
||||
-V --version Print program version and exit
|
||||
-h --help This help message
|
||||
|
||||
Configuration File
|
||||
------------------
|
||||
|
||||
Stud can also use a configuration file that supports all the same options as the
|
||||
command-line arguments. You can use `stud --default-config` to
|
||||
generate the default configuration on stdout; then, customize your configuration and
|
||||
pass it to `stud --config=FILE`.
|
||||
|
||||
Serving HTTPS
|
||||
-------------
|
||||
|
||||
If you're using `stud` for HTTPS, please make sure to use the `--ssl` option!
|
||||
|
||||
|
||||
Diffie–Hellman
|
||||
--------------
|
||||
|
||||
To use DH with stud, you will need to add some bytes to your pem file:
|
||||
|
||||
% openssl dhparam -rand - 1024 >> PEMFILE
|
||||
|
||||
Be sure to set your cipher suite appropriately: -c DHE-RSA-AES256-SHA
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
`stud` was originally written by Jamie Turner (@jamwt) and is maintained
|
||||
by the Bump (http://bu.mp) server team. It currently (12/11) provides
|
||||
server-side TLS termination for over 85 million Bump users.
|
||||
|
||||
Special thanks to Colin Percival (@cperciva) for an early security
|
||||
audit and code review.
|
||||
|
||||
Finally, thank you to all the stud contributors, who have taken the
|
||||
program from a good start to a solid project:
|
||||
|
||||
https://github.com/bumptech/stud/contributors
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
openssl genrsa -out test.com.key 1024
|
||||
openssl req -new -key test.com.key -out test.com.csr
|
||||
openssl x509 -req -days 365 -in test.com.csr -signkey test.com.key -out test.com.crt
|
||||
cat test.com.key test.com.crt > openssl genrsa -out test.com.key 1024
|
||||
openssl req -new -key test.com.key -out test.com.csr
|
||||
openssl x509 -req -days 365 -in test.com.csr -signkey test.com.key -out test.com.crt
|
||||
cat test.com.key test.com.crt > test.com.pem
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* configuration.h
|
||||
*
|
||||
* Author: Brane F. Gracnar
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef USE_SHARED_CACHE
|
||||
#include "shctx.h"
|
||||
|
||||
#ifndef MAX_SHCUPD_PEERS
|
||||
#define MAX_SHCUPD_PEERS 15
|
||||
#endif
|
||||
|
||||
typedef struct shcupd_peer_opt {
|
||||
char *ip;
|
||||
char *port;
|
||||
} shcupd_peer_opt;
|
||||
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
ENC_TLS,
|
||||
ENC_SSL
|
||||
} ENC_TYPE;
|
||||
|
||||
typedef enum {
|
||||
SSL_SERVER,
|
||||
SSL_CLIENT
|
||||
} PROXY_MODE;
|
||||
|
||||
struct cert_files {
|
||||
char *CERT_FILE;
|
||||
struct cert_files *NEXT;
|
||||
};
|
||||
|
||||
/* configuration structure */
|
||||
struct __stud_config {
|
||||
ENC_TYPE ETYPE;
|
||||
PROXY_MODE PMODE;
|
||||
int WRITE_IP_OCTET;
|
||||
int WRITE_PROXY_LINE;
|
||||
int PROXY_PROXY_LINE;
|
||||
char *CHROOT;
|
||||
uid_t UID;
|
||||
gid_t GID;
|
||||
char *FRONT_IP;
|
||||
char *FRONT_PORT;
|
||||
char *BACK_IP;
|
||||
char *BACK_PORT;
|
||||
long NCORES;
|
||||
struct cert_files *CERT_FILES;
|
||||
char *CIPHER_SUITE;
|
||||
char *ENGINE;
|
||||
int BACKLOG;
|
||||
#ifdef USE_SHARED_CACHE
|
||||
int SHARED_CACHE;
|
||||
char *SHCUPD_IP;
|
||||
char *SHCUPD_PORT;
|
||||
shcupd_peer_opt SHCUPD_PEERS[MAX_SHCUPD_PEERS+1];
|
||||
char *SHCUPD_MCASTIF;
|
||||
char *SHCUPD_MCASTTTL;
|
||||
#endif
|
||||
int QUIET;
|
||||
int SYSLOG;
|
||||
int SYSLOG_FACILITY;
|
||||
int TCP_KEEPALIVE_TIME;
|
||||
int DAEMONIZE;
|
||||
int PREFER_SERVER_CIPHERS;
|
||||
};
|
||||
|
||||
typedef struct __stud_config stud_config;
|
||||
|
||||
char * config_error_get (void);
|
||||
stud_config * config_new (void);
|
||||
void config_destroy (stud_config *cfg);
|
||||
int config_file_parse (char *file, stud_config *cfg);
|
||||
void config_parse_cli(int argc, char **argv, stud_config *cfg);
|
||||
@@ -0,0 +1,6 @@
|
||||
stud (0.3~0.20111212) unstable; urgency=low
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- Charlie Root <root@interseek.com> Mon, 12 Dec 2011 13:24:18 +0100
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
7
|
||||
@@ -0,0 +1,26 @@
|
||||
Source: stud
|
||||
Section: universe/net
|
||||
Priority: optional
|
||||
Maintainer: Charlie Root <root@example.com>
|
||||
Build-Depends: debhelper (>= 7.0.50~)
|
||||
Standards-Version: 3.9.2
|
||||
Homepage: https://github.com/bumptech/stud
|
||||
|
||||
Package: stud
|
||||
Section: universe/net
|
||||
Priority: optional
|
||||
Architecture: any
|
||||
Depends: libc6 (>= 2.4), libev4 (>= 1:4.04), libssl0.9.8 (>= 0.9.8k-1) | libssl1.0.0 (>= 1.0.0e-1)
|
||||
# Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: The Scalable TLS Unwrapping Daemon
|
||||
stud is a network proxy that terminates TLS/SSL connections and forwards
|
||||
the unencrypted traffic to some backend. It's designed to handle 10s of
|
||||
thousands of connections efficiently on multicore machines.
|
||||
.
|
||||
It follows a process-per-core model; a parent process spawns N children who
|
||||
each accept() on a common socket to distribute connected clients among them.
|
||||
Within each child, asynchronous socket I/O is conducted across the local
|
||||
connections using libev and OpenSSL's nonblocking API. By default, stud
|
||||
has an overhead of ~200KB per connection--it preallocates some buffer space
|
||||
for data in flight between frontend and backend.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
Copyright 2011 Bump Technologies, Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are
|
||||
permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
of conditions and the following disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY BUMP TECHNOLOGIES, INC. ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BUMP TECHNOLOGIES, INC. OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those of the
|
||||
authors and should not be interpreted as representing official policies, either expressed
|
||||
or implied, of Bump Technologies, Inc.
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
case $1 in
|
||||
# Configure this package. If the package must prompt the user for
|
||||
# information, do it here.
|
||||
configure)
|
||||
mkdir -p /var/run/stud
|
||||
;;
|
||||
|
||||
# Back out of an attempt to upgrade this package FROM THIS VERSION
|
||||
# to version $2. Undo the effects of "prerm upgrade $2".
|
||||
abort-upgrade)
|
||||
;;
|
||||
|
||||
# Back out of an attempt to remove this package, which was due to
|
||||
# a conflict with package $3 (version $4). Undo the effects of
|
||||
# "prerm remove in-favour $3 $4".
|
||||
abort-remove)
|
||||
;;
|
||||
|
||||
# Back out of an attempt to deconfigure this package, which was
|
||||
# due to package $6 (version $7) which we depend on being removed
|
||||
# to make way for package $3 (version $4). Undo the effects of
|
||||
# "prerm deconfigure in-favour $3 $4 removing $6 $7".
|
||||
abort-deconfigure)
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "$0: didn't understand being called with \`$1'" 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
if service stud status >/dev/null 2>&1; then
|
||||
echo
|
||||
echo " !!!! WARNING !!!!"
|
||||
echo
|
||||
echo "stud is running, you should restart it."
|
||||
echo
|
||||
fi
|
||||
|
||||
# EOF
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
|
||||
case "$1" in
|
||||
remove|purge|abort-install|abort-upgrade)
|
||||
# This package is being removed, but its configuration has not yet
|
||||
# been purged.
|
||||
|
||||
# stud shouldn't start at system startup
|
||||
# update-rc.d -f stud remove
|
||||
|
||||
# remove rundir
|
||||
rm -rf /var/run/stud >/dev/null 2>&1
|
||||
|
||||
# remove config dir
|
||||
rm -rf /etc/stud >/dev/null 2>&1
|
||||
|
||||
;;
|
||||
|
||||
disappear)
|
||||
;;
|
||||
|
||||
upgrade)
|
||||
;;
|
||||
|
||||
failed-upgrade)
|
||||
;;
|
||||
|
||||
*) echo "$0: didn't understand being called with \`$1'" 1>&2
|
||||
exit 1;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# EOF
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
case "$1" in
|
||||
remove|purge|abort-install|abort-upgrade)
|
||||
# This package is being removed, but its configuration has not yet
|
||||
# been purged.
|
||||
|
||||
# shutdown service
|
||||
service stud stop
|
||||
|
||||
;;
|
||||
|
||||
disappear)
|
||||
;;
|
||||
|
||||
upgrade)
|
||||
;;
|
||||
|
||||
failed-upgrade)
|
||||
;;
|
||||
|
||||
*) echo "$0: didn't understand being called with \`$1'" 1>&2
|
||||
exit 1;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# EOF
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
override_dh_auto_clean:
|
||||
rm -rf ebtree
|
||||
make clean
|
||||
|
||||
override_dh_auto_configure:
|
||||
if [ ! -f ebtree-6.0.6.tar.gz ]; then wget http://1wt.eu/tools/ebtree/ebtree-6.0.6.tar.gz; fi
|
||||
if [ ! -d ebtree ]; then tar zxpf ebtree-6.0.6.tar.gz; mv ebtree-6.0.6 ebtree; fi
|
||||
|
||||
override_dh_auto_build:
|
||||
make USE_SHARED_CACHE=1 PREFIX=/usr
|
||||
|
||||
override_dh_auto_install:
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_prep
|
||||
dh_installdirs
|
||||
make install DESTDIR=$(CURDIR)/debian/stud PREFIX=/usr
|
||||
|
||||
# create stud instance configuration directory
|
||||
mkdir -p $(CURDIR)/debian/stud/etc/stud
|
||||
chown -R root:root $(CURDIR)/debian/stud/etc/stud
|
||||
chmod 700 $(CURDIR)/debian/stud/etc/stud
|
||||
echo "Run /etc/init.d/stud --sample-config > /etc/stud/something.conf for default instance configuration" >> "$(CURDIR)/debian/stud/etc/stud/README.TXT"
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
# EOF
|
||||
@@ -0,0 +1,7 @@
|
||||
openssl genrsa -out test.com.key 1024
|
||||
penssl req -new -key test.com.key -out test.com.csr
|
||||
openssl x509 -req -days 365 -in test.com.csr -signkey test.com.key -out test.com.crt
|
||||
cat test.com.key test.com.crt > openssl genrsa -out test.com.key 1024
|
||||
openssl req -new -key test.com.key -out test.com.csr
|
||||
openssl x509 -req -days 365 -in test.com.csr -signkey test.com.key -out test.com.crt
|
||||
cat test.com.key test.com.crt > test.com.pem
|
||||
@@ -0,0 +1,837 @@
|
||||
#!/bin/sh
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: stud
|
||||
# Required-Start: $local_fs $remote_fs
|
||||
# Required-Stop: $local_fs $remote_fs
|
||||
# Should-Start: $syslog
|
||||
# Should-Stop: $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Start or stop stud (SSL offloader)
|
||||
### END INIT INFO
|
||||
|
||||
#######################################################
|
||||
# GLOBALS #
|
||||
#######################################################
|
||||
|
||||
# instance configuration directory
|
||||
CONFIG_DIR="/etc/stud"
|
||||
|
||||
# Runtime directory data
|
||||
RUNTIME_DIR="/var/run/stud"
|
||||
|
||||
#######################################################
|
||||
|
||||
#######################################################
|
||||
|
||||
stud_single_instance_config_reset() {
|
||||
#######################################################
|
||||
# stud instance configuration #
|
||||
#######################################################
|
||||
|
||||
# stud listening address
|
||||
FRONTEND_ADDRESS="*,8443"
|
||||
|
||||
# upstream service address
|
||||
BACKEND_ADDRESS="127.0.0.1,80"
|
||||
|
||||
# x509 certificate file
|
||||
CERT_FILE=""
|
||||
|
||||
# TLS only service? Don't set this to 1 if you're
|
||||
# offloading HTTPS.
|
||||
TLS_ONLY="0"
|
||||
|
||||
# cipher suite (run openssl ciphers for full list)
|
||||
CIPHER_SUITE="HIGH"
|
||||
|
||||
# OpenSSL engine
|
||||
ENGINE=""
|
||||
|
||||
# Number of worker processes
|
||||
WORKERS="1"
|
||||
|
||||
# Listen backlog
|
||||
BACKLOG=""
|
||||
|
||||
# Chroot directory
|
||||
CHROOT_DIR=""
|
||||
|
||||
# drop privileges and run as specified
|
||||
# user if set
|
||||
SETUID_USER=""
|
||||
|
||||
# use shared cache with specified number of sessions
|
||||
# WARNING: stud must be compiled with USE_SHARED_CACHE=1
|
||||
SHARED_CACHE_SESSIONS="0"
|
||||
|
||||
# Accept cache updates on specified address
|
||||
#
|
||||
# syntax: HOST,PORT
|
||||
#
|
||||
# WARNING: stud must be compiled with USE_SHARED_CACHE=1
|
||||
# SHARED_CACHE_SESSIONS must be >= 1
|
||||
CACHE_UPDATE_ACCEPT=""
|
||||
|
||||
# Send cache updates to specified list space separated peers
|
||||
#
|
||||
# syntax: HOST1,PORT HOST2,PORT
|
||||
#
|
||||
# WARNING: stud must be compiled with USE_SHARED_CACHE=1
|
||||
# and CACHE_UPDATE_ACCEPT must be defined
|
||||
CACHE_UPDATE_SEND=""
|
||||
|
||||
# Force network interface and ttl to receive and send multicast
|
||||
# cache updates
|
||||
#
|
||||
# syntax: IFACE[,TTL]
|
||||
#
|
||||
# WARNING: stud must be compiled with USE_SHARED_CACHE=1
|
||||
# and CACHE_UPDATE_ACCEPT must be defined
|
||||
CACHE_UPDATE_IFACE=""
|
||||
|
||||
# default tcp keepalive on client socket in seconds
|
||||
CLIENT_TCP_KEEPALIVE_SEC=""
|
||||
|
||||
# log to syslog?
|
||||
SYSLOG="1"
|
||||
|
||||
# Enable write-ip?
|
||||
WRITE_IP="0"
|
||||
|
||||
# Enable SENDPROXY protocol; see
|
||||
# http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt
|
||||
# for additional info
|
||||
WRITE_PROXY="0"
|
||||
|
||||
# Alternative OpenSSL library dir
|
||||
# Use this if you'd like to run stud with different
|
||||
# version of OpenSSL library
|
||||
OPENSSL_LIB_DIR=""
|
||||
|
||||
# Semicolon separated list of process affinities; requires
|
||||
# taskset(8) utility.
|
||||
#
|
||||
# SYNTAX:
|
||||
# "<process_number>:<affinity>;<process_number2>:<affinity2>;..."
|
||||
#
|
||||
# <process_number>: stud worker process number, starting with 1
|
||||
# <affinity>: process affinity, see taskset(8) for details
|
||||
#
|
||||
# EXAMPLES:
|
||||
#
|
||||
# "1:0" => bind first process to CPU0
|
||||
#
|
||||
# "1:0;2:3-4;3:5;4:7" => bind first worker process to CPU0,
|
||||
# second worker process to CPU3 and CPU4,
|
||||
# third worker process to CPU5 and fourth
|
||||
# worker process to CPU7
|
||||
PROCESS_AFFINITY=""
|
||||
|
||||
# Process priority (integer between -19 to 19)
|
||||
# lower value means higher priority
|
||||
#
|
||||
PROCESS_PRIORITY=""
|
||||
|
||||
# ulimit -n value before starting single stud instance
|
||||
#
|
||||
# Comment out or set to 0 to disable ulimit -n
|
||||
# setup.
|
||||
#
|
||||
ULIMIT_N=""
|
||||
|
||||
# Additional stud command line options
|
||||
#
|
||||
# NOTE: set this only if you really know what your're
|
||||
# doing
|
||||
#
|
||||
# ADDITIONAL_STUD_OPT=""
|
||||
|
||||
# EOF
|
||||
}
|
||||
|
||||
PATH="${PATH}:."
|
||||
INSTANCE_NAME=""
|
||||
STUD=`which stud 2>/dev/null`
|
||||
|
||||
die() {
|
||||
msg_log "FATAL: $@"
|
||||
echo "FATAL: $@" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
msg_log() {
|
||||
ident="stud"
|
||||
test ! -z "${INSTANCE_NAME}" && ident="${ident}/${INSTANCE_NAME}"
|
||||
logger -i -t "${ident}" "$@" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
msg_err() {
|
||||
msg_log "ERROR: $@"
|
||||
}
|
||||
|
||||
|
||||
_real_single_instance_start() {
|
||||
# check stud binary
|
||||
if [ -z "${STUD}" ] || [ ! -f "${STUD}" ] || [ ! -x "${STUD}" ]; then
|
||||
die "Invalid stud binary: '${STUD}'"
|
||||
fi
|
||||
|
||||
# generate stud command line options
|
||||
opts="-f ${FRONTEND_ADDRESS}"
|
||||
opts="${opts} -b ${BACKEND_ADDRESS}"
|
||||
|
||||
if [ "${TLS_ONLY}" = "1" ]; then
|
||||
opts="${opts} --tls"
|
||||
else
|
||||
opts="${opts} --ssl"
|
||||
fi
|
||||
|
||||
test ! -z "${CIPHER_SUITE}" && opts="${opts} -c ${CIPHER_SUITE}"
|
||||
test ! -z "${ENGINE}" && opts="${opts} -e ${ENGINE}"
|
||||
|
||||
if [ ! -z "${WORKERS}" ] && [ ${WORKERS} -gt 0 ]; then
|
||||
opts="${opts} -n ${WORKERS}"
|
||||
fi
|
||||
|
||||
if [ ! -z "${BACKLOG}" ] && [ ${BACKLOG} -gt 0 ]; then
|
||||
opts="${opts} -B ${BACKLOG}"
|
||||
fi
|
||||
|
||||
if [ ! -z "${CLIENT_TCP_KEEPALIVE_SEC}" ] && [ ${CLIENT_TCP_KEEPALIVE_SEC} -gt 0 ]; then
|
||||
opts="${opts} -k ${CLIENT_TCP_KEEPALIVE_SEC}"
|
||||
fi
|
||||
|
||||
# shared cache sessions...
|
||||
if [ ! -z "${SHARED_CACHE_SESSIONS}" ] && [ ${SHARED_CACHE_SESSIONS} -gt 0 ]; then
|
||||
opts="${opts} -C ${SHARED_CACHE_SESSIONS}"
|
||||
|
||||
# shared cache stuff
|
||||
if [ ! -z "${CACHE_UPDATE_ACCEPT}" ]; then
|
||||
opts="${opts} -U ${CACHE_UPDATE_ACCEPT}"
|
||||
|
||||
c_u=0
|
||||
for h in ${CACHE_UPDATE_SEND}; do
|
||||
test ! -z "${h}" || continue
|
||||
opts="${opts} -P ${h}"
|
||||
c_u=$((c_u + 1))
|
||||
done
|
||||
if [ ${c_u} -lt 1 ]; then
|
||||
die "Cache updates are enabled but CACHE_UPDATE_SEND option seems to be empty."
|
||||
fi
|
||||
|
||||
if [ ! -z "${CACHE_UPDATE_IFACE}" ]; then
|
||||
opts="${opts} -M ${CACHE_UPDATE_IFACE}"
|
||||
fi
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# chroot?
|
||||
test ! -z "${CHROOT_DIR}" && opts="${opts} -r ${CHROOT_DIR}"
|
||||
test ! -z "${SETUID_USER}" && opts="${opts} -u ${SETUID_USER}"
|
||||
|
||||
opts="${opts} -q"
|
||||
test "${SYSLOG}" = "1" && opts="${opts} -s"
|
||||
|
||||
test "${WRITE_IP}" = "1" && opts="${opts} --write-ip"
|
||||
test "${WRITE_PROXY}" = "1" && opts="${opts} --write-proxy"
|
||||
|
||||
if [ ! -z "${CERT_FILE}" ] && [ -f "${CERT_FILE}" ] && [ -r "${CERT_FILE}" ]; then
|
||||
opts="${opts} ${CERT_FILE}"
|
||||
else
|
||||
die "Invalid or unreadable certificate file '${CERT_FILE}'."
|
||||
fi
|
||||
|
||||
# additional command line options?!
|
||||
if [ ! -z "${ADDITIONAL_STUD_OPT}" ]; then
|
||||
opts="${opts} ${ADDITIONAL_STUD_OPT}"
|
||||
fi
|
||||
|
||||
# priority?!
|
||||
prefix=""
|
||||
if [ ! -z "${PROCESS_PRIORITY}" ]; then
|
||||
prefix="nice -n ${PROCESS_PRIORITY}"
|
||||
fi
|
||||
|
||||
# we want to start stud in a daemon mode...
|
||||
opts="${opts} --daemon"
|
||||
|
||||
# set ulimits!
|
||||
ulimit_set || die "Unable to set stud runtime limits."
|
||||
|
||||
# disable linker stuff
|
||||
unset LD_LIBRARY_PATH
|
||||
|
||||
# set new lib path if requested
|
||||
if [ ! -z "${OPENSSL_LIB_DIR}" -a -d "${OPENSSL_LIB_DIR}" ]; then
|
||||
LD_LIBRARY_PATH="${OPENSSL_LIB_DIR}"
|
||||
export LD_LIBRARY_PATH
|
||||
fi
|
||||
|
||||
# start stud!
|
||||
msg_log "Starting instance '${INSTANCE_NAME}': ${STUD} ${opts}"
|
||||
out=`${prefix} ${STUD} ${opts} 2>&1`
|
||||
|
||||
# remove lib path
|
||||
unset LD_LIBRARY_PATH
|
||||
|
||||
# check invocation
|
||||
stud_pid=`echo "${out}" | tail -n 1 | cut -d " " -f5 | tr -d '.'`
|
||||
if [ -z "${stud_pid}" ]; then
|
||||
die "Empty stud pid. This is extremely weird."
|
||||
fi
|
||||
|
||||
# wait a little bit, check if pid is still alive
|
||||
sleep 0.2 >/dev/null 2>&1
|
||||
if ! kill -0 "${stud_pid}" >/dev/null 2>&1; then
|
||||
die "Stud started successfully as pid ${stud_pid} but died shortly after startup.";
|
||||
fi
|
||||
|
||||
# write pid file!
|
||||
pid_file_write "${INSTANCE_NAME}" "${stud_pid}" || msg_warn "${Error}"
|
||||
|
||||
# set affinity!
|
||||
stud_affinity_set "${stud_pid}" || die "${Error}"
|
||||
}
|
||||
|
||||
stud_single_instance_start() {
|
||||
name="${1}"
|
||||
if [ -z "${name}" ]; then
|
||||
Error="Unable to stop undefined stud instance."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# check if it is running
|
||||
if stud_single_instance_status "${name}"; then
|
||||
Error="Instance ${name} is already running as pid ${Error}."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# do the real stuff...
|
||||
Error=""
|
||||
out=`_real_single_instance_start 2>&1`
|
||||
rv=$?
|
||||
if [ "${rv}" != "0" ]; then
|
||||
Error="${out}"
|
||||
rv=1
|
||||
fi
|
||||
|
||||
# this is it! :)
|
||||
return $rv
|
||||
}
|
||||
|
||||
stud_single_instance_stop() {
|
||||
name="${1}"
|
||||
if [ -z "${name}" ]; then
|
||||
Error="Unable to stop undefined stud instance."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# check if it is running
|
||||
stud_single_instance_status "${name}" || return 1
|
||||
|
||||
# time to stop instance
|
||||
pid="${Error}"
|
||||
msg_log "Stopping stud instance '${name}', pid ${pid}."
|
||||
|
||||
ok=0
|
||||
if ! kill "${pid}" >/dev/null 2>&1; then
|
||||
Error="Unable to stop instance: unable to kill pid ${pid}."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# wait for termination
|
||||
i=0
|
||||
while [ ${i} -lt 9 ]; do
|
||||
i=$((i + 1))
|
||||
# are you dead yet?
|
||||
if ! kill -0 "${pid}" >/dev/null 2>&1; then
|
||||
ok=1
|
||||
break
|
||||
fi
|
||||
sleep 0.1 >/dev/null 2>&1
|
||||
done
|
||||
|
||||
# not ok?! try to with headshot...
|
||||
if [ "${ok}" != "1" ]; then
|
||||
msg_log "Gentle stop of instance ${name} failed, trying to stop it with SIGKILL."
|
||||
if ! kill -9 "${pid}"; then
|
||||
Error="Unable to stop instance ${name}: kill(1) failed."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
stud_single_instance_restart() {
|
||||
name="${1}"
|
||||
if [ -z "${name}" ]; then
|
||||
Error="Unable to stop undefined stud instance."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# maybe we need to stop it first...
|
||||
if stud_single_instance_status "${name}"; then
|
||||
stud_single_instance_stop "${name}" || return 1
|
||||
fi
|
||||
|
||||
# start it back...
|
||||
Error=""
|
||||
stud_single_instance_start "${name}"
|
||||
}
|
||||
|
||||
stud_single_instance_status() {
|
||||
Error=""
|
||||
if [ -z "${1}" ]; then
|
||||
Error="Invalid instance name."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# get pid file...
|
||||
pid=`pid_file_read "${1}"`
|
||||
|
||||
# check it...
|
||||
if [ -z "${pid}" ] || ! kill -0 "${pid}" >/dev/null 2>&1; then
|
||||
Error="Instance '${1}' is stopped."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# set pid to Error
|
||||
Error="${pid}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# reads pid file of specific instance
|
||||
pid_file_read() {
|
||||
test -z "${1}" && return 1
|
||||
file="${RUNTIME_DIR}/${1}.pid"
|
||||
test -f "${file}" -a -r "${file}" || return 1
|
||||
head -n 1 "${file}"
|
||||
}
|
||||
|
||||
# writes pid file for specific instance
|
||||
pid_file_write() {
|
||||
test -z "${1}" && return 1
|
||||
test -z "${2}" && return 1
|
||||
|
||||
# check runtime directory
|
||||
if [ ! -e "${RUNTIME_DIR}" ] || [ ! -d "${RUNTIME_DIR}" ] || [ ! -w "${RUNTIME_DIR}" ]; then
|
||||
# try to create directory
|
||||
mkdir -p "${RUNTIME_DIR}" || die "Unable to create missing runtime directory '${RUNTIME_DIR}'"
|
||||
fi
|
||||
|
||||
file="${RUNTIME_DIR}/${1}.pid"
|
||||
echo "${2}" > "${file}"
|
||||
}
|
||||
|
||||
# lists running instances
|
||||
running_instance_list() {
|
||||
list=""
|
||||
for file in ${RUNTIME_DIR}/*.pid; do
|
||||
test -f "${file}" || continue
|
||||
|
||||
fileb=`basename "${file}"`
|
||||
name=`echo "${fileb}" | cut -d. -f1`
|
||||
if [ -z "${name}" ]; then
|
||||
msg_log "Removing bogus pid file '${file}'."
|
||||
rm -f "${file}" >/dev/null 2>&1
|
||||
continue
|
||||
fi
|
||||
pid=`pid_file_read "${name}"`
|
||||
if [ -z "${pid}" ]; then
|
||||
msg_log "Removing bogus pid file '${file}': instance '${name}' doesn't contain pid."
|
||||
rm -f "${file}" >/dev/null 2>&1
|
||||
continue
|
||||
fi
|
||||
|
||||
# is this pid alive?
|
||||
if ! kill -0 "${pid}" >/dev/null 2>&1 ; then
|
||||
msg_log "Removing bogus pid file '${file}': instance '${name}' [pid ${pid}] is stopped."
|
||||
rm -f "${file}" >/dev/null 2>&1
|
||||
continue
|
||||
fi
|
||||
|
||||
list="${list} ${name}"
|
||||
done
|
||||
echo ${list}
|
||||
}
|
||||
|
||||
stud_instances_start() {
|
||||
list="$@"
|
||||
if [ -z "${list}" ]; then
|
||||
list=`stud_config_instances_list`
|
||||
fi
|
||||
if [ -z "${list}" ]; then
|
||||
die "No stud instances configured in directory '${CONFIG_DIR}'."
|
||||
fi
|
||||
|
||||
echo "Starting stud instances:"
|
||||
num_ok=0
|
||||
num_failed=0
|
||||
for instance in ${list}; do
|
||||
echo -n " ${instance}: "
|
||||
# load configuration
|
||||
if ! stud_single_instance_config_load "${instance}"; then
|
||||
echo "failed: ${Error}"
|
||||
return 1
|
||||
# start instance
|
||||
elif stud_single_instance_start "${instance}"; then
|
||||
echo "done."
|
||||
msg_log "Instance ${name} successfully started."
|
||||
num_ok=$((num_ok + 1))
|
||||
else
|
||||
echo "failed: ${Error}"
|
||||
msg_err "Error starting instance ${name}: ${Error}"
|
||||
num_failed=$((num_failed + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${num_failed}" != "0" ]; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
stud_instances_stop() {
|
||||
list="$@"
|
||||
if [ -z "${list}" ]; then
|
||||
list=`running_instance_list`
|
||||
fi
|
||||
if [ -z "${list}" ]; then
|
||||
die "No stud instances are running."
|
||||
fi
|
||||
|
||||
echo "Stopping stud instances:"
|
||||
num_ok=0
|
||||
num_failed=0
|
||||
for instance in ${list}; do
|
||||
echo -n " ${instance}: "
|
||||
if stud_single_instance_stop "${instance}"; then
|
||||
echo "done."
|
||||
num_ok=$((num_ok + 1))
|
||||
msg_log "Instance ${instance} successfully stopped."
|
||||
else
|
||||
echo "failed: ${Error}"
|
||||
msg_err "Error stopping instance ${instance}: ${Error}"
|
||||
num_failed=$((num_failed + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${num_failed}" != "0" ]; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
stud_instances_restart() {
|
||||
list="$@"
|
||||
if [ -z "${list}" ]; then
|
||||
list=`(running_instance_list ; stud_config_instances_list) | tr ' ' '\n' | sort -u | xargs echo`
|
||||
fi
|
||||
|
||||
echo "Restarting stud instances: "
|
||||
num_ok=0
|
||||
num_failed=0
|
||||
for instance in ${list}; do
|
||||
echo -n " ${instance}: ";
|
||||
|
||||
# load configuration
|
||||
if ! stud_single_instance_config_load "${instance}"; then
|
||||
echo "failed: ${Error}"
|
||||
return 1
|
||||
# restart instance
|
||||
elif stud_single_instance_restart "${instance}"; then
|
||||
echo "done."
|
||||
num_ok=$((num_ok + 1))
|
||||
msg_log "Instance ${instance} successfully restarted."
|
||||
else
|
||||
echo "failed: ${Error}"
|
||||
msg_err "Error restarting instance ${instance}: ${Error}"
|
||||
num_failed=$((num_failed + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${num_failed}" != "0" ]; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
stud_instances_status() {
|
||||
list_config=`stud_config_instances_list`
|
||||
list_running=`running_instance_list`
|
||||
|
||||
list_all=`echo ${list_config} ${list_running} | tr ' ' '\n' | sort -u | xargs echo`
|
||||
|
||||
i=0;
|
||||
|
||||
echo "Stud instance status: "
|
||||
|
||||
if [ -z "${list_all}" ]; then
|
||||
die "No instances are configured and/or running."
|
||||
fi
|
||||
|
||||
for instance in ${list_all}; do
|
||||
echo -n " ${instance}: "
|
||||
if stud_single_instance_status "${instance}"; then
|
||||
echo "running as pid $Error"
|
||||
i=$((i + 1))
|
||||
else
|
||||
echo "stopped"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${i} -gt 0 ]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
stud_config_instances_list() {
|
||||
list=""
|
||||
for file in ${CONFIG_DIR}/*.conf; do
|
||||
test -f "${file}" -a -r "${file}" || continue
|
||||
fileb=`basename "${file}"`
|
||||
name=`echo "${fileb}" | cut -d. -f1`
|
||||
test ! -z "${name}" || continue
|
||||
list="${list} ${name}"
|
||||
done
|
||||
|
||||
echo ${list}
|
||||
}
|
||||
|
||||
stud_single_instance_config_print() {
|
||||
head -n 151 "$0" | tail -n 123
|
||||
}
|
||||
|
||||
stud_single_instance_config_load() {
|
||||
file="${CONFIG_DIR}/${1}.conf"
|
||||
INSTANCE_NAME=""
|
||||
|
||||
# reset configuration
|
||||
stud_single_instance_config_reset
|
||||
Error=''
|
||||
|
||||
if [ -f "${file}" -a -r "${file}" ]; then
|
||||
. "${file}" >/dev/null || Error="Unable to load instance configuration file '${file}'."
|
||||
else
|
||||
Error="Invalid or unreadable instance configuration file '${file}'."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# set instance name...
|
||||
INSTANCE_NAME="${1}"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
stud_instance_worker_pids() {
|
||||
test -z "${1}" && return 1
|
||||
ps -ef | grep " ${1} " | grep -v ' 1 ' | grep -v ' grep ' | awk '{print $2}' | xargs echo
|
||||
}
|
||||
|
||||
# prints worker pid for n-th worker
|
||||
# arguments:
|
||||
# $1: list of worker pids (string)
|
||||
# $2: worker number
|
||||
stud_instance_worker_pid_by_num() {
|
||||
i=0
|
||||
local IFS=" "
|
||||
for e in ${1}; do
|
||||
i=$((i + 1))
|
||||
if [ "${i}" = "${2}" ]; then
|
||||
echo "$e"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
stud_affinity_set() {
|
||||
# nothing to set?
|
||||
test -z "$PROCESS_AFFINITY" && return 0
|
||||
|
||||
Error=""
|
||||
|
||||
# "1:0;2:3-4;3:5;4:7" => bind first haproxy process to CPU0,
|
||||
# second haproxy process to CPU3 and CPU4,
|
||||
# third haproxy process to CPU5 and fourth
|
||||
# process to CPU7
|
||||
|
||||
|
||||
worker_pids=`stud_instance_worker_pids "${1}"`
|
||||
|
||||
local IFS=";"
|
||||
item=""
|
||||
for item in $PROCESS_AFFINITY; do
|
||||
num=`echo "${item}" | cut -f1 -d:`
|
||||
affinity=`echo "${item}" | cut -f2 -d:`
|
||||
|
||||
# validate process number
|
||||
test -z "$num" && continue
|
||||
test ${num} -ge 1 2>&1 || continue
|
||||
|
||||
# validate affinity
|
||||
test -z "${affinity}" && continue
|
||||
# WORKS: OpenSUSE
|
||||
# DOESNT WORK: Debian/Ubuntu!!!
|
||||
#echo "${affinity}" | grep -qPi '[^a-f0-9\-\,x]' && continue
|
||||
|
||||
# is this raw affinity mask?
|
||||
raw_affinity=0
|
||||
echo "${affinity}" | grep -qE '^0x' && raw_affinity=1
|
||||
|
||||
# get pid for process id $num
|
||||
pid=`stud_instance_worker_pid_by_num "${worker_pids}" "$num"`
|
||||
test -z "$pid" && continue
|
||||
|
||||
#echo "item: $item; process num: $num; pid: $pid; affinity: $affinity; raw: $raw_affinity"
|
||||
|
||||
opt="-p"
|
||||
test "${raw_affinity}" = "0" && opt="${opt} -c"
|
||||
opt="${opt} ${affinity}"
|
||||
opt="${opt} ${pid}"
|
||||
# echo "WILL RUN: 'taskset $opt'"
|
||||
msg_log "Setting stud worker number ${num} (pid ${pid}) affinity using command: taskset ${opt}"
|
||||
eval taskset ${opt} >/dev/null 2>&1 || msg_log "Error setting process affinity."
|
||||
done
|
||||
}
|
||||
|
||||
ulimit_n_set() {
|
||||
if [ -z "$ULIMIT_N" ] || [ "$ULIMIT_N" = "0" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# try to set maximum possible limit...
|
||||
i="$ULIMIT_N"
|
||||
num=0
|
||||
while [ $i -gt 0 ]; do
|
||||
num=$((num + 1))
|
||||
if ulimit -n "$i" > /dev/null 2>&1; then
|
||||
percentage=$((i*100 / ${ULIMIT_N}))
|
||||
if [ $percentage -lt 75 ]; then
|
||||
Error="Filedescriptor limit set to only $i (${percentage}% of desired value of $ULIMIT_N); check your system settings."
|
||||
return 1
|
||||
fi
|
||||
msg_log "Filedescriptor limit successfully set to $i (${percentage}% of desired value of $ULIMIT_N) after ${num} iteration(s)."
|
||||
return 0
|
||||
break
|
||||
else
|
||||
i=$((i - 100))
|
||||
fi
|
||||
done
|
||||
|
||||
Error="Filedescriptor limit of $ULIMIT_N could not be set."
|
||||
msg_log "$Error"
|
||||
return 1
|
||||
}
|
||||
|
||||
ulimit_set() {
|
||||
# set fd limit
|
||||
ulimit_n_set || return 1
|
||||
|
||||
# set core file limit
|
||||
ulimit -c unlimited >/dev/null 2>&1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
printhelp() {
|
||||
cat <<EOF
|
||||
Usage: ${MYNAME} {start|stop|restart|status|sample_instance_config} [name, name2, ...]
|
||||
|
||||
This is stud SSL offloader multi-instance init script.
|
||||
|
||||
OPTIONS:
|
||||
-C --config-dir Instance configuration directory (Default: "${CONFIG_DIR}")
|
||||
|
||||
This directory is searched for files matching *.conf
|
||||
glob pattern; each file represents single stud instance
|
||||
configuration file.
|
||||
|
||||
-R --runtime-dir Runtime (pid file) directory (Default: "${RUNTIME_DIR}")
|
||||
|
||||
--sample-config Prints out default single instance configuration
|
||||
|
||||
-V --version Prints script version
|
||||
-h --help This help message
|
||||
EOF
|
||||
}
|
||||
|
||||
# parse command line...
|
||||
TEMP=`getopt -o C:R:Vh --long config-dir:,runtime-dir:,sample-config,version,help -n "$MYNAME" -- "$@"`
|
||||
test "$?" != "0" && die "Invalid command line arguments. Run $MYNAME --help for instructions."
|
||||
eval set -- "$TEMP"
|
||||
while true; do
|
||||
case $1 in
|
||||
-C|--config-dir)
|
||||
CONFIG_DIR="${2}"
|
||||
shift 2
|
||||
;;
|
||||
-R|--runtime-dir)
|
||||
RUNTIME_DIR="${2}"
|
||||
shift 2
|
||||
;;
|
||||
--sample-config)
|
||||
stud_single_instance_config_print
|
||||
exit 0
|
||||
;;
|
||||
-V|--version)
|
||||
echo "$MYNAME $VERSION"
|
||||
exit 0
|
||||
;;
|
||||
-h|--help)
|
||||
printhelp
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
die "Invalid command line arguments. Run $MYNAME --help for instructions."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# weed out real action and do something
|
||||
case $1 in
|
||||
start)
|
||||
shift
|
||||
stud_instances_start "$@"
|
||||
;;
|
||||
|
||||
stop)
|
||||
shift
|
||||
stud_instances_stop "$@"
|
||||
;;
|
||||
|
||||
force-reload|restart)
|
||||
shift
|
||||
stud_instances_restart "$@"
|
||||
;;
|
||||
|
||||
status)
|
||||
stud_instances_status
|
||||
exit $?
|
||||
;;
|
||||
|
||||
sample_instance_config|instance_config)
|
||||
stud_single_instance_config_print
|
||||
exit 0
|
||||
;;
|
||||
|
||||
*)
|
||||
printhelp
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# EOF
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright 2011 Bump Technologies, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BUMP TECHNOLOGIES, INC. ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BUMP TECHNOLOGIES, INC. OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of Bump Technologies, Inc.
|
||||
*
|
||||
**/
|
||||
|
||||
#include "ringbuffer.h"
|
||||
#include <assert.h>
|
||||
|
||||
/* Initialize a ringbuffer structure to empty */
|
||||
|
||||
void ringbuffer_init(ringbuffer *rb) {
|
||||
rb->head = &rb->slots[0];
|
||||
rb->tail = &rb->slots[0];
|
||||
rb->used = 0;
|
||||
int x;
|
||||
for (x=0; x<RING_SLOTS; x++)
|
||||
rb->slots[x].next = &(rb->slots[(x + 1) % RING_SLOTS]);
|
||||
}
|
||||
|
||||
/** READ FUNCTIONS **/
|
||||
|
||||
/* Return a char * that represents the current unconsumed buffer */
|
||||
char * ringbuffer_read_next(ringbuffer *rb, int * length) {
|
||||
assert(rb->used);
|
||||
*length = rb->head->left;
|
||||
return rb->head->ptr;
|
||||
}
|
||||
|
||||
/* Mark consumption of only part of the read head buffer */
|
||||
void ringbuffer_read_skip(ringbuffer *rb, int length) {
|
||||
assert(rb->used);
|
||||
rb->head->ptr += length;
|
||||
rb->head->left -= length;
|
||||
}
|
||||
|
||||
/* Pop a consumed (fully read) head from the buffer */
|
||||
void ringbuffer_read_pop(ringbuffer *rb) {
|
||||
assert(rb->used);
|
||||
rb->head = rb->head->next;
|
||||
rb->used--;
|
||||
}
|
||||
|
||||
|
||||
/** WRITE FUNCTIONS **/
|
||||
|
||||
/* Return the tail ptr (current target of new writes) */
|
||||
char * ringbuffer_write_ptr(ringbuffer *rb) {
|
||||
assert(rb->used < RING_SLOTS);
|
||||
return rb->tail->data;
|
||||
}
|
||||
|
||||
/* Mark the tail appended for `length` bytes, and move the cursor
|
||||
* to the next slot */
|
||||
void ringbuffer_write_append(ringbuffer *rb, int length) {
|
||||
assert(rb->used < RING_SLOTS);
|
||||
|
||||
rb->used++;
|
||||
|
||||
rb->tail->ptr = rb->tail->data;
|
||||
rb->tail->left = length;
|
||||
rb->tail = rb->tail->next;
|
||||
}
|
||||
|
||||
/** RING STATE FUNCTIONS **/
|
||||
|
||||
/* Used size of the ringbuffer */
|
||||
int ringbuffer_size(ringbuffer *rb) {
|
||||
return rb->used;
|
||||
}
|
||||
|
||||
/* Used size of the ringbuffer */
|
||||
int ringbuffer_capacity(ringbuffer *rb) {
|
||||
(void) rb;
|
||||
return RING_SLOTS;
|
||||
}
|
||||
|
||||
/* Is the ringbuffer completely empty (implies: no data to be written) */
|
||||
int ringbuffer_is_empty(ringbuffer *rb) {
|
||||
return rb->used == 0;
|
||||
}
|
||||
|
||||
/* Is the ringbuffer completely full (implies: no more data should be read) */
|
||||
int ringbuffer_is_full(ringbuffer *rb) {
|
||||
return rb->used == RING_SLOTS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright 2011 Bump Technologies, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BUMP TECHNOLOGIES, INC. ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BUMP TECHNOLOGIES, INC. OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of Bump Technologies, Inc.
|
||||
*
|
||||
**/
|
||||
|
||||
#ifndef RINGBUFFER_H
|
||||
#define RINGBUFFER_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* Tweak these for potential memory/throughput tradeoffs */
|
||||
#define RING_SLOTS 3
|
||||
#define RING_DATA_LEN 1024 * 32
|
||||
|
||||
typedef struct bufent {
|
||||
char data[RING_DATA_LEN];
|
||||
char *ptr;
|
||||
size_t left;
|
||||
struct bufent *next;
|
||||
} bufent;
|
||||
|
||||
typedef struct ringbuffer {
|
||||
bufent slots[RING_SLOTS];
|
||||
bufent *head; // reads from the head
|
||||
bufent *tail; // writes to the tail
|
||||
size_t used;
|
||||
} ringbuffer;
|
||||
|
||||
void ringbuffer_init(ringbuffer *rb);
|
||||
|
||||
char * ringbuffer_read_next(ringbuffer *rb, int * length);
|
||||
void ringbuffer_read_skip(ringbuffer *rb, int length);
|
||||
void ringbuffer_read_pop(ringbuffer *rb);
|
||||
|
||||
char * ringbuffer_write_ptr(ringbuffer *rb);
|
||||
void ringbuffer_write_append(ringbuffer *rb, int length);
|
||||
|
||||
int ringbuffer_size(ringbuffer *rb);
|
||||
int ringbuffer_capacity(ringbuffer *rb);
|
||||
int ringbuffer_is_empty(ringbuffer *rb);
|
||||
int ringbuffer_is_full(ringbuffer *rb);
|
||||
|
||||
#endif /* RINGBUFFER_H */
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* shctx.c
|
||||
*
|
||||
* Copyright (C) 2011 EXCELIANCE
|
||||
*
|
||||
* Author: Emeric Brun - emeric@exceliance.fr
|
||||
*
|
||||
*/
|
||||
#include <sys/mman.h>
|
||||
#ifdef USE_SYSCALL_FUTEX
|
||||
#include <unistd.h>
|
||||
#include <linux/futex.h>
|
||||
#include <sys/syscall.h>
|
||||
#else /* USE_SYSCALL_FUTEX */
|
||||
#include <pthread.h>
|
||||
#endif /* USE_SYSCALL_FUTEX */
|
||||
|
||||
#include "ebtree/ebmbtree.h"
|
||||
#include "shctx.h"
|
||||
|
||||
struct shared_session {
|
||||
struct ebmb_node key;
|
||||
unsigned char key_data[SSL_MAX_SSL_SESSION_ID_LENGTH];
|
||||
long c_date;
|
||||
int data_len;
|
||||
unsigned char data[SHSESS_MAX_DATA_LEN];
|
||||
struct shared_session *p;
|
||||
struct shared_session *n;
|
||||
};
|
||||
|
||||
|
||||
struct shared_context {
|
||||
#ifdef USE_SYSCALL_FUTEX
|
||||
unsigned int waiters;
|
||||
#else /* USE_SYSCALL_FUTEX */
|
||||
pthread_mutex_t mutex;
|
||||
#endif
|
||||
struct shared_session active;
|
||||
struct shared_session free;
|
||||
};
|
||||
|
||||
/* Static shared context */
|
||||
static struct shared_context *shctx = NULL;
|
||||
|
||||
/* Callbacks */
|
||||
static void (*shared_session_new_cbk)(unsigned char *session, unsigned int session_len, long cdate);
|
||||
|
||||
|
||||
/* Lock functions */
|
||||
#ifdef USE_SYSCALL_FUTEX
|
||||
static inline unsigned int xchg(unsigned int *ptr, unsigned int x)
|
||||
{
|
||||
__asm volatile("lock xchgl %0,%1"
|
||||
: "=r" (x), "+m" (*ptr)
|
||||
: "0" (x)
|
||||
: "memory");
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline unsigned int cmpxchg(unsigned int *ptr, unsigned int old, unsigned int new)
|
||||
{
|
||||
unsigned int ret;
|
||||
|
||||
__asm volatile("lock cmpxchgl %2,%1"
|
||||
: "=a" (ret), "+m" (*ptr)
|
||||
: "r" (new), "0" (old)
|
||||
: "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline unsigned char atomic_inc(unsigned int *ptr)
|
||||
{
|
||||
unsigned char ret;
|
||||
__asm volatile("lock incl %0\n"
|
||||
"setne %1\n"
|
||||
: "+m" (*ptr), "=qm" (ret)
|
||||
:
|
||||
: "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline unsigned char atomic_dec(unsigned int *ptr)
|
||||
{
|
||||
unsigned char ret;
|
||||
__asm volatile("lock decl %0\n"
|
||||
"setne %1\n"
|
||||
: "+m" (*ptr), "=qm" (ret)
|
||||
:
|
||||
: "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void shared_context_lock(void)
|
||||
{
|
||||
unsigned int x;
|
||||
|
||||
x = cmpxchg(&shctx->waiters, 0, 1);
|
||||
if (x) {
|
||||
if (x != 2)
|
||||
x = xchg(&shctx->waiters, 2);
|
||||
|
||||
while (x) {
|
||||
syscall(SYS_futex, &shctx->waiters, FUTEX_WAIT, 2, NULL, 0, 0);
|
||||
x = xchg(&shctx->waiters, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void shared_context_unlock(void)
|
||||
{
|
||||
if (atomic_dec(&shctx->waiters)) {
|
||||
shctx->waiters = 0;
|
||||
syscall(SYS_futex, &shctx->waiters, FUTEX_WAKE, 1, NULL, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* USE_SYSCALL_FUTEX */
|
||||
|
||||
#define shared_context_lock(v) pthread_mutex_lock(&shctx->mutex)
|
||||
#define shared_context_unlock(v) pthread_mutex_unlock(&shctx->mutex)
|
||||
|
||||
#endif
|
||||
|
||||
/* List Macros */
|
||||
|
||||
#define shsess_unset(s) (s)->n->p = (s)->p; \
|
||||
(s)->p->n = (s)->n;
|
||||
|
||||
#define shsess_set_free(s) shsess_unset(s) \
|
||||
(s)->p = &shctx->free; \
|
||||
(s)->n = shctx->free.n; \
|
||||
shctx->free.n->p = s; \
|
||||
shctx->free.n = s;
|
||||
|
||||
|
||||
#define shsess_set_active(s) shsess_unset(s) \
|
||||
(s)->p = &shctx->active; \
|
||||
(s)->n = shctx->active.n; \
|
||||
shctx->active.n->p = s; \
|
||||
shctx->active.n = s;
|
||||
|
||||
|
||||
#define shsess_get_next() (shctx->free.p == shctx->free.n) ? \
|
||||
shctx->active.p : shctx->free.p;
|
||||
|
||||
/* Tree Macros */
|
||||
|
||||
#define shsess_tree_delete(s) ebmb_delete(&(s)->key);
|
||||
|
||||
#define shsess_tree_insert(s) (struct shared_session *)ebmb_insert(&shctx->active.key.node.branches, \
|
||||
&(s)->key, SSL_MAX_SSL_SESSION_ID_LENGTH);
|
||||
|
||||
#define shsess_tree_lookup(k) (struct shared_session *)ebmb_lookup(&shctx->active.key.node.branches, \
|
||||
(k), SSL_MAX_SSL_SESSION_ID_LENGTH);
|
||||
|
||||
/* Other Macros */
|
||||
|
||||
#define shsess_set_key(s,k,l) { memcpy((s)->key_data, (k), (l)); \
|
||||
if ((l) < SSL_MAX_SSL_SESSION_ID_LENGTH) \
|
||||
memset((s)->key_data+(l), 0, SSL_MAX_SSL_SESSION_ID_LENGTH-(l)); };
|
||||
|
||||
|
||||
/* SSL context callbacks */
|
||||
|
||||
/* SSL callback used on new session creation */
|
||||
int shctx_new_cb(SSL *ssl, SSL_SESSION *sess) {
|
||||
(void)ssl;
|
||||
struct shared_session *shsess;
|
||||
unsigned char *data,*p;
|
||||
unsigned int data_len;
|
||||
unsigned char encsess[SHSESS_MAX_ENCODED_LEN];
|
||||
|
||||
/* check if session reserved size in aligned buffer is large enougth for the ASN1 encode session */
|
||||
data_len=i2d_SSL_SESSION(sess, NULL);
|
||||
if(data_len > SHSESS_MAX_DATA_LEN)
|
||||
return 1;
|
||||
|
||||
/* process ASN1 session encoding before the lock: lower cost */
|
||||
p = data = encsess+SSL_MAX_SSL_SESSION_ID_LENGTH;
|
||||
i2d_SSL_SESSION(sess, &p);
|
||||
|
||||
shared_context_lock();
|
||||
|
||||
shsess = shsess_get_next();
|
||||
|
||||
shsess_tree_delete(shsess);
|
||||
|
||||
shsess_set_key(shsess, sess->session_id, sess->session_id_length);
|
||||
|
||||
/* it returns the already existing node or current node if none, never returns null */
|
||||
shsess = shsess_tree_insert(shsess);
|
||||
|
||||
/* store ASN1 encoded session into cache */
|
||||
shsess->data_len = data_len;
|
||||
memcpy(shsess->data, data, data_len);
|
||||
|
||||
/* store creation date */
|
||||
shsess->c_date = SSL_SESSION_get_time(sess);
|
||||
|
||||
shsess_set_active(shsess);
|
||||
|
||||
shared_context_unlock();
|
||||
|
||||
if (shared_session_new_cbk) { /* if user level callback is set */
|
||||
|
||||
/* copy sessionid padded with 0 into the sessionid + data aligned buffer */
|
||||
memcpy(encsess, sess->session_id, sess->session_id_length);
|
||||
if (sess->session_id_length < SSL_MAX_SSL_SESSION_ID_LENGTH)
|
||||
memset(encsess+sess->session_id_length, 0, SSL_MAX_SSL_SESSION_ID_LENGTH-sess->session_id_length);
|
||||
|
||||
shared_session_new_cbk(encsess, SSL_MAX_SSL_SESSION_ID_LENGTH+data_len, SSL_SESSION_get_time(sess));
|
||||
}
|
||||
|
||||
return 0; /* do not increment session reference count */
|
||||
}
|
||||
|
||||
/* SSL callback used on lookup an existing session cause none found in internal cache */
|
||||
SSL_SESSION *shctx_get_cb(SSL *ssl, unsigned char *key, int key_len, int *do_copy) {
|
||||
(void)ssl;
|
||||
struct shared_session *shsess;
|
||||
unsigned char data[SHSESS_MAX_DATA_LEN], *p;
|
||||
unsigned char tmpkey[SSL_MAX_SSL_SESSION_ID_LENGTH];
|
||||
unsigned int data_len;
|
||||
long cdate;
|
||||
SSL_SESSION *sess;
|
||||
|
||||
/* allow the session to be freed automatically by openssl */
|
||||
*do_copy = 0;
|
||||
|
||||
/* tree key is zeros padded sessionid */
|
||||
if ( key_len < SSL_MAX_SSL_SESSION_ID_LENGTH ) {
|
||||
memcpy(tmpkey, key, key_len);
|
||||
memset(tmpkey+key_len, 0, SSL_MAX_SSL_SESSION_ID_LENGTH-key_len);
|
||||
key = tmpkey;
|
||||
}
|
||||
|
||||
/* lock cache */
|
||||
shared_context_lock();
|
||||
|
||||
/* lookup for session */
|
||||
shsess = shsess_tree_lookup(key);
|
||||
if(!shsess) {
|
||||
/* no session found: unlock cache and exit */
|
||||
shared_context_unlock();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* backup creation date to reset in session after ASN1 decode */
|
||||
cdate = shsess->c_date;
|
||||
|
||||
/* copy ASN1 session data to decode outside the lock */
|
||||
data_len = shsess->data_len;
|
||||
memcpy(data, shsess->data, shsess->data_len);
|
||||
|
||||
shsess_set_active(shsess);
|
||||
|
||||
shared_context_unlock();
|
||||
|
||||
/* decode ASN1 session */
|
||||
p = data;
|
||||
sess = d2i_SSL_SESSION(NULL, (const unsigned char **)&p, data_len);
|
||||
|
||||
/* reset creation date */
|
||||
if (sess)
|
||||
SSL_SESSION_set_time(sess, cdate);
|
||||
|
||||
return sess;
|
||||
}
|
||||
|
||||
/* SSL callback used to signal session is no more used in internal cache */
|
||||
void shctx_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess) {
|
||||
(void)ctx;
|
||||
struct shared_session *shsess;
|
||||
unsigned char tmpkey[SSL_MAX_SSL_SESSION_ID_LENGTH];
|
||||
unsigned char *key = sess->session_id;
|
||||
|
||||
/* tree key is zeros padded sessionid */
|
||||
if ( sess->session_id_length < SSL_MAX_SSL_SESSION_ID_LENGTH ) {
|
||||
memcpy(tmpkey, sess->session_id, sess->session_id_length);
|
||||
memset(tmpkey+sess->session_id_length, 0, SSL_MAX_SSL_SESSION_ID_LENGTH-sess->session_id_length);
|
||||
key = tmpkey;
|
||||
}
|
||||
|
||||
shared_context_lock();
|
||||
|
||||
/* lookup for session */
|
||||
shsess = shsess_tree_lookup(key);
|
||||
if ( shsess ) {
|
||||
shsess_set_free(shsess);
|
||||
}
|
||||
|
||||
/* unlock cache */
|
||||
shared_context_unlock();
|
||||
}
|
||||
|
||||
/* User level function called to add a session to the cache (remote updates) */
|
||||
void shctx_sess_add(const unsigned char *encsess, unsigned int len, long cdate) {
|
||||
struct shared_session *shsess;
|
||||
|
||||
/* check buffer is at least padded key long + 1 byte
|
||||
and data_len not too long */
|
||||
if ( (len <= SSL_MAX_SSL_SESSION_ID_LENGTH)
|
||||
|| (len > SHSESS_MAX_DATA_LEN+SSL_MAX_SSL_SESSION_ID_LENGTH) )
|
||||
return;
|
||||
|
||||
|
||||
shared_context_lock();
|
||||
|
||||
shsess = shsess_get_next();
|
||||
|
||||
shsess_tree_delete(shsess);
|
||||
|
||||
shsess_set_key(shsess, encsess, SSL_MAX_SSL_SESSION_ID_LENGTH);
|
||||
|
||||
/* it returns the already existing node or current node if none, never returns null */
|
||||
shsess = shsess_tree_insert(shsess);
|
||||
|
||||
/* store into cache and update earlier on session get events */
|
||||
if (cdate)
|
||||
shsess->c_date = (long)cdate;
|
||||
|
||||
/* copy ASN1 session data into cache */
|
||||
shsess->data_len = len-SSL_MAX_SSL_SESSION_ID_LENGTH;
|
||||
memcpy(shsess->data, encsess+SSL_MAX_SSL_SESSION_ID_LENGTH, shsess->data_len);
|
||||
|
||||
shsess_set_active(shsess);
|
||||
|
||||
shared_context_unlock();
|
||||
}
|
||||
|
||||
/* Function used to set a callback on new session creation */
|
||||
void shsess_set_new_cbk(void (*func)(unsigned char *, unsigned int, long)) {
|
||||
shared_session_new_cbk = func;
|
||||
}
|
||||
|
||||
/* Init shared memory context if not allocated and set SSL context callbacks
|
||||
* size is the max number of stored session
|
||||
* Returns: -1 on alloc failure, size if performs context alloc, and 0 if just perform
|
||||
* callbacks registration */
|
||||
int shared_context_init(SSL_CTX *ctx, int size)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!shctx) {
|
||||
int i;
|
||||
|
||||
#ifndef USE_SYSCALL_FUTEX
|
||||
pthread_mutexattr_t attr;
|
||||
#endif /* USE_SYSCALL_FUTEX */
|
||||
struct shared_session *prev,*cur;
|
||||
|
||||
shctx = (struct shared_context *)mmap(NULL, sizeof(struct shared_context)+(size*sizeof(struct shared_session)),
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
if (!shctx || shctx == MAP_FAILED)
|
||||
return -1;
|
||||
|
||||
#ifdef USE_SYSCALL_FUTEX
|
||||
shctx->waiters = 0;
|
||||
#else
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
|
||||
pthread_mutex_init(&shctx->mutex, &attr);
|
||||
#endif
|
||||
memset(&shctx->active.key, 0, sizeof(struct ebmb_node));
|
||||
memset(&shctx->free.key, 0, sizeof(struct ebmb_node));
|
||||
|
||||
/* No duplicate authorized in tree: */
|
||||
shctx->active.key.node.branches.b[1] = (void *)1;
|
||||
|
||||
cur = &shctx->active;
|
||||
cur->n = cur->p = cur;
|
||||
|
||||
cur = &shctx->free;
|
||||
for ( i = 0 ; i < size ; i++) {
|
||||
prev = cur;
|
||||
cur = (struct shared_session *)((char *)prev + sizeof(struct shared_session));
|
||||
prev->n = cur;
|
||||
cur->p = prev;
|
||||
}
|
||||
cur->n = &shctx->free;
|
||||
shctx->free.p = cur;
|
||||
|
||||
ret = size;
|
||||
}
|
||||
|
||||
/* set SSL internal cache size to external cache / 8 + 123 */
|
||||
SSL_CTX_sess_set_cache_size(ctx, size >> 3 | 0x3ff);
|
||||
|
||||
/* Set callbacks */
|
||||
SSL_CTX_sess_set_new_cb(ctx, shctx_new_cb);
|
||||
SSL_CTX_sess_set_get_cb(ctx, shctx_get_cb);
|
||||
SSL_CTX_sess_set_remove_cb(ctx, shctx_remove_cb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* shctx.h
|
||||
*
|
||||
* Copyright (C) 2011 EXCELIANCE
|
||||
*
|
||||
* Author: Emeric Brun - emeric@exceliance.fr
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHCTX_H
|
||||
#define SHCTX_H
|
||||
#include <openssl/ssl.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef SHSESS_MAX_FOOTER_LEN
|
||||
#define SHSESS_MAX_FOOTER_LEN sizeof(uint32_t) \
|
||||
+ EVP_MAX_MD_SIZE
|
||||
#endif
|
||||
|
||||
#ifndef SHSESS_MAX_DATA_LEN
|
||||
#define SHSESS_MAX_DATA_LEN 512
|
||||
#endif
|
||||
|
||||
#define SHSESS_MAX_ENCODED_LEN SSL_MAX_SSL_SESSION_ID_LENGTH \
|
||||
+ SHSESS_MAX_DATA_LEN \
|
||||
+ SHSESS_MAX_FOOTER_LEN
|
||||
|
||||
|
||||
/* Callback called on a new session event:
|
||||
* session contains the sessionid zeros padded to SSL_MAX_SSL_SESSION_ID_LENGTH
|
||||
* followed by ASN1 session encoding.
|
||||
* len is set to SSL_MAX_SSL_SESSION_ID_LENGTH + ASN1 session length
|
||||
* len is always less than SSL_MAX_SSL_SESSION_ID_LENGTH + SHSESS_MAX_DATA_LEN.
|
||||
* Remaining Bytes from len to SHSESS_MAX_ENCODED_LEN can be used to add a footer.
|
||||
* cdate is the creation date timestamp.
|
||||
*/
|
||||
void shsess_set_new_cbk(void (*func)(unsigned char *session, unsigned int len, long cdate));
|
||||
|
||||
/* Add a session into the cache,
|
||||
* session contains the sessionid zeros padded to SSL_MAX_SSL_SESSION_ID_LENGTH
|
||||
* followed by ASN1 session encoding.
|
||||
* len is set to SSL_MAX_SSL_SESSION_ID_LENGTH + ASN1 data length.
|
||||
* if len greater than SHSESS_MAX_ENCODED_LEN, session is not added.
|
||||
* if cdate not 0, on get events session creation date will be reset to cdate */
|
||||
void shctx_sess_add(const unsigned char *session, unsigned int session_len, long cdate);
|
||||
|
||||
/* Init shared memory context if not allocated and set SSL context callbacks
|
||||
* size is the max number of stored session
|
||||
* Returns: -1 on alloc failure, size if performs context alloc, and 0 if just
|
||||
* perform callbacks registration */
|
||||
int shared_context_init(SSL_CTX *ctx, int size);
|
||||
|
||||
#endif /* SHCTX_H */
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
.\" Copyright (c) 2011 Vincent Bernat <bernat@luffy.cx>
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without modification, are
|
||||
.\" permitted provided that the following conditions are met:
|
||||
.\"
|
||||
.\" 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
.\" conditions and the following disclaimer.
|
||||
.\"
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
.\" of conditions and the following disclaimer in the documentation and/or other materials
|
||||
.\" provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY BUMP TECHNOLOGIES, INC. ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
.\" FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BUMP TECHNOLOGIES, INC. OR
|
||||
.\" CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
.\" ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
.\" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.\" The views and conclusions contained in the software and documentation are those of the
|
||||
.\" authors and should not be interpreted as representing official policies, either expressed
|
||||
.\" or implied, of Bump Technologies, Inc.
|
||||
.\"
|
||||
.Dd $Mdocdate: September 23 2011 $
|
||||
.Dt STUD 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm stud
|
||||
.Nd The Scalable TLS Unwrapping Daemon
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl -tls
|
||||
.Op Fl -ssl
|
||||
.Op Fl c Ar ciphers
|
||||
.Op Fl e Ar engine
|
||||
.Op Fl b Ar host,port
|
||||
.Op Fl f Ar host,port
|
||||
.Op Fl n Ar cores
|
||||
.Op Fl B Ar backlog
|
||||
.Op Fl C Ar cache
|
||||
.Op Fl r Ar path
|
||||
.Op Fl u Ar username
|
||||
.Op Fl qs
|
||||
.Op Fl -write-ip
|
||||
.Op Fl -write-proxy
|
||||
.Ar certificate.pem
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is a network proxy that terminates TLS/SSL connections and forwards the
|
||||
unencrypted traffic to some backend. It's designed to handle 10s of thousands of
|
||||
connections efficiently on multicore machines.
|
||||
.Pp
|
||||
.Nm
|
||||
has very few features -- it's designed to be paired with an intelligent
|
||||
backend like haproxy or nginx. It maintains a strict 1:1 connection pattern
|
||||
with this backend handler so that the backend can dictate throttling behavior,
|
||||
maxmium connection behavior, availability of service, etc.
|
||||
.Pp
|
||||
The only required argument is a path to a PEM file that contains the certificate
|
||||
(or a chain of certificates) and private key. It should also contain
|
||||
DH parameter if you wish to use Diffie-Hellman cipher suites.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl -tls
|
||||
Use TLSv1 (default).
|
||||
.It Fl -ssl
|
||||
Use only SSLv3 and no TLSv1.
|
||||
.It Fl c Ar ciphers
|
||||
Set allowed ciphers using the same format as
|
||||
.Ic openssl ciphers .
|
||||
For example, you can use
|
||||
.Ar RSA:!COMPLEMENTOFALL .
|
||||
.It Fl e Ar engine
|
||||
Specify an OpenSSL engine by its unique ID. The engine will be used by
|
||||
default for all algorithms. The keyword
|
||||
.Ar auto
|
||||
can be used to load all available engines.
|
||||
.It Fl b Ar host,port
|
||||
Define backend. Default is
|
||||
.Ar 127.0.0.1,8000 .
|
||||
Incoming connections will be unwrapped and sent to this IP and port.
|
||||
.It Fl f Ar host,port
|
||||
Define frontend. Default is
|
||||
.Ar *,8443 .
|
||||
Incoming connections will be accepted to this IP and port and will be
|
||||
sent to the backend defined above.
|
||||
.It Fl n Ar cores
|
||||
Use
|
||||
.Ar cores
|
||||
worker processes. Default is 1.
|
||||
.It Fl B Ar backlog
|
||||
Set listen backlog size. Default is 100.
|
||||
.It Fl C Ar cache
|
||||
Set shared cache size in sessions. By default, no shared cache is used.
|
||||
.It Fl r Ar path
|
||||
Chroot to the given path. By default, no chroot is done.
|
||||
.It Fl u Ar username
|
||||
Set GID/UID after binding the socket. By default, no privilege is dropped.
|
||||
.It Fl q
|
||||
Be quiet. Only emit error messages.
|
||||
.It Fl s
|
||||
Send messages to syslog in addition to
|
||||
.Em stderr
|
||||
and
|
||||
.Em stdout .
|
||||
.It Fl -syslog-facility Ar facility
|
||||
Syslog facility to use. Default is
|
||||
.Ar daemon .
|
||||
.It Fl -write-ip
|
||||
Write 1 octet with the IP family followed by the IP address in 4
|
||||
(IPv4) or 16 (IPv6) octets little-endian to backend before the actual
|
||||
data.
|
||||
.It Fl -write-proxy
|
||||
Write HaProxy's PROXY (IPv4 or IPv6) protocol line
|
||||
before actual data.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr ciphers 1SSL ,
|
||||
.Xr dhparam 1SSL ,
|
||||
.Xr haproxy 1
|
||||
.Sh AUTHORS
|
||||
.Nm
|
||||
was originally written by Jamie Turner (@jamwt) and is maintained by
|
||||
the Bump server team. It currently provides server-side TLS
|
||||
termination for over 40 million Bump users.
|
||||
+1878
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
#define STUD_VERSION "0.3-dev"
|
||||
Reference in New Issue
Block a user