mirror of
https://github.com/skeeto/endlessh.git
synced 2025-12-10 15:05:35 +00:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfe44eb2c5 | ||
|
|
1ecaafd577 | ||
|
|
a5913cbbb2 | ||
|
|
4cb4fc6eac | ||
|
|
8daa5992f1 | ||
|
|
ad7031f79a | ||
|
|
5b7dc86a47 | ||
|
|
e4f8c9f8f4 | ||
|
|
585a4b1d96 | ||
|
|
2602caa459 | ||
|
|
715f30c3a7 | ||
|
|
3d6aec6080 | ||
|
|
ae7473536e | ||
|
|
33dff0cfc9 | ||
|
|
f465f2dcbb | ||
|
|
df0ffbf629 | ||
|
|
b2c811ecf7 | ||
|
|
a154fcaf43 | ||
|
|
6b721e58ac | ||
|
|
8ec96ea899 | ||
|
|
44b3285bb2 | ||
|
|
4321fe93e5 | ||
|
|
964a860634 | ||
|
|
8794f02d22 |
3
Makefile
3
Makefile
@@ -1,6 +1,7 @@
|
|||||||
.POSIX:
|
.POSIX:
|
||||||
CC = cc
|
CC = cc
|
||||||
CFLAGS = -std=c99 -Wall -Wextra -Wno-missing-field-initializers -Os
|
CFLAGS = -std=c99 -Wall -Wextra -Wno-missing-field-initializers -Os
|
||||||
|
CPPFLAGS =
|
||||||
LDFLAGS = -ggdb3
|
LDFLAGS = -ggdb3
|
||||||
LDLIBS =
|
LDLIBS =
|
||||||
PREFIX = /usr/local
|
PREFIX = /usr/local
|
||||||
@@ -8,7 +9,7 @@ PREFIX = /usr/local
|
|||||||
all: endlessh
|
all: endlessh
|
||||||
|
|
||||||
endlessh: endlessh.c
|
endlessh: endlessh.c
|
||||||
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ endlessh.c $(LDLIBS)
|
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ endlessh.c $(LDLIBS)
|
||||||
|
|
||||||
install: endlessh
|
install: endlessh
|
||||||
install -d $(DESTDIR)$(PREFIX)/bin
|
install -d $(DESTDIR)$(PREFIX)/bin
|
||||||
|
|||||||
31
README.md
31
README.md
@@ -11,14 +11,12 @@ occurs, this program doesn't depend on any cryptographic libraries. It's
|
|||||||
a simple, single-threaded, standalone C program. It uses `poll()` to
|
a simple, single-threaded, standalone C program. It uses `poll()` to
|
||||||
trap multiple clients at a time.
|
trap multiple clients at a time.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Usage information is printed with `-h`.
|
Usage information is printed with `-h`.
|
||||||
|
|
||||||
```
|
```
|
||||||
Usage: endlessh [-vh] [-d MS] [-f CONFIG] [-l LEN] [-m LIMIT] [-p PORT]
|
Usage: endlessh [-vhs] [-d MS] [-f CONFIG] [-l LEN] [-m LIMIT] [-p PORT]
|
||||||
-4 Bind to IPv4 only
|
-4 Bind to IPv4 only
|
||||||
-6 Bind to IPv6 only
|
-6 Bind to IPv6 only
|
||||||
-d INT Message millisecond delay [10000]
|
-d INT Message millisecond delay [10000]
|
||||||
@@ -27,7 +25,8 @@ Usage: endlessh [-vh] [-d MS] [-f CONFIG] [-l LEN] [-m LIMIT] [-p PORT]
|
|||||||
-l INT Maximum banner line length (3-255) [32]
|
-l INT Maximum banner line length (3-255) [32]
|
||||||
-m INT Maximum number of clients [4096]
|
-m INT Maximum number of clients [4096]
|
||||||
-p INT Listening port [2222]
|
-p INT Listening port [2222]
|
||||||
-v Print diagnostics to standard output (repeatable)
|
-s Print diagnostics to syslog instead of standard output
|
||||||
|
-v Print diagnostics (repeatable)
|
||||||
```
|
```
|
||||||
|
|
||||||
Argument order matters. The configuration file is loaded when the `-f`
|
Argument order matters. The configuration file is loaded when the `-f`
|
||||||
@@ -36,7 +35,8 @@ configuration file.
|
|||||||
|
|
||||||
By default no log messages are produced. The first `-v` enables basic
|
By default no log messages are produced. The first `-v` enables basic
|
||||||
logging and a second `-v` enables debugging logging (noisy). All log
|
logging and a second `-v` enables debugging logging (noisy). All log
|
||||||
messages are sent to standard output.
|
messages are sent to standard output by default. `-s` causes them to be
|
||||||
|
sent to syslog.
|
||||||
|
|
||||||
endlessh -v >endlessh.log 2>endlessh.err
|
endlessh -v >endlessh.log 2>endlessh.err
|
||||||
|
|
||||||
@@ -108,5 +108,26 @@ to remove GCC-specific options. For example, on Solaris:
|
|||||||
The feature test macros on these systems isn't reliable, so you may also
|
The feature test macros on these systems isn't reliable, so you may also
|
||||||
need to use `-D__EXTENSIONS__` in `CFLAGS`.
|
need to use `-D__EXTENSIONS__` in `CFLAGS`.
|
||||||
|
|
||||||
|
### OpenBSD
|
||||||
|
|
||||||
|
The man page needs to go into a different path for OpenBSD's `man` command:
|
||||||
|
|
||||||
|
```
|
||||||
|
diff --git a/Makefile b/Makefile
|
||||||
|
index 119347a..dedf69d 100644
|
||||||
|
--- a/Makefile
|
||||||
|
+++ b/Makefile
|
||||||
|
@@ -14,8 +14,8 @@ endlessh: endlessh.c
|
||||||
|
install: endlessh
|
||||||
|
install -d $(DESTDIR)$(PREFIX)/bin
|
||||||
|
install -m 755 endlessh $(DESTDIR)$(PREFIX)/bin/
|
||||||
|
- install -d $(DESTDIR)$(PREFIX)/share/man/man1
|
||||||
|
- install -m 644 endlessh.1 $(DESTDIR)$(PREFIX)/share/man/man1/
|
||||||
|
+ install -d $(DESTDIR)$(PREFIX)/man/man1
|
||||||
|
+ install -m 644 endlessh.1 $(DESTDIR)$(PREFIX)/man/man1/
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf endlessh
|
||||||
|
```
|
||||||
|
|
||||||
[np]: https://nullprogram.com/blog/2019/03/22/
|
[np]: https://nullprogram.com/blog/2019/03/22/
|
||||||
|
|||||||
13
endlessh.1
13
endlessh.1
@@ -1,4 +1,4 @@
|
|||||||
.Dd $Mdocdate: April 12 2019 $
|
.Dd $Mdocdate: January 29 2020 $
|
||||||
.Dt ENDLESSH 1
|
.Dt ENDLESSH 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
.Nd An SSH tarpit
|
.Nd An SSH tarpit
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm endless
|
.Nm endless
|
||||||
.Op Fl 46chvV
|
.Op Fl 46chsvV
|
||||||
.Op Fl d Ar delay
|
.Op Fl d Ar delay
|
||||||
.Op Fl f Ar config
|
.Op Fl f Ar config
|
||||||
.Op Fl l Ar max banner length
|
.Op Fl l Ar max banner length
|
||||||
@@ -55,15 +55,17 @@ Maximum number of clients. Default: 4096
|
|||||||
Set the listening port. By default
|
Set the listening port. By default
|
||||||
.Nm
|
.Nm
|
||||||
listens on port 2222.
|
listens on port 2222.
|
||||||
|
.It Fl s
|
||||||
|
Print diagnostics to syslog. By default
|
||||||
|
.Nm
|
||||||
|
prints them to standard output.
|
||||||
.It Fl v
|
.It Fl v
|
||||||
Print diagnostics to standard output. Can be specified
|
Print diagnostics. Can be specified up to twice to increase verbosity.
|
||||||
numerous times to increase verbosity.
|
|
||||||
.It Fl V
|
.It Fl V
|
||||||
Causes
|
Causes
|
||||||
.Nm
|
.Nm
|
||||||
to print version information and exit.
|
to print version information and exit.
|
||||||
.El
|
.El
|
||||||
.El
|
|
||||||
.Pp
|
.Pp
|
||||||
If
|
If
|
||||||
.Nm
|
.Nm
|
||||||
@@ -79,3 +81,4 @@ A SIGUSR1 signal will print connections stats to the log.
|
|||||||
The default
|
The default
|
||||||
.Nm
|
.Nm
|
||||||
configuration file.
|
configuration file.
|
||||||
|
.El
|
||||||
|
|||||||
140
endlessh.c
140
endlessh.c
@@ -1,4 +1,13 @@
|
|||||||
#define _XOPEN_SOURCE 600
|
/* Endlessh: an SSH tarpit
|
||||||
|
*
|
||||||
|
* This is free and unencumbered software released into the public domain.
|
||||||
|
*/
|
||||||
|
#if defined(__OpenBSD__)
|
||||||
|
# define _BSD_SOURCE /* for pledge(2) and unveil(2) */
|
||||||
|
#else
|
||||||
|
# define _XOPEN_SOURCE 600
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -15,14 +24,21 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
|
||||||
#define ENDLESSH_VERSION 1.0
|
#define ENDLESSH_VERSION 1.1
|
||||||
|
|
||||||
#define DEFAULT_PORT 2222
|
#define DEFAULT_PORT 2222
|
||||||
#define DEFAULT_DELAY 10000 /* milliseconds */
|
#define DEFAULT_DELAY 10000 /* milliseconds */
|
||||||
#define DEFAULT_MAX_LINE_LENGTH 32
|
#define DEFAULT_MAX_LINE_LENGTH 32
|
||||||
#define DEFAULT_MAX_CLIENTS 4096
|
#define DEFAULT_MAX_CLIENTS 4096
|
||||||
#define DEFAULT_CONFIG_FILE "/etc/endlessh/config"
|
|
||||||
|
#if defined(__FreeBSD__)
|
||||||
|
# define DEFAULT_CONFIG_FILE "/usr/local/etc/endlessh.config"
|
||||||
|
#else
|
||||||
|
# define DEFAULT_CONFIG_FILE "/etc/endlessh/config"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define DEFAULT_BIND_FAMILY AF_UNSPEC
|
#define DEFAULT_BIND_FAMILY AF_UNSPEC
|
||||||
|
|
||||||
#define XSTR(s) STR(s)
|
#define XSTR(s) STR(s)
|
||||||
@@ -37,13 +53,15 @@ epochms(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static enum loglevel {
|
static enum loglevel {
|
||||||
LOG_NONE,
|
log_none,
|
||||||
LOG_INFO,
|
log_info,
|
||||||
LOG_DEBUG
|
log_debug
|
||||||
} loglevel = LOG_NONE;
|
} loglevel = log_none;
|
||||||
|
|
||||||
|
static void (*logmsg)(enum loglevel level, const char *, ...);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
logmsg(enum loglevel level, const char *format, ...)
|
logstdio(enum loglevel level, const char *format, ...)
|
||||||
{
|
{
|
||||||
if (loglevel >= level) {
|
if (loglevel >= level) {
|
||||||
int save = errno;
|
int save = errno;
|
||||||
@@ -67,7 +85,27 @@ logmsg(enum loglevel level, const char *format, ...)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct {
|
static void
|
||||||
|
logsyslog(enum loglevel level, const char *format, ...)
|
||||||
|
{
|
||||||
|
static const int prio_map[] = { LOG_NOTICE, LOG_INFO, LOG_DEBUG };
|
||||||
|
|
||||||
|
if (loglevel >= level) {
|
||||||
|
int save = errno;
|
||||||
|
|
||||||
|
/* Output the log message */
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
char buf[256];
|
||||||
|
vsnprintf(buf, sizeof buf, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
syslog(prio_map[level], "%s", buf);
|
||||||
|
|
||||||
|
errno = save;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct {
|
||||||
long long connects;
|
long long connects;
|
||||||
long long milliseconds;
|
long long milliseconds;
|
||||||
long long bytes_sent;
|
long long bytes_sent;
|
||||||
@@ -101,9 +139,9 @@ client_new(int fd, long long send_next)
|
|||||||
*/
|
*/
|
||||||
int value = 1;
|
int value = 1;
|
||||||
int r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value));
|
int r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value));
|
||||||
logmsg(LOG_DEBUG, "setsockopt(%d, SO_RCVBUF, %d) = %d", fd, value, r);
|
logmsg(log_debug, "setsockopt(%d, SO_RCVBUF, %d) = %d", fd, value, r);
|
||||||
if (r == -1)
|
if (r == -1)
|
||||||
logmsg(LOG_DEBUG, "errno = %d, %s", errno, strerror(errno));
|
logmsg(log_debug, "errno = %d, %s", errno, strerror(errno));
|
||||||
|
|
||||||
/* Get IP address */
|
/* Get IP address */
|
||||||
struct sockaddr_storage addr;
|
struct sockaddr_storage addr;
|
||||||
@@ -128,9 +166,9 @@ client_new(int fd, long long send_next)
|
|||||||
static void
|
static void
|
||||||
client_destroy(struct client *client)
|
client_destroy(struct client *client)
|
||||||
{
|
{
|
||||||
logmsg(LOG_DEBUG, "close(%d)", client->fd);
|
logmsg(log_debug, "close(%d)", client->fd);
|
||||||
long long dt = epochms() - client->connect_time;
|
long long dt = epochms() - client->connect_time;
|
||||||
logmsg(LOG_INFO,
|
logmsg(log_info,
|
||||||
"CLOSE host=%s port=%d fd=%d "
|
"CLOSE host=%s port=%d fd=%d "
|
||||||
"time=%lld.%03lld bytes=%lld",
|
"time=%lld.%03lld bytes=%lld",
|
||||||
client->ipaddr, client->port, client->fd,
|
client->ipaddr, client->port, client->fd,
|
||||||
@@ -147,7 +185,7 @@ statistics_log_totals(struct client *clients)
|
|||||||
long long milliseconds = statistics.milliseconds;
|
long long milliseconds = statistics.milliseconds;
|
||||||
for (long long now = epochms(); clients; clients = clients->next)
|
for (long long now = epochms(); clients; clients = clients->next)
|
||||||
milliseconds += now - clients->connect_time;
|
milliseconds += now - clients->connect_time;
|
||||||
logmsg(LOG_INFO, "TOTALS connects=%lld seconds=%lld.%03lld bytes=%lld",
|
logmsg(log_info, "TOTALS connects=%lld seconds=%lld.%03lld bytes=%lld",
|
||||||
statistics.connects,
|
statistics.connects,
|
||||||
milliseconds / 1000,
|
milliseconds / 1000,
|
||||||
milliseconds % 1000,
|
milliseconds % 1000,
|
||||||
@@ -447,7 +485,7 @@ config_load(struct config *c, const char *file, int hardfail)
|
|||||||
errno = 0;
|
errno = 0;
|
||||||
char *end;
|
char *end;
|
||||||
long v = strtol(tokens[1], &end, 10);
|
long v = strtol(tokens[1], &end, 10);
|
||||||
if (errno || *end || v < LOG_NONE || v > LOG_DEBUG) {
|
if (errno || *end || v < log_none || v > log_debug) {
|
||||||
fprintf(stderr, "%s:%ld: Invalid log level '%s'\n",
|
fprintf(stderr, "%s:%ld: Invalid log level '%s'\n",
|
||||||
file, lineno, tokens[1]);
|
file, lineno, tokens[1]);
|
||||||
if (hardfail) exit(EXIT_FAILURE);
|
if (hardfail) exit(EXIT_FAILURE);
|
||||||
@@ -465,11 +503,11 @@ config_load(struct config *c, const char *file, int hardfail)
|
|||||||
static void
|
static void
|
||||||
config_log(const struct config *c)
|
config_log(const struct config *c)
|
||||||
{
|
{
|
||||||
logmsg(LOG_INFO, "Port %d", c->port);
|
logmsg(log_info, "Port %d", c->port);
|
||||||
logmsg(LOG_INFO, "Delay %ld", c->delay);
|
logmsg(log_info, "Delay %d", c->delay);
|
||||||
logmsg(LOG_INFO, "MaxLineLength %d", c->max_line_length);
|
logmsg(log_info, "MaxLineLength %d", c->max_line_length);
|
||||||
logmsg(LOG_INFO, "MaxClients %d", c->max_clients);
|
logmsg(log_info, "MaxClients %d", c->max_clients);
|
||||||
logmsg(LOG_INFO, "BindFamily %s",
|
logmsg(log_info, "BindFamily %s",
|
||||||
c->bind_family == AF_INET6 ? "IPv6 Only" :
|
c->bind_family == AF_INET6 ? "IPv6 Only" :
|
||||||
c->bind_family == AF_INET ? "IPv4 Only" :
|
c->bind_family == AF_INET ? "IPv4 Only" :
|
||||||
"IPv4 Mapped IPv6");
|
"IPv4 Mapped IPv6");
|
||||||
@@ -509,15 +547,15 @@ server_create(int port, int family)
|
|||||||
int r, s, value;
|
int r, s, value;
|
||||||
|
|
||||||
s = socket(family == AF_UNSPEC ? AF_INET6 : family, SOCK_STREAM, 0);
|
s = socket(family == AF_UNSPEC ? AF_INET6 : family, SOCK_STREAM, 0);
|
||||||
logmsg(LOG_DEBUG, "socket() = %d", s);
|
logmsg(log_debug, "socket() = %d", s);
|
||||||
if (s == -1) die();
|
if (s == -1) die();
|
||||||
|
|
||||||
/* Socket options are best effort, allowed to fail */
|
/* Socket options are best effort, allowed to fail */
|
||||||
value = 1;
|
value = 1;
|
||||||
r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
|
r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
|
||||||
logmsg(LOG_DEBUG, "setsockopt(%d, SO_REUSEADDR, true) = %d", s, r);
|
logmsg(log_debug, "setsockopt(%d, SO_REUSEADDR, true) = %d", s, r);
|
||||||
if (r == -1)
|
if (r == -1)
|
||||||
logmsg(LOG_DEBUG, "errno = %d, %s", errno, strerror(errno));
|
logmsg(log_debug, "errno = %d, %s", errno, strerror(errno));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* With OpenBSD IPv6 sockets are always IPv6-only, so the socket option
|
* With OpenBSD IPv6 sockets are always IPv6-only, so the socket option
|
||||||
@@ -529,9 +567,9 @@ server_create(int port, int family)
|
|||||||
errno = 0;
|
errno = 0;
|
||||||
value = (family == AF_INET6);
|
value = (family == AF_INET6);
|
||||||
r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));
|
r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));
|
||||||
logmsg(LOG_DEBUG, "setsockopt(%d, IPV6_V6ONLY, true) = %d", s, r);
|
logmsg(log_debug, "setsockopt(%d, IPV6_V6ONLY, true) = %d", s, r);
|
||||||
if (r == -1)
|
if (r == -1)
|
||||||
logmsg(LOG_DEBUG, "errno = %d, %s", errno, strerror(errno));
|
logmsg(log_debug, "errno = %d, %s", errno, strerror(errno));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -550,11 +588,11 @@ server_create(int port, int family)
|
|||||||
};
|
};
|
||||||
r = bind(s, (void *)&addr6, sizeof(addr6));
|
r = bind(s, (void *)&addr6, sizeof(addr6));
|
||||||
}
|
}
|
||||||
logmsg(LOG_DEBUG, "bind(%d, port=%d) = %d", s, port, r);
|
logmsg(log_debug, "bind(%d, port=%d) = %d", s, port, r);
|
||||||
if (r == -1) die();
|
if (r == -1) die();
|
||||||
|
|
||||||
r = listen(s, INT_MAX);
|
r = listen(s, INT_MAX);
|
||||||
logmsg(LOG_DEBUG, "listen(%d) = %d", s, r);
|
logmsg(log_debug, "listen(%d) = %d", s, r);
|
||||||
if (r == -1) die();
|
if (r == -1) die();
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
@@ -568,7 +606,7 @@ sendline(struct client *client, int max_line_length, unsigned long *rng)
|
|||||||
int len = randline(line, max_line_length, rng);
|
int len = randline(line, max_line_length, rng);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ssize_t out = write(client->fd, line, len);
|
ssize_t out = write(client->fd, line, len);
|
||||||
logmsg(LOG_DEBUG, "write(%d) = %d", client->fd, (int)out);
|
logmsg(log_debug, "write(%d) = %d", client->fd, (int)out);
|
||||||
if (out == -1) {
|
if (out == -1) {
|
||||||
if (errno == EINTR) {
|
if (errno == EINTR) {
|
||||||
continue; /* try again */
|
continue; /* try again */
|
||||||
@@ -590,12 +628,20 @@ sendline(struct client *client, int max_line_length, unsigned long *rng)
|
|||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
logmsg = logstdio;
|
||||||
struct config config = CONFIG_DEFAULT;
|
struct config config = CONFIG_DEFAULT;
|
||||||
const char *config_file = DEFAULT_CONFIG_FILE;
|
const char *config_file = DEFAULT_CONFIG_FILE;
|
||||||
|
|
||||||
|
#if defined(__OpenBSD__)
|
||||||
|
unveil(config_file, "r"); /* return ignored as the file may not exist */
|
||||||
|
if (pledge("inet stdio rpath unveil", 0) == -1)
|
||||||
|
die();
|
||||||
|
#endif
|
||||||
|
|
||||||
config_load(&config, config_file, 1);
|
config_load(&config, config_file, 1);
|
||||||
|
|
||||||
int option;
|
int option;
|
||||||
while ((option = getopt(argc, argv, "46d:f:hl:m:p:vV")) != -1) {
|
while ((option = getopt(argc, argv, "46d:f:hl:m:p:svV")) != -1) {
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case '4':
|
case '4':
|
||||||
config_set_bind_family(&config, "4", 1);
|
config_set_bind_family(&config, "4", 1);
|
||||||
@@ -608,6 +654,13 @@ main(int argc, char **argv)
|
|||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
config_file = optarg;
|
config_file = optarg;
|
||||||
|
|
||||||
|
#if defined(__OpenBSD__)
|
||||||
|
unveil(config_file, "r");
|
||||||
|
if (unveil(0, 0) == -1)
|
||||||
|
die();
|
||||||
|
#endif
|
||||||
|
|
||||||
config_load(&config, optarg, 1);
|
config_load(&config, optarg, 1);
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
@@ -623,7 +676,11 @@ main(int argc, char **argv)
|
|||||||
case 'p':
|
case 'p':
|
||||||
config_set_port(&config, optarg, 1);
|
config_set_port(&config, optarg, 1);
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
logmsg = logsyslog;
|
||||||
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
|
if (loglevel < log_debug)
|
||||||
loglevel++;
|
loglevel++;
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
@@ -641,8 +698,15 @@ main(int argc, char **argv)
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (logmsg == logsyslog) {
|
||||||
|
/* Prepare the syslog */
|
||||||
|
const char *prog = strrchr(argv[0], '/');
|
||||||
|
prog = prog ? prog + 1 : argv[0];
|
||||||
|
openlog(prog, LOG_PID, LOG_DAEMON);
|
||||||
|
} else {
|
||||||
/* Set output (log) to line buffered */
|
/* Set output (log) to line buffered */
|
||||||
setvbuf(stdout, 0, _IOLBF, 0);
|
setvbuf(stdout, 0, _IOLBF, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Log configuration */
|
/* Log configuration */
|
||||||
config_log(&config);
|
config_log(&config);
|
||||||
@@ -713,13 +777,13 @@ main(int argc, char **argv)
|
|||||||
/* Wait for next event */
|
/* Wait for next event */
|
||||||
struct pollfd fds = {server, POLLIN, 0};
|
struct pollfd fds = {server, POLLIN, 0};
|
||||||
int nfds = fifo->length < config.max_clients;
|
int nfds = fifo->length < config.max_clients;
|
||||||
logmsg(LOG_DEBUG, "poll(%d, %d)", nfds, timeout);
|
logmsg(log_debug, "poll(%d, %d)", nfds, timeout);
|
||||||
int r = poll(&fds, nfds, timeout);
|
int r = poll(&fds, nfds, timeout);
|
||||||
logmsg(LOG_DEBUG, "= %d", r);
|
logmsg(log_debug, "= %d", r);
|
||||||
if (r == -1) {
|
if (r == -1) {
|
||||||
switch (errno) {
|
switch (errno) {
|
||||||
case EINTR:
|
case EINTR:
|
||||||
logmsg(LOG_DEBUG, "EINTR");
|
logmsg(log_debug, "EINTR");
|
||||||
continue;
|
continue;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "endlessh: fatal: %s\n", strerror(errno));
|
fprintf(stderr, "endlessh: fatal: %s\n", strerror(errno));
|
||||||
@@ -730,7 +794,7 @@ main(int argc, char **argv)
|
|||||||
/* Check for new incoming connections */
|
/* Check for new incoming connections */
|
||||||
if (fds.revents & POLLIN) {
|
if (fds.revents & POLLIN) {
|
||||||
int fd = accept(server, 0, 0);
|
int fd = accept(server, 0, 0);
|
||||||
logmsg(LOG_DEBUG, "accept() = %d", fd);
|
logmsg(log_debug, "accept() = %d", fd);
|
||||||
statistics.connects++;
|
statistics.connects++;
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
const char *msg = strerror(errno);
|
const char *msg = strerror(errno);
|
||||||
@@ -738,7 +802,7 @@ main(int argc, char **argv)
|
|||||||
case EMFILE:
|
case EMFILE:
|
||||||
case ENFILE:
|
case ENFILE:
|
||||||
config.max_clients = fifo->length;
|
config.max_clients = fifo->length;
|
||||||
logmsg(LOG_INFO,
|
logmsg(log_info,
|
||||||
"MaxClients %d",
|
"MaxClients %d",
|
||||||
fifo->length);
|
fifo->length);
|
||||||
break;
|
break;
|
||||||
@@ -761,15 +825,19 @@ main(int argc, char **argv)
|
|||||||
if (!client) {
|
if (!client) {
|
||||||
fprintf(stderr, "endlessh: warning: out of memory\n");
|
fprintf(stderr, "endlessh: warning: out of memory\n");
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
} else {
|
||||||
fifo_append(fifo, client);
|
fifo_append(fifo, client);
|
||||||
logmsg(LOG_INFO, "ACCEPT host=%s port=%d fd=%d n=%d/%d",
|
logmsg(log_info, "ACCEPT host=%s port=%d fd=%d n=%d/%d",
|
||||||
client->ipaddr, client->port, client->fd,
|
client->ipaddr, client->port, client->fd,
|
||||||
fifo->length, config.max_clients);
|
fifo->length, config.max_clients);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fifo_destroy(fifo);
|
fifo_destroy(fifo);
|
||||||
statistics_log_totals(0);
|
statistics_log_totals(0);
|
||||||
|
|
||||||
|
if (logmsg == logsyslog)
|
||||||
|
closelog();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Endlessh SSH Tarpit
|
Description=Endlessh SSH Tarpit
|
||||||
|
Documentation=man:endlessh(1)
|
||||||
Requires=network-online.target
|
Requires=network-online.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=30sec
|
RestartSec=30sec
|
||||||
ExecStart=/opt/endlessh/endlessh
|
ExecStart=/usr/local/bin/endlessh
|
||||||
KillSignal=SIGTERM
|
KillSignal=SIGTERM
|
||||||
|
|
||||||
# Stop trying to restart the service if it restarts too many times in a row
|
# Stop trying to restart the service if it restarts too many times in a row
|
||||||
@@ -21,9 +22,18 @@ PrivateTmp=true
|
|||||||
PrivateDevices=true
|
PrivateDevices=true
|
||||||
ProtectSystem=full
|
ProtectSystem=full
|
||||||
ProtectHome=true
|
ProtectHome=true
|
||||||
NoNewPrivileges=true
|
InaccessiblePaths=/run /var
|
||||||
ConfigurationDirectory=/etc/endlessh
|
|
||||||
|
## If you want Endlessh to bind on ports < 1024
|
||||||
|
## 1) run:
|
||||||
|
## setcap 'cap_net_bind_service=+ep' /usr/local/bin/endlessh
|
||||||
|
## 2) uncomment following line
|
||||||
|
#AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||||
|
## 3) comment following line
|
||||||
PrivateUsers=true
|
PrivateUsers=true
|
||||||
|
|
||||||
|
NoNewPrivileges=true
|
||||||
|
ConfigurationDirectory=endlessh
|
||||||
ProtectKernelTunables=true
|
ProtectKernelTunables=true
|
||||||
ProtectKernelModules=true
|
ProtectKernelModules=true
|
||||||
ProtectControlGroups=true
|
ProtectControlGroups=true
|
||||||
@@ -31,3 +41,4 @@ MemoryDenyWriteExecute=true
|
|||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
|
|||||||
34
util/openbsd/README.md
Normal file
34
util/openbsd/README.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Running `endlessh` on OpenBSD
|
||||||
|
|
||||||
|
## Covering IPv4 and IPv6
|
||||||
|
|
||||||
|
If you want to cover both IPv4 and IPv6 you'll need to run *two* instances of
|
||||||
|
`endlessh` due to OpenBSD limitations. Here's how I did it:
|
||||||
|
|
||||||
|
- copy the `endlessh` script to `rc.d` twice, as `endlessh` and `endlessh6`
|
||||||
|
- copy the `config` file to `/etc/endlessh` twice, as `config` and `config6`
|
||||||
|
- use `BindFamily 4` in `config`
|
||||||
|
- use `BindFamily 6` in `config6`
|
||||||
|
- in `rc.conf.local` force `endlessh6` to load `config6` like so:
|
||||||
|
|
||||||
|
```
|
||||||
|
endlessh6_flags=-s -f /etc/endlessh/config6
|
||||||
|
endlessh_flags=-s
|
||||||
|
```
|
||||||
|
|
||||||
|
## Covering more than 128 connections
|
||||||
|
|
||||||
|
The defaults in OpenBSD only allow for 128 open file descriptors per process,
|
||||||
|
so regardless of the `MaxClients` setting in `/etc/config` you'll end up with
|
||||||
|
something like 124 clients at the most.
|
||||||
|
You can increase these limits in `/etc/login.conf` for `endlessh` (and
|
||||||
|
`endlessh6`) like so:
|
||||||
|
|
||||||
|
```
|
||||||
|
endlessh:\
|
||||||
|
:openfiles=1024:\
|
||||||
|
:tc=daemon:
|
||||||
|
endlessh6:\
|
||||||
|
:openfiles=1024:\
|
||||||
|
:tc=daemon:
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user