mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-27 14:58:30 +00:00
Make some more fixes to prod
This commit is contained in:
parent
69bee64a59
commit
da336b3ea8
6 changed files with 90 additions and 28 deletions
|
@ -24,6 +24,6 @@
|
||||||
*/
|
*/
|
||||||
bool IsTestnetIp(uint32_t x) {
|
bool IsTestnetIp(uint32_t x) {
|
||||||
return (((x & 0xFFFFFF00u) == 0xC0000200u) /* 192.0.2.0/24 */ ||
|
return (((x & 0xFFFFFF00u) == 0xC0000200u) /* 192.0.2.0/24 */ ||
|
||||||
((x & 0xFFFFFF00u) == 0xC0000200u) /* 198.51.100.0/24 */ ||
|
((x & 0xFFFFFF00u) == 0x0c6336400) /* 198.51.100.0/24 */ ||
|
||||||
((x & 0xFFFFFF00u) == 0xCB007100u) /* 203.0.113.0/24 */);
|
((x & 0xFFFFFF00u) == 0xCB007100u) /* 203.0.113.0/24 */);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ RELAY_HEADERS_TO_CLIENT = {
|
||||||
'Content-Type',
|
'Content-Type',
|
||||||
'Last-Modified',
|
'Last-Modified',
|
||||||
'Referrer-Policy',
|
'Referrer-Policy',
|
||||||
'Vary',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function OnServerStart()
|
function OnServerStart()
|
||||||
|
@ -30,10 +29,27 @@ function OnWorkerStart()
|
||||||
assert(unix.setrlimit(unix.RLIMIT_RSS, 2*1024*1024))
|
assert(unix.setrlimit(unix.RLIMIT_RSS, 2*1024*1024))
|
||||||
assert(unix.setrlimit(unix.RLIMIT_CPU, 2))
|
assert(unix.setrlimit(unix.RLIMIT_CPU, 2))
|
||||||
assert(unix.unveil(nil, nil))
|
assert(unix.unveil(nil, nil))
|
||||||
assert(unix.pledge("stdio inet", nil, unix.PLEDGE_PENALTY_RETURN_EPERM))
|
assert(unix.pledge("stdio inet unix", nil, unix.PLEDGE_PENALTY_RETURN_EPERM))
|
||||||
end
|
end
|
||||||
|
|
||||||
function OnHttpRequest()
|
function OnHttpRequest()
|
||||||
|
local ip = GetClientAddr()
|
||||||
|
if not IsTrustedIp(ip) then
|
||||||
|
local tok = AcquireToken(ip)
|
||||||
|
if tok < 2 then
|
||||||
|
if Blackhole(ip) then
|
||||||
|
Log(kLogWarn, "banned %s" % {FormatIp(ip)})
|
||||||
|
else
|
||||||
|
Log(kLogWarn, "failed to ban %s" % {FormatIp(ip)})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if tok < 30 then
|
||||||
|
ServeError(429)
|
||||||
|
SetHeader('Connection', 'close')
|
||||||
|
Log(kLogWarn, "warned %s who has %d tokens" % {FormatIp(ip), tok})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
local url = 'http://127.0.0.1' .. EscapePath(GetPath())
|
local url = 'http://127.0.0.1' .. EscapePath(GetPath())
|
||||||
local name = GetParam('name')
|
local name = GetParam('name')
|
||||||
if name then
|
if name then
|
||||||
|
@ -49,7 +65,7 @@ function OnHttpRequest()
|
||||||
['Referer'] = GetHeader('Referer'),
|
['Referer'] = GetHeader('Referer'),
|
||||||
['Sec-CH-UA-Platform'] = GetHeader('Sec-CH-UA-Platform'),
|
['Sec-CH-UA-Platform'] = GetHeader('Sec-CH-UA-Platform'),
|
||||||
['User-Agent'] = GetHeader('User-Agent'),
|
['User-Agent'] = GetHeader('User-Agent'),
|
||||||
['X-Forwarded-For'] = FormatIp(GetClientAddr())}})
|
['X-Forwarded-For'] = FormatIp(ip)}})
|
||||||
if status then
|
if status then
|
||||||
SetStatus(status)
|
SetStatus(status)
|
||||||
for k,v in pairs(RELAY_HEADERS_TO_CLIENT) do
|
for k,v in pairs(RELAY_HEADERS_TO_CLIENT) do
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include "libc/sysv/consts/timer.h"
|
#include "libc/sysv/consts/timer.h"
|
||||||
#include "libc/time/struct/tm.h"
|
#include "libc/time/struct/tm.h"
|
||||||
#include "net/http/http.h"
|
#include "net/http/http.h"
|
||||||
|
#include "net/http/ip.h"
|
||||||
#include "third_party/getopt/getopt.h"
|
#include "third_party/getopt/getopt.h"
|
||||||
#include "third_party/musl/passwd.h"
|
#include "third_party/musl/passwd.h"
|
||||||
|
|
||||||
|
@ -355,7 +356,7 @@ void WritePid(void) {
|
||||||
bool IsMyIp(uint32_t ip) {
|
bool IsMyIp(uint32_t ip) {
|
||||||
uint32_t *p;
|
uint32_t *p;
|
||||||
for (p = g_myips; *p; ++p) {
|
for (p = g_myips; *p; ++p) {
|
||||||
if (ip == *p) {
|
if (ip == *p && !IsTestnetIp(ip)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1029,8 +1029,6 @@ void *HttpWorker(void *arg) {
|
||||||
"Cache-Control: max-age=3600, private\r\n"
|
"Cache-Control: max-age=3600, private\r\n"
|
||||||
"Date: ");
|
"Date: ");
|
||||||
p = FormatDate(p);
|
p = FormatDate(p);
|
||||||
p = stpcpy(p, "\r\nX-Token-Count: ");
|
|
||||||
p = FormatInt32(p, CountTokens(g_tok.b, ip, TB_CIDR));
|
|
||||||
p = stpcpy(p, "\r\nContent-Length: ");
|
p = stpcpy(p, "\r\nContent-Length: ");
|
||||||
p = FormatInt32(p, strlen(ipbuf));
|
p = FormatInt32(p, strlen(ipbuf));
|
||||||
p = stpcpy(p, "\r\n\r\n");
|
p = stpcpy(p, "\r\n\r\n");
|
||||||
|
|
|
@ -2038,6 +2038,25 @@ FUNCTIONS
|
||||||
`ip` should be an IPv4 address and this defaults to GetClientAddr(),
|
`ip` should be an IPv4 address and this defaults to GetClientAddr(),
|
||||||
although other interpretations of its meaning are possible.
|
although other interpretations of its meaning are possible.
|
||||||
|
|
||||||
|
Blackhole(ip:uint32)
|
||||||
|
└─→ bool
|
||||||
|
|
||||||
|
Sends IP address to blackholed service.
|
||||||
|
|
||||||
|
ProgramTokenBucket() needs to be called beforehand. The default
|
||||||
|
settings will blackhole automatically, during the accept() loop
|
||||||
|
based on the banned threshold. However if your Lua code calls
|
||||||
|
AcquireToken() manually, then you'll need this function to take
|
||||||
|
action on the returned values.
|
||||||
|
|
||||||
|
This function returns true if a datagram could be sent sucessfully.
|
||||||
|
Otherwise false is returned, which can happen if blackholed isn't
|
||||||
|
running, or if a lot of processes are sending messages to it and the
|
||||||
|
operation would have blocked.
|
||||||
|
|
||||||
|
It's assumed that the blackholed service is running locally in the
|
||||||
|
background.
|
||||||
|
|
||||||
|
|
||||||
────────────────────────────────────────────────────────────────────────────────
|
────────────────────────────────────────────────────────────────────────────────
|
||||||
CONSTANTS
|
CONSTANTS
|
||||||
|
|
|
@ -897,7 +897,8 @@ static bool IsTrustedIp(uint32_t ip) {
|
||||||
uint32_t *p;
|
uint32_t *p;
|
||||||
if (interfaces) {
|
if (interfaces) {
|
||||||
for (p = interfaces; *p; ++p) {
|
for (p = interfaces; *p; ++p) {
|
||||||
if (ip == *p) {
|
if (ip == *p && !IsTestnetIp(ip)) {
|
||||||
|
DEBUGF("(token) ip is trusted because it's %s", "a local interface");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -905,12 +906,19 @@ static bool IsTrustedIp(uint32_t ip) {
|
||||||
if (trustedips.n) {
|
if (trustedips.n) {
|
||||||
for (i = 0; i < trustedips.n; ++i) {
|
for (i = 0; i < trustedips.n; ++i) {
|
||||||
if ((ip & trustedips.p[i].mask) == trustedips.p[i].ip) {
|
if ((ip & trustedips.p[i].mask) == trustedips.p[i].ip) {
|
||||||
|
DEBUGF("(token) ip is trusted because it's %s", "whitelisted");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
} else if (IsPrivateIp(ip) && !IsTestnetIp(ip)) {
|
||||||
|
DEBUGF("(token) ip is trusted because it's %s", "private");
|
||||||
|
return true;
|
||||||
|
} else if (IsLoopbackIp(ip)) {
|
||||||
|
DEBUGF("(token) ip is trusted because it's %s", "loopback");
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return IsPrivateIp(ip) || IsLoopbackIp(ip);
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4752,20 +4760,34 @@ static int LuaIsAssetCompressed(lua_State *L) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Blackhole(uint32_t ip) {
|
static bool Blackhole(uint32_t ip) {
|
||||||
char buf[4];
|
char buf[4];
|
||||||
if (blackhole.fd > 0) return;
|
if (blackhole.fd <= 0) return false;
|
||||||
WRITE32BE(buf, ip);
|
WRITE32BE(buf, ip);
|
||||||
if (sendto(blackhole.fd, &buf, 4, 0, (struct sockaddr *)&blackhole.addr,
|
if (sendto(blackhole.fd, &buf, 4, 0, (struct sockaddr *)&blackhole.addr,
|
||||||
sizeof(blackhole.addr)) == -1) {
|
sizeof(blackhole.addr)) != -1) {
|
||||||
VERBOSEF("error: sendto(%s) failed: %m\n", blackhole.addr.sun_path);
|
return true;
|
||||||
|
} else {
|
||||||
|
VERBOSEF("(token) sendto(%s) failed: %m", blackhole.addr.sun_path);
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int LuaBlackhole(lua_State *L) {
|
||||||
|
lua_Integer ip;
|
||||||
|
ip = luaL_checkinteger(L, 1);
|
||||||
|
if (!(0 <= ip && ip <= 0xffffffff)) {
|
||||||
|
luaL_argerror(L, 1, "ip out of range");
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
lua_pushboolean(L, Blackhole(ip));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
wontreturn static void Replenisher(void) {
|
wontreturn static void Replenisher(void) {
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
VERBOSEF("token replenish worker started");
|
VERBOSEF("(token) replenish worker started");
|
||||||
signal(SIGINT, OnTerm);
|
signal(SIGINT, OnTerm);
|
||||||
signal(SIGHUP, OnTerm);
|
signal(SIGHUP, OnTerm);
|
||||||
signal(SIGTERM, OnTerm);
|
signal(SIGTERM, OnTerm);
|
||||||
|
@ -4779,8 +4801,9 @@ wontreturn static void Replenisher(void) {
|
||||||
}
|
}
|
||||||
ReplenishTokens(tokenbucket.w, (1ul << tokenbucket.cidr) / 8);
|
ReplenishTokens(tokenbucket.w, (1ul << tokenbucket.cidr) / 8);
|
||||||
ts = _timespec_add(ts, tokenbucket.replenish);
|
ts = _timespec_add(ts, tokenbucket.replenish);
|
||||||
|
DEBUGF("(token) replenished tokens");
|
||||||
}
|
}
|
||||||
VERBOSEF("token replenish worker exiting");
|
VERBOSEF("(token) replenish worker exiting");
|
||||||
_Exit(0);
|
_Exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4846,7 +4869,7 @@ static int LuaProgramTokenBucket(lua_State *L) {
|
||||||
luaL_argerror(L, 5, "require ban <= ignore");
|
luaL_argerror(L, 5, "require ban <= ignore");
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
INFOF("deploying %,ld buckets "
|
INFOF("(token) deploying %,ld buckets "
|
||||||
"(one for every %ld ips) "
|
"(one for every %ld ips) "
|
||||||
"each holding 127 tokens which "
|
"each holding 127 tokens which "
|
||||||
"replenish %g times per second, "
|
"replenish %g times per second, "
|
||||||
|
@ -4867,14 +4890,14 @@ static int LuaProgramTokenBucket(lua_State *L) {
|
||||||
strlcpy(blackhole.addr.sun_path, "/var/run/blackhole.sock",
|
strlcpy(blackhole.addr.sun_path, "/var/run/blackhole.sock",
|
||||||
sizeof(blackhole.addr.sun_path));
|
sizeof(blackhole.addr.sun_path));
|
||||||
if ((blackhole.fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) {
|
if ((blackhole.fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) {
|
||||||
WARNF("error: socket(AF_UNIX) failed: %m");
|
WARNF("(token) socket(AF_UNIX) failed: %m");
|
||||||
ban = -1;
|
ban = -1;
|
||||||
} else if (sendto(blackhole.fd, &testip, 4, 0,
|
} else if (sendto(blackhole.fd, &testip, 4, 0,
|
||||||
(struct sockaddr *)&blackhole.addr,
|
(struct sockaddr *)&blackhole.addr,
|
||||||
sizeof(blackhole.addr)) == -1) {
|
sizeof(blackhole.addr)) == -1) {
|
||||||
WARNF("error: sendto(%`'s) failed: %m", blackhole.addr.sun_path);
|
WARNF("(token) error: sendto(%`'s) failed: %m", blackhole.addr.sun_path);
|
||||||
WARNF("redbean isn't able to protect your kernel from level 4 ddos");
|
WARNF("(token) redbean isn't able to protect your kernel from ddos");
|
||||||
WARNF("please run the blackholed program, see https://justine.lol/");
|
WARNF("(token) please run the blackholed program; see our website!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tokenbucket.b = _mapshared(ROUNDUP(1ul << cidr, FRAMESIZE));
|
tokenbucket.b = _mapshared(ROUNDUP(1ul << cidr, FRAMESIZE));
|
||||||
|
@ -5212,6 +5235,7 @@ static const luaL_Reg kLuaFuncs[] = {
|
||||||
{"oct", LuaOct}, //
|
{"oct", LuaOct}, //
|
||||||
#ifndef UNSECURE
|
#ifndef UNSECURE
|
||||||
{"AcquireToken", LuaAcquireToken}, //
|
{"AcquireToken", LuaAcquireToken}, //
|
||||||
|
{"Blackhole", LuaBlackhole}, // undocumented
|
||||||
{"CountTokens", LuaCountTokens}, //
|
{"CountTokens", LuaCountTokens}, //
|
||||||
{"EvadeDragnetSurveillance", LuaEvadeDragnetSurveillance}, //
|
{"EvadeDragnetSurveillance", LuaEvadeDragnetSurveillance}, //
|
||||||
{"GetSslIdentity", LuaGetSslIdentity}, //
|
{"GetSslIdentity", LuaGetSslIdentity}, //
|
||||||
|
@ -6122,7 +6146,6 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) {
|
||||||
} else {
|
} else {
|
||||||
return ServeError(500, "Internal Server Error");
|
return ServeError(500, "Internal Server Error");
|
||||||
}
|
}
|
||||||
#if 0 // TODO(jart): re-enable me
|
|
||||||
} else if (!IsTiny() && cpm.msg.method != kHttpHead && !IsSslCompressed() &&
|
} else if (!IsTiny() && cpm.msg.method != kHttpHead && !IsSslCompressed() &&
|
||||||
ClientAcceptsGzip() &&
|
ClientAcceptsGzip() &&
|
||||||
!(a->file &&
|
!(a->file &&
|
||||||
|
@ -6132,7 +6155,6 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) {
|
||||||
MeasureEntropy(cpm.content, 1000) < 7))) {
|
MeasureEntropy(cpm.content, 1000) < 7))) {
|
||||||
WARNF("serving compressed asset");
|
WARNF("serving compressed asset");
|
||||||
p = ServeAssetCompressed(a);
|
p = ServeAssetCompressed(a);
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
p = ServeAssetIdentity(a, ct);
|
p = ServeAssetIdentity(a, ct);
|
||||||
}
|
}
|
||||||
|
@ -6700,32 +6722,38 @@ static int HandleConnection(size_t i) {
|
||||||
LockInc(&shared->c.accepts);
|
LockInc(&shared->c.accepts);
|
||||||
GetClientAddr(&ip, 0);
|
GetClientAddr(&ip, 0);
|
||||||
if (tokenbucket.cidr && tokenbucket.reject >= 0) {
|
if (tokenbucket.cidr && tokenbucket.reject >= 0) {
|
||||||
if (!IsLoopbackIp(ip) && !IsTrustedIp(ip)) {
|
if (!IsTrustedIp(ip)) {
|
||||||
tok = AcquireToken(tokenbucket.b, ip, tokenbucket.cidr);
|
tok = AcquireToken(tokenbucket.b, ip, tokenbucket.cidr);
|
||||||
if (tok <= tokenbucket.ban && tokenbucket.ban >= 0) {
|
if (tok <= tokenbucket.ban && tokenbucket.ban >= 0) {
|
||||||
WARNF("(srvr) banning %hhu.%hhu.%hhu.%hhu who only has %d tokens",
|
WARNF("(token) banning %hhu.%hhu.%hhu.%hhu who only has %d tokens",
|
||||||
ip >> 24, ip >> 16, ip >> 8, ip, tok);
|
ip >> 24, ip >> 16, ip >> 8, ip, tok);
|
||||||
LockInc(&shared->c.bans);
|
LockInc(&shared->c.bans);
|
||||||
Blackhole(ip);
|
Blackhole(ip);
|
||||||
close(client);
|
close(client);
|
||||||
return 0;
|
return 0;
|
||||||
} else if (tok <= tokenbucket.ignore && tokenbucket.ignore >= 0) {
|
} else if (tok <= tokenbucket.ignore && tokenbucket.ignore >= 0) {
|
||||||
|
DEBUGF("(token) ignoring %hhu.%hhu.%hhu.%hhu who only has %d tokens",
|
||||||
|
ip >> 24, ip >> 16, ip >> 8, ip, tok);
|
||||||
LockInc(&shared->c.ignores);
|
LockInc(&shared->c.ignores);
|
||||||
close(client);
|
close(client);
|
||||||
return 0;
|
return 0;
|
||||||
} else if (tok < tokenbucket.reject) {
|
} else if (tok < tokenbucket.reject) {
|
||||||
WARNF("(srvr) rejecting %hhu.%hhu.%hhu.%hhu who only has %d tokens",
|
WARNF("(token) rejecting %hhu.%hhu.%hhu.%hhu who only has %d tokens",
|
||||||
ip >> 24, ip >> 16, ip >> 8, ip, tok);
|
ip >> 24, ip >> 16, ip >> 8, ip, tok);
|
||||||
LockInc(&shared->c.rejects);
|
LockInc(&shared->c.rejects);
|
||||||
SendTooManyRequests();
|
SendTooManyRequests();
|
||||||
close(client);
|
close(client);
|
||||||
return 0;
|
return 0;
|
||||||
|
} else {
|
||||||
|
DEBUGF("(token) %hhu.%hhu.%hhu.%hhu has %d tokens", ip >> 24,
|
||||||
|
ip >> 16, ip >> 8, ip, tok - 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DEBUGF(
|
DEBUGF("(token) won't acquire token for trusted ip %hhu.%hhu.%hhu.%hhu",
|
||||||
"(srvr) won't acquire token for whitelisted ip %hhu.%hhu.%hhu.%hhu",
|
ip >> 24, ip >> 16, ip >> 8, ip);
|
||||||
ip >> 24, ip >> 16, ip >> 8, ip);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
DEBUGF("(token) can't acquire accept() token for client");
|
||||||
}
|
}
|
||||||
startconnection = _timespec_real();
|
startconnection = _timespec_real();
|
||||||
if (UNLIKELY(maxworkers) && shared->workers >= maxworkers) {
|
if (UNLIKELY(maxworkers) && shared->workers >= maxworkers) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue