mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 15:38:22 +00:00
Make some last minute production changes
This commit is contained in:
parent
f7ff77d865
commit
69bee64a59
8 changed files with 166 additions and 61 deletions
|
@ -1,31 +1,49 @@
|
|||
-- reverse proxy for turfwar
|
||||
|
||||
ProgramPort(443)
|
||||
ProgramTokenBucket()
|
||||
if IsDaemon() then
|
||||
ProgramPort(443)
|
||||
ProgramUid(65534)
|
||||
ProgramUid(65534)
|
||||
ProgramLogPath('/var/log/turfbean.log')
|
||||
ProgramPidPath('/var/log/turfbean.pid')
|
||||
ProgramTrustedIp(ParseIp(Slurp('/etc/justine-ip.txt')), 32);
|
||||
ProgramCertificate(Slurp('/etc/letsencrypt/live/ipv4.games-ecdsa/fullchain.pem'))
|
||||
ProgramPrivateKey(Slurp('/etc/letsencrypt/live/ipv4.games-ecdsa/privkey.pem'))
|
||||
end
|
||||
|
||||
RELAY_HEADERS_TO_CLIENT = {
|
||||
'Access-Control-Allow-Origin',
|
||||
'Cache-Control',
|
||||
'Connection',
|
||||
'Content-Encoding',
|
||||
'Content-Type',
|
||||
'Last-Modified',
|
||||
'Referrer-Policy',
|
||||
'Vary',
|
||||
}
|
||||
|
||||
function OnServerStart()
|
||||
ProgramTokenBucket()
|
||||
assert(unix.setrlimit(unix.RLIMIT_NPROC, 1000, 1000))
|
||||
end
|
||||
|
||||
function OnWorkerStart()
|
||||
assert(unix.setrlimit(unix.RLIMIT_RSS, 2*1024*1024))
|
||||
assert(unix.setrlimit(unix.RLIMIT_CPU, 2))
|
||||
assert(unix.unveil(nil, nil))
|
||||
assert(unix.pledge("stdio inet", nil, unix.PLEDGE_PENALTY_RETURN_EPERM))
|
||||
end
|
||||
|
||||
function OnHttpRequest()
|
||||
local url = 'http://127.0.0.1' .. EscapePath(GetPath())
|
||||
local name = GetParam('name')
|
||||
if name then
|
||||
url = url .. '?name=' .. EscapeParam(name)
|
||||
end
|
||||
local status, headers, body =
|
||||
Fetch('http://127.0.0.1' .. EscapePath(GetPath()),
|
||||
Fetch(url,
|
||||
{method = GetMethod(),
|
||||
headers = {
|
||||
['Accept'] = GetHeader('Accept'),
|
||||
['Accept-Encoding'] = GetHeader('Accept-Encoding'),
|
||||
['CF-IPCountry'] = GetHeader('CF-IPCountry'),
|
||||
['If-Modified-Since'] = GetHeader('If-Modified-Since'),
|
||||
['Referer'] = GetHeader('Referer'),
|
||||
|
@ -39,7 +57,7 @@ function OnHttpRequest()
|
|||
end
|
||||
Write(body)
|
||||
else
|
||||
err = headers
|
||||
local err = headers
|
||||
Log(kLogError, "proxy failed %s" % {err})
|
||||
ServeError(503)
|
||||
end
|
||||
|
|
|
@ -37,18 +37,11 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
int fd;
|
||||
struct sockaddr_un addr = {
|
||||
AF_UNIX,
|
||||
"/var/run/blackhole.sock",
|
||||
};
|
||||
struct sockaddr_un addr = {AF_UNIX, "/var/run/blackhole.sock"};
|
||||
if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
|
||||
kprintf("error: socket(AF_UNIX) failed: %s\n", strerror(errno));
|
||||
return 3;
|
||||
}
|
||||
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||
kprintf("error: connect(%#s) failed: %s\n", addr.sun_path, strerror(errno));
|
||||
return 4;
|
||||
}
|
||||
|
||||
int rc = 0;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
|
@ -56,8 +49,9 @@ int main(int argc, char *argv[]) {
|
|||
char buf[4];
|
||||
if ((ip = ParseIp(argv[i], -1)) != -1) {
|
||||
WRITE32BE(buf, ip);
|
||||
if (write(fd, buf, 4) == -1) {
|
||||
kprintf("error: write() failed: %s\n", strerror(errno));
|
||||
if (sendto(fd, buf, 4, 0, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||
kprintf("error: sendto(%#s) failed: %s\n", addr.sun_path,
|
||||
strerror(errno));
|
||||
rc |= 2;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "libc/sysv/consts/sock.h"
|
||||
#include "libc/sysv/consts/timer.h"
|
||||
#include "libc/time/struct/tm.h"
|
||||
#include "net/http/http.h"
|
||||
#include "third_party/getopt/getopt.h"
|
||||
#include "third_party/musl/passwd.h"
|
||||
|
||||
|
@ -55,12 +56,13 @@
|
|||
#define DEFAULT_LOGNAME "/var/log/blackhole.log"
|
||||
#define DEFAULT_PIDNAME "/var/run/blackhole.pid"
|
||||
#define DEFAULT_SOCKNAME "/var/run/blackhole.sock"
|
||||
#define GETOPTS "L:S:P:M:G:dh"
|
||||
#define GETOPTS "L:S:P:M:G:W:dh"
|
||||
#define USAGE \
|
||||
"\
|
||||
Usage: blackholed [-hdLPSMG]\n\
|
||||
Usage: blackholed [-hdLPSMGW]\n\
|
||||
-h help\n\
|
||||
-d daemonize\n\
|
||||
-W IP whitelist ip address\n\
|
||||
-L PATH log file name (default: " DEFAULT_LOGNAME ")\n\
|
||||
-P PATH pid file name (default: " DEFAULT_PIDNAME ")\n\
|
||||
-S PATH socket file name (default: " DEFAULT_SOCKNAME ")\n\
|
||||
|
@ -131,6 +133,7 @@ const char *g_sockname;
|
|||
const char *g_iptables;
|
||||
sig_atomic_t g_shutdown;
|
||||
struct SortedInts g_blocked;
|
||||
struct SortedInts g_whitelisted;
|
||||
|
||||
static wontreturn void ShowUsage(int fd, int rc) {
|
||||
write(fd, USAGE, sizeof(USAGE) - 1);
|
||||
|
@ -139,8 +142,23 @@ static wontreturn void ShowUsage(int fd, int rc) {
|
|||
_Exit(rc);
|
||||
}
|
||||
|
||||
static void GetOpts(int argc, char *argv[]) {
|
||||
char *GetTimestamp(void) {
|
||||
struct timespec ts;
|
||||
static struct tm tm;
|
||||
static int64_t last;
|
||||
static char str[27];
|
||||
clock_gettime(0, &ts);
|
||||
if (ts.tv_sec != last) {
|
||||
localtime_r(&ts.tv_sec, &tm);
|
||||
last = ts.tv_sec;
|
||||
}
|
||||
iso8601us(str, &tm, ts.tv_nsec);
|
||||
return str;
|
||||
}
|
||||
|
||||
void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
int64_t ip;
|
||||
g_sockmode = 0777;
|
||||
g_pidname = DEFAULT_PIDNAME;
|
||||
g_logname = DEFAULT_LOGNAME;
|
||||
|
@ -165,6 +183,16 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
case 'M':
|
||||
g_sockmode = strtol(optarg, 0, 8) & 0777;
|
||||
break;
|
||||
case 'W':
|
||||
if ((ip = ParseIp(optarg, -1)) != -1) {
|
||||
if (InsertInt(&g_whitelisted, ip, true)) {
|
||||
LOG("whitelisted %s", optarg);
|
||||
}
|
||||
} else {
|
||||
kprintf("error: could not parse -W %#s IP address\n", optarg);
|
||||
_Exit(1);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
ShowUsage(1, 0);
|
||||
default:
|
||||
|
@ -173,20 +201,6 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
char *GetTimestamp(void) {
|
||||
struct timespec ts;
|
||||
static struct tm tm;
|
||||
static int64_t last;
|
||||
static char str[27];
|
||||
clock_gettime(0, &ts);
|
||||
if (ts.tv_sec != last) {
|
||||
localtime_r(&ts.tv_sec, &tm);
|
||||
last = ts.tv_sec;
|
||||
}
|
||||
iso8601us(str, &tm, ts.tv_nsec);
|
||||
return str;
|
||||
}
|
||||
|
||||
void OnTerm(int sig) {
|
||||
char tmp[15];
|
||||
LOG("got %s", strsignal_r(sig, tmp));
|
||||
|
@ -278,9 +292,11 @@ void Daemonize(void) {
|
|||
}
|
||||
|
||||
void UseLog(void) {
|
||||
_npassert(dup2(g_logfd, 2) == 2);
|
||||
if (g_logfd != 2) {
|
||||
_npassert(!close(g_logfd));
|
||||
if (g_logfd > 0) {
|
||||
_npassert(dup2(g_logfd, 2) == 2);
|
||||
if (g_logfd != 2) {
|
||||
_npassert(!close(g_logfd));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -426,6 +442,7 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
if ((ip = READ32BE(msg))) {
|
||||
if (IsMyIp(ip) || // nics
|
||||
ContainsInt(&g_whitelisted, ip) || // protected
|
||||
(ip & 0xff000000) == 0x00000000 || // 0.0.0.0/8
|
||||
(ip & 0xff000000) == 0x7f000000) { // 127.0.0.0/8
|
||||
LOG("won't block %s", FormatIp(ip));
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
#!/bin/sh
|
||||
make -j16 o//net/turfwar/blackholed.elf &&
|
||||
sudo chown root o//net/turfwar/blackholed.elf &&
|
||||
sudo chmod 06755 o//net/turfwar/blackholed.elf &&
|
||||
exec o//net/turfwar/blackholed.elf
|
|
@ -39,6 +39,7 @@
|
|||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/mem/sortedints.internal.h"
|
||||
#include "libc/nexgen32e/crc32.h"
|
||||
#include "libc/paths.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
|
@ -118,13 +119,14 @@
|
|||
#define TB_BYTES (1u << TB_CIDR)
|
||||
#define TB_WORDS (TB_BYTES / 8)
|
||||
|
||||
#define GETOPTS "idvp:w:k:"
|
||||
#define GETOPTS "idvp:w:k:W:"
|
||||
#define USAGE \
|
||||
"\
|
||||
Usage: turfwar.com [-dv] ARGS...\n\
|
||||
-i integrity check and vacuum at startup\n\
|
||||
-d daemonize\n\
|
||||
-v verbosity\n\
|
||||
-W IP whitelist\n\
|
||||
-p INT port\n\
|
||||
-w INT workers\n\
|
||||
-k INT keepalive\n\
|
||||
|
@ -247,6 +249,7 @@ bool g_daemonize;
|
|||
int g_port = PORT;
|
||||
int g_workers = WORKERS;
|
||||
int g_keepalive = KEEPALIVE_MS;
|
||||
struct SortedInts g_whitelisted;
|
||||
|
||||
// lifecycle vars
|
||||
pthread_t g_listener;
|
||||
|
@ -417,7 +420,7 @@ bool Blackhole(uint32_t ip) {
|
|||
sizeof(g_blackhole.addr)) == 4) {
|
||||
return true;
|
||||
} else {
|
||||
kprintf("error: sendto(/var/run/blackhole.sock) failed: %s\n",
|
||||
kprintf("error: sendto(%#s) failed: %s\n", g_blackhole.addr.sun_path,
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
@ -891,7 +894,8 @@ void *HttpWorker(void *arg) {
|
|||
ksnprintf(ipbuf, sizeof(ipbuf), "%hhu.%hhu.%hhu.%hhu", ip >> 24, ip >> 16,
|
||||
ip >> 8, ip);
|
||||
|
||||
if (!ipv6 && (tok = AcquireToken(g_tok.b, ip, TB_CIDR)) < 32) {
|
||||
if (!ipv6 && !ContainsInt(&g_whitelisted, ip) &&
|
||||
(tok = AcquireToken(g_tok.b, ip, TB_CIDR)) < 32) {
|
||||
if (tok > 4) {
|
||||
LOG("%s rate limiting client\n", ipbuf, msg->version);
|
||||
Write(client.sock, "HTTP/1.1 429 Too Many Requests\r\n"
|
||||
|
@ -1342,8 +1346,9 @@ void OnCtrlC(int sig) {
|
|||
}
|
||||
|
||||
// parses cli arguments
|
||||
static void GetOpts(int argc, char *argv[]) {
|
||||
void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
int64_t ip;
|
||||
while ((opt = getopt(argc, argv, GETOPTS)) != -1) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
|
@ -1364,6 +1369,16 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
case 'v':
|
||||
++__log_level;
|
||||
break;
|
||||
case 'W':
|
||||
if ((ip = ParseIp(optarg, -1)) != -1) {
|
||||
if (InsertInt(&g_whitelisted, ip, true)) {
|
||||
LOG("whitelisted %s", optarg);
|
||||
}
|
||||
} else {
|
||||
kprintf("error: could not parse -w %#s IP address\n", optarg);
|
||||
_Exit(1);
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
write(1, USAGE, sizeof(USAGE) - 1);
|
||||
exit(0);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/version.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/sock.h"
|
||||
|
@ -119,3 +120,58 @@ TEST(unix, stream) {
|
|||
EXPECT_EQ(0, WEXITSTATUS(ws));
|
||||
alarm(0);
|
||||
}
|
||||
|
||||
TEST(unix, serverGoesDown_deletedSockFile) { // field of landmine
|
||||
if (IsWindows()) return;
|
||||
int ws, rc;
|
||||
char buf[8] = {0};
|
||||
uint32_t len = sizeof(struct sockaddr_un);
|
||||
struct sockaddr_un addr = {AF_UNIX, "foo.sock"};
|
||||
ASSERT_SYS(0, 3, socket(AF_UNIX, SOCK_DGRAM, 0));
|
||||
ASSERT_SYS(0, 0, bind(3, (void *)&addr, len));
|
||||
ASSERT_SYS(0, 4, socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0));
|
||||
ASSERT_SYS(0, 0, connect(4, (void *)&addr, len));
|
||||
ASSERT_SYS(0, 5, write(4, "hello", 5));
|
||||
ASSERT_SYS(0, 5, read(3, buf, 8));
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
ASSERT_SYS(IsBsd() ? ECONNRESET : ECONNREFUSED, -1, write(4, "hello", 5));
|
||||
ASSERT_SYS(0, 0, unlink(addr.sun_path));
|
||||
ASSERT_SYS(0, 3, socket(AF_UNIX, SOCK_DGRAM, 0));
|
||||
ASSERT_SYS(0, 0, bind(3, (void *)&addr, len));
|
||||
rc = write(4, "hello", 5);
|
||||
ASSERT_TRUE(rc == -1 && (errno == ECONNRESET || //
|
||||
errno == ENOTCONN || //
|
||||
errno == ECONNREFUSED || //
|
||||
errno == EDESTADDRREQ));
|
||||
errno = 0;
|
||||
ASSERT_SYS(0, 0, close(4));
|
||||
ASSERT_SYS(0, 4, socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0));
|
||||
ASSERT_SYS(0, 0, connect(4, (void *)&addr, len));
|
||||
ASSERT_SYS(0, 5, write(4, "hello", 5));
|
||||
ASSERT_SYS(0, 5, read(3, buf, 8));
|
||||
ASSERT_SYS(0, 0, close(4));
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
}
|
||||
|
||||
TEST(unix, serverGoesDown_usingSendTo_unlink) { // much easier
|
||||
if (IsWindows()) return;
|
||||
int ws, rc;
|
||||
char buf[8] = {0};
|
||||
uint32_t len = sizeof(struct sockaddr_un);
|
||||
struct sockaddr_un addr = {AF_UNIX, "foo.sock"};
|
||||
ASSERT_SYS(0, 3, socket(AF_UNIX, SOCK_DGRAM, 0));
|
||||
ASSERT_SYS(0, 0, bind(3, (void *)&addr, len));
|
||||
ASSERT_SYS(0, 4, socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0));
|
||||
ASSERT_SYS(0, 5, sendto(4, "hello", 5, 0, (void *)&addr, len));
|
||||
ASSERT_SYS(0, 5, read(3, buf, 8));
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
ASSERT_SYS(ECONNREFUSED, -1, sendto(4, "hello", 5, 0, (void *)&addr, len));
|
||||
ASSERT_SYS(0, 0, unlink(addr.sun_path));
|
||||
ASSERT_SYS(ENOENT, -1, sendto(4, "hello", 5, 0, (void *)&addr, len));
|
||||
ASSERT_SYS(0, 3, socket(AF_UNIX, SOCK_DGRAM, 0));
|
||||
ASSERT_SYS(0, 0, bind(3, (void *)&addr, len));
|
||||
ASSERT_SYS(0, 5, sendto(4, "hello", 5, 0, (void *)&addr, len));
|
||||
ASSERT_SYS(0, 5, read(3, buf, 8));
|
||||
ASSERT_SYS(0, 0, close(4));
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
}
|
||||
|
|
|
@ -1935,6 +1935,12 @@ FUNCTIONS
|
|||
If the client ignores this warning and keeps sending requests, until
|
||||
there's no tokens left, then the banhammer finally comes down.
|
||||
|
||||
function OnServerStart()
|
||||
ProgramTokenBucket()
|
||||
ProgramTrustedIp(ParseIp('x.x.x.x'), 32)
|
||||
assert(unix.setrlimit(unix.RLIMIT_NPROC, 1000, 1000))
|
||||
end
|
||||
|
||||
This model of network rate limiting generously lets people "burst" a
|
||||
tiny bit. For example someone might get a strong craving for content
|
||||
and smash the reload button in Chrome 64 times in a fow seconds. But
|
||||
|
|
|
@ -2171,7 +2171,12 @@ static bool OpenZip(bool force) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
WARNF("(zip) stat() error: %m");
|
||||
// avoid noise if we setuid to user who can't see executable
|
||||
if (errno == EACCES) {
|
||||
VERBOSEF("(zip) stat(%`'s) error: %m", zpath);
|
||||
} else {
|
||||
WARNF("(zip) stat(%`'s) error: %m", zpath);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -4749,10 +4754,12 @@ static int LuaIsAssetCompressed(lua_State *L) {
|
|||
|
||||
static void Blackhole(uint32_t ip) {
|
||||
char buf[4];
|
||||
if (blackhole.fd <= 0) return;
|
||||
if (blackhole.fd > 0) return;
|
||||
WRITE32BE(buf, ip);
|
||||
if (write(blackhole.fd, buf, 4) == -1) {
|
||||
WARNF("error: write(%s) failed: %m\n", blackhole.addr.sun_path);
|
||||
if (sendto(blackhole.fd, &buf, 4, 0, (struct sockaddr *)&blackhole.addr,
|
||||
sizeof(blackhole.addr)) == -1) {
|
||||
VERBOSEF("error: sendto(%s) failed: %m\n", blackhole.addr.sun_path);
|
||||
errno = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4855,22 +4862,19 @@ static int LuaProgramTokenBucket(lua_State *L) {
|
|||
if (ignore == -1) ignore = -128;
|
||||
if (ban == -1) ban = -128;
|
||||
if (ban >= 0 && (IsLinux() || IsBsd())) {
|
||||
struct Blackhole bh;
|
||||
bh.addr.sun_family = AF_UNIX;
|
||||
strlcpy(bh.addr.sun_path, "/var/run/blackhole.sock",
|
||||
sizeof(bh.addr.sun_path));
|
||||
if ((bh.fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) {
|
||||
uint32_t testip = 0;
|
||||
blackhole.addr.sun_family = AF_UNIX;
|
||||
strlcpy(blackhole.addr.sun_path, "/var/run/blackhole.sock",
|
||||
sizeof(blackhole.addr.sun_path));
|
||||
if ((blackhole.fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) {
|
||||
WARNF("error: socket(AF_UNIX) failed: %m");
|
||||
ban = -1;
|
||||
} else if (connect(bh.fd, (struct sockaddr *)&bh.addr, sizeof(bh.addr)) ==
|
||||
-1) {
|
||||
WARNF("error: connect(%`'s) failed: %m", bh.addr.sun_path);
|
||||
} else if (sendto(blackhole.fd, &testip, 4, 0,
|
||||
(struct sockaddr *)&blackhole.addr,
|
||||
sizeof(blackhole.addr)) == -1) {
|
||||
WARNF("error: sendto(%`'s) failed: %m", blackhole.addr.sun_path);
|
||||
WARNF("redbean isn't able to protect your kernel from level 4 ddos");
|
||||
WARNF("please run the blackholed program, see https://justine.lol/");
|
||||
close(bh.fd);
|
||||
ban = -1;
|
||||
} else {
|
||||
blackhole = bh;
|
||||
}
|
||||
}
|
||||
tokenbucket.b = _mapshared(ROUNDUP(1ul << cidr, FRAMESIZE));
|
||||
|
|
Loading…
Add table
Reference in a new issue