mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +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) {
|
||||
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 */);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ RELAY_HEADERS_TO_CLIENT = {
|
|||
'Content-Type',
|
||||
'Last-Modified',
|
||||
'Referrer-Policy',
|
||||
'Vary',
|
||||
}
|
||||
|
||||
function OnServerStart()
|
||||
|
@ -30,10 +29,27 @@ 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))
|
||||
assert(unix.pledge("stdio inet unix", nil, unix.PLEDGE_PENALTY_RETURN_EPERM))
|
||||
end
|
||||
|
||||
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 name = GetParam('name')
|
||||
if name then
|
||||
|
@ -49,7 +65,7 @@ function OnHttpRequest()
|
|||
['Referer'] = GetHeader('Referer'),
|
||||
['Sec-CH-UA-Platform'] = GetHeader('Sec-CH-UA-Platform'),
|
||||
['User-Agent'] = GetHeader('User-Agent'),
|
||||
['X-Forwarded-For'] = FormatIp(GetClientAddr())}})
|
||||
['X-Forwarded-For'] = FormatIp(ip)}})
|
||||
if status then
|
||||
SetStatus(status)
|
||||
for k,v in pairs(RELAY_HEADERS_TO_CLIENT) do
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "libc/sysv/consts/timer.h"
|
||||
#include "libc/time/struct/tm.h"
|
||||
#include "net/http/http.h"
|
||||
#include "net/http/ip.h"
|
||||
#include "third_party/getopt/getopt.h"
|
||||
#include "third_party/musl/passwd.h"
|
||||
|
||||
|
@ -355,7 +356,7 @@ void WritePid(void) {
|
|||
bool IsMyIp(uint32_t ip) {
|
||||
uint32_t *p;
|
||||
for (p = g_myips; *p; ++p) {
|
||||
if (ip == *p) {
|
||||
if (ip == *p && !IsTestnetIp(ip)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1029,8 +1029,6 @@ void *HttpWorker(void *arg) {
|
|||
"Cache-Control: max-age=3600, private\r\n"
|
||||
"Date: ");
|
||||
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 = FormatInt32(p, strlen(ipbuf));
|
||||
p = stpcpy(p, "\r\n\r\n");
|
||||
|
|
|
@ -2038,6 +2038,25 @@ FUNCTIONS
|
|||
`ip` should be an IPv4 address and this defaults to GetClientAddr(),
|
||||
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
|
||||
|
|
|
@ -897,7 +897,8 @@ static bool IsTrustedIp(uint32_t ip) {
|
|||
uint32_t *p;
|
||||
if (interfaces) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -905,12 +906,19 @@ static bool IsTrustedIp(uint32_t ip) {
|
|||
if (trustedips.n) {
|
||||
for (i = 0; i < trustedips.n; ++i) {
|
||||
if ((ip & trustedips.p[i].mask) == trustedips.p[i].ip) {
|
||||
DEBUGF("(token) ip is trusted because it's %s", "whitelisted");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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 {
|
||||
return IsPrivateIp(ip) || IsLoopbackIp(ip);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4752,20 +4760,34 @@ static int LuaIsAssetCompressed(lua_State *L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void Blackhole(uint32_t ip) {
|
||||
static bool Blackhole(uint32_t ip) {
|
||||
char buf[4];
|
||||
if (blackhole.fd > 0) return;
|
||||
if (blackhole.fd <= 0) return false;
|
||||
WRITE32BE(buf, ip);
|
||||
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);
|
||||
sizeof(blackhole.addr)) != -1) {
|
||||
return true;
|
||||
} else {
|
||||
VERBOSEF("(token) sendto(%s) failed: %m", blackhole.addr.sun_path);
|
||||
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) {
|
||||
struct timespec ts;
|
||||
VERBOSEF("token replenish worker started");
|
||||
VERBOSEF("(token) replenish worker started");
|
||||
signal(SIGINT, OnTerm);
|
||||
signal(SIGHUP, OnTerm);
|
||||
signal(SIGTERM, OnTerm);
|
||||
|
@ -4779,8 +4801,9 @@ wontreturn static void Replenisher(void) {
|
|||
}
|
||||
ReplenishTokens(tokenbucket.w, (1ul << tokenbucket.cidr) / 8);
|
||||
ts = _timespec_add(ts, tokenbucket.replenish);
|
||||
DEBUGF("(token) replenished tokens");
|
||||
}
|
||||
VERBOSEF("token replenish worker exiting");
|
||||
VERBOSEF("(token) replenish worker exiting");
|
||||
_Exit(0);
|
||||
}
|
||||
|
||||
|
@ -4846,7 +4869,7 @@ static int LuaProgramTokenBucket(lua_State *L) {
|
|||
luaL_argerror(L, 5, "require ban <= ignore");
|
||||
unreachable;
|
||||
}
|
||||
INFOF("deploying %,ld buckets "
|
||||
INFOF("(token) deploying %,ld buckets "
|
||||
"(one for every %ld ips) "
|
||||
"each holding 127 tokens which "
|
||||
"replenish %g times per second, "
|
||||
|
@ -4867,14 +4890,14 @@ static int LuaProgramTokenBucket(lua_State *L) {
|
|||
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");
|
||||
WARNF("(token) socket(AF_UNIX) failed: %m");
|
||||
ban = -1;
|
||||
} 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/");
|
||||
WARNF("(token) error: sendto(%`'s) failed: %m", blackhole.addr.sun_path);
|
||||
WARNF("(token) redbean isn't able to protect your kernel from ddos");
|
||||
WARNF("(token) please run the blackholed program; see our website!");
|
||||
}
|
||||
}
|
||||
tokenbucket.b = _mapshared(ROUNDUP(1ul << cidr, FRAMESIZE));
|
||||
|
@ -5212,6 +5235,7 @@ static const luaL_Reg kLuaFuncs[] = {
|
|||
{"oct", LuaOct}, //
|
||||
#ifndef UNSECURE
|
||||
{"AcquireToken", LuaAcquireToken}, //
|
||||
{"Blackhole", LuaBlackhole}, // undocumented
|
||||
{"CountTokens", LuaCountTokens}, //
|
||||
{"EvadeDragnetSurveillance", LuaEvadeDragnetSurveillance}, //
|
||||
{"GetSslIdentity", LuaGetSslIdentity}, //
|
||||
|
@ -6122,7 +6146,6 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) {
|
|||
} else {
|
||||
return ServeError(500, "Internal Server Error");
|
||||
}
|
||||
#if 0 // TODO(jart): re-enable me
|
||||
} else if (!IsTiny() && cpm.msg.method != kHttpHead && !IsSslCompressed() &&
|
||||
ClientAcceptsGzip() &&
|
||||
!(a->file &&
|
||||
|
@ -6132,7 +6155,6 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) {
|
|||
MeasureEntropy(cpm.content, 1000) < 7))) {
|
||||
WARNF("serving compressed asset");
|
||||
p = ServeAssetCompressed(a);
|
||||
#endif
|
||||
} else {
|
||||
p = ServeAssetIdentity(a, ct);
|
||||
}
|
||||
|
@ -6700,32 +6722,38 @@ static int HandleConnection(size_t i) {
|
|||
LockInc(&shared->c.accepts);
|
||||
GetClientAddr(&ip, 0);
|
||||
if (tokenbucket.cidr && tokenbucket.reject >= 0) {
|
||||
if (!IsLoopbackIp(ip) && !IsTrustedIp(ip)) {
|
||||
if (!IsTrustedIp(ip)) {
|
||||
tok = AcquireToken(tokenbucket.b, ip, tokenbucket.cidr);
|
||||
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);
|
||||
LockInc(&shared->c.bans);
|
||||
Blackhole(ip);
|
||||
close(client);
|
||||
return 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);
|
||||
close(client);
|
||||
return 0;
|
||||
} 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);
|
||||
LockInc(&shared->c.rejects);
|
||||
SendTooManyRequests();
|
||||
close(client);
|
||||
return 0;
|
||||
} else {
|
||||
DEBUGF("(token) %hhu.%hhu.%hhu.%hhu has %d tokens", ip >> 24,
|
||||
ip >> 16, ip >> 8, ip, tok - 1);
|
||||
}
|
||||
} else {
|
||||
DEBUGF(
|
||||
"(srvr) won't acquire token for whitelisted ip %hhu.%hhu.%hhu.%hhu",
|
||||
ip >> 24, ip >> 16, ip >> 8, ip);
|
||||
DEBUGF("(token) won't acquire token for trusted ip %hhu.%hhu.%hhu.%hhu",
|
||||
ip >> 24, ip >> 16, ip >> 8, ip);
|
||||
}
|
||||
} else {
|
||||
DEBUGF("(token) can't acquire accept() token for client");
|
||||
}
|
||||
startconnection = _timespec_real();
|
||||
if (UNLIKELY(maxworkers) && shared->workers >= maxworkers) {
|
||||
|
|
Loading…
Reference in a new issue