core: Implement basic git integration
Missing streaming, so git's a bit confused, but works. Also easy to bypass. Although I doubt any mf at alibaba gon bypass it with his custom UA.
This commit is contained in:
@@ -17,6 +17,7 @@ AI scrapers are everywhere. This will stop them. `robots.txt` won't.
|
|||||||
- Dynamic challenge amount (aka difficulty)
|
- Dynamic challenge amount (aka difficulty)
|
||||||
- Detection of token overuse
|
- Detection of token overuse
|
||||||
- Better wait screen
|
- Better wait screen
|
||||||
|
- Better git integration (it's quite rudimentary right now)
|
||||||
|
|
||||||
## Caveats
|
## Caveats
|
||||||
If you are using this, it's almost certain search engines will stop indexing your site. Keep this in mind.
|
If you are using this, it's almost certain search engines will stop indexing your site. Keep this in mind.
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class CConfig {
|
|||||||
std::string data_dir = "";
|
std::string data_dir = "";
|
||||||
std::string html_dir = "";
|
std::string html_dir = "";
|
||||||
unsigned long int max_request_size = 10000000; // 10MB
|
unsigned long int max_request_size = 10000000; // 10MB
|
||||||
|
bool git_host = false;
|
||||||
} m_config;
|
} m_config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "../headers/authorization.hpp"
|
#include "../headers/authorization.hpp"
|
||||||
#include "../headers/cfHeader.hpp"
|
#include "../headers/cfHeader.hpp"
|
||||||
#include "../headers/xforwardfor.hpp"
|
#include "../headers/xforwardfor.hpp"
|
||||||
|
#include "../headers/gitProtocolHeader.hpp"
|
||||||
#include "../debug/log.hpp"
|
#include "../debug/log.hpp"
|
||||||
#include "../GlobalState.hpp"
|
#include "../GlobalState.hpp"
|
||||||
#include "../config/Config.hpp"
|
#include "../config/Config.hpp"
|
||||||
@@ -12,7 +13,9 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#define private public
|
||||||
#include <pistache/client.h>
|
#include <pistache/client.h>
|
||||||
|
#undef private
|
||||||
#include <tinylates/tinylates.hpp>
|
#include <tinylates/tinylates.hpp>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <glaze/glaze.hpp>
|
#include <glaze/glaze.hpp>
|
||||||
@@ -89,9 +92,11 @@ void CServerHandler::onRequest(const Pistache::Http::Request& req, Pistache::Htt
|
|||||||
const auto HEADERS = req.headers();
|
const auto HEADERS = req.headers();
|
||||||
std::shared_ptr<const Pistache::Http::Header::Host> hostHeader;
|
std::shared_ptr<const Pistache::Http::Header::Host> hostHeader;
|
||||||
std::shared_ptr<const Pistache::Http::Header::ContentType> contentTypeHeader;
|
std::shared_ptr<const Pistache::Http::Header::ContentType> contentTypeHeader;
|
||||||
|
std::shared_ptr<const Pistache::Http::Header::UserAgent> userAgentHeader;
|
||||||
std::shared_ptr<const CFConnectingIPHeader> cfHeader;
|
std::shared_ptr<const CFConnectingIPHeader> cfHeader;
|
||||||
std::shared_ptr<const XForwardedForHeader> xForwardedForHeader;
|
std::shared_ptr<const XForwardedForHeader> xForwardedForHeader;
|
||||||
std::shared_ptr<const AuthorizationHeader> authHeader;
|
std::shared_ptr<const AuthorizationHeader> authHeader;
|
||||||
|
std::shared_ptr<const GitProtocolHeader> gitProtocolHeader;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
hostHeader = Pistache::Http::Header::header_cast<Pistache::Http::Header::Host>(HEADERS.get("Host"));
|
hostHeader = Pistache::Http::Header::header_cast<Pistache::Http::Header::Host>(HEADERS.get("Host"));
|
||||||
@@ -125,6 +130,18 @@ void CServerHandler::onRequest(const Pistache::Http::Request& req, Pistache::Htt
|
|||||||
; // silent ignore
|
; // silent ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
userAgentHeader = Pistache::Http::Header::header_cast<Pistache::Http::Header::UserAgent>(HEADERS.get("User-Agent"));
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
; // silent ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
gitProtocolHeader = Pistache::Http::Header::header_cast<GitProtocolHeader>(HEADERS.get("Git-Protocol"));
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
; // silent ignore
|
||||||
|
}
|
||||||
|
|
||||||
Debug::log(LOG, "Got request for: {}:{}{}", hostHeader->host(), hostHeader->port().toString(), req.resource());
|
Debug::log(LOG, "Got request for: {}:{}{}", hostHeader->host(), hostHeader->port().toString(), req.resource());
|
||||||
Debug::log(LOG, "Request author: IP {}", req.address().host());
|
Debug::log(LOG, "Request author: IP {}", req.address().host());
|
||||||
if (cfHeader)
|
if (cfHeader)
|
||||||
@@ -132,6 +149,9 @@ void CServerHandler::onRequest(const Pistache::Http::Request& req, Pistache::Htt
|
|||||||
else
|
else
|
||||||
Debug::log(WARN, "Connection does not come through CloudFlare");
|
Debug::log(WARN, "Connection does not come through CloudFlare");
|
||||||
|
|
||||||
|
if (userAgentHeader)
|
||||||
|
Debug::log(LOG, "UA: {}", userAgentHeader->agent());
|
||||||
|
|
||||||
if (req.resource() == "/checkpoint/challenge") {
|
if (req.resource() == "/checkpoint/challenge") {
|
||||||
if (req.method() == Pistache::Http::Method::Post)
|
if (req.method() == Pistache::Http::Method::Post)
|
||||||
challengeSubmitted(req, response);
|
challengeSubmitted(req, response);
|
||||||
@@ -140,6 +160,21 @@ void CServerHandler::onRequest(const Pistache::Http::Request& req, Pistache::Htt
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_pConfig->m_config.git_host) {
|
||||||
|
// TODO: ratelimit and check this. This can be faked!
|
||||||
|
if (gitProtocolHeader && userAgentHeader) {
|
||||||
|
Debug::log(LOG, "Request looks like it is coming from git (UA + GP). Accepting.");
|
||||||
|
|
||||||
|
proxyPass(req, response);
|
||||||
|
return;
|
||||||
|
} else if (userAgentHeader->agent().starts_with("git/")) {
|
||||||
|
Debug::log(LOG, "Request looks like it is coming from git (UA git). Accepting.");
|
||||||
|
|
||||||
|
proxyPass(req, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (req.cookies().has("CheckpointToken")) {
|
if (req.cookies().has("CheckpointToken")) {
|
||||||
// check the token
|
// check the token
|
||||||
const auto TOKEN = g_pDB->getToken(req.cookies().get("CheckpointToken").value);
|
const auto TOKEN = g_pDB->getToken(req.cookies().get("CheckpointToken").value);
|
||||||
@@ -243,89 +278,58 @@ void CServerHandler::proxyPass(const Pistache::Http::Request& req, Pistache::Htt
|
|||||||
client.init(Pistache::Http::Experimental::Client::options().threads(1).maxConnectionsPerHost(8));
|
client.init(Pistache::Http::Experimental::Client::options().threads(1).maxConnectionsPerHost(8));
|
||||||
const std::string FORWARD_ADDR = g_pConfig->m_config.forward_address;
|
const std::string FORWARD_ADDR = g_pConfig->m_config.forward_address;
|
||||||
|
|
||||||
switch (req.method()) {
|
Debug::log(LOG, "Method ({}): Forwarding to {}", (uint32_t)req.method(), FORWARD_ADDR + req.resource());
|
||||||
// there are some crazy semantics going on here, idk how to make this cleaner with less c+p
|
|
||||||
|
|
||||||
case Pistache::Http::Method::Get: {
|
auto builder = client.prepareRequest(FORWARD_ADDR + req.resource(), req.method());
|
||||||
Debug::log(LOG, "Get: Forwarding to {}", FORWARD_ADDR + req.resource());
|
builder.body(req.body());
|
||||||
auto builder = client.get(FORWARD_ADDR + req.resource()).body(req.body());
|
for (auto it = req.cookies().begin(); it != req.cookies().end(); ++it) {
|
||||||
for (auto it = req.cookies().begin(); it != req.cookies().end(); ++it) {
|
builder.cookie(*it);
|
||||||
builder.cookie(*it);
|
|
||||||
}
|
|
||||||
builder.timeout(std::chrono::milliseconds(10000));
|
|
||||||
|
|
||||||
auto resp = builder.send();
|
|
||||||
resp.then([&](Pistache::Http::Response resp) { response.send(Pistache::Http::Code::Ok, resp.body()); },
|
|
||||||
[&](std::exception_ptr e) { response.send(Pistache::Http::Code::Internal_Server_Error, "Internal Proxy Error"); });
|
|
||||||
Pistache::Async::Barrier<Pistache::Http::Response> b(resp);
|
|
||||||
b.wait_for(std::chrono::seconds(10));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Pistache::Http::Method::Post: {
|
|
||||||
Debug::log(LOG, "Post: Forwarding to {}", FORWARD_ADDR + req.resource());
|
|
||||||
auto builder = client.post(FORWARD_ADDR + req.resource()).body(req.body());
|
|
||||||
for (auto it = req.cookies().begin(); it != req.cookies().end(); ++it) {
|
|
||||||
builder.cookie(*it);
|
|
||||||
}
|
|
||||||
builder.timeout(std::chrono::milliseconds(10000));
|
|
||||||
|
|
||||||
auto resp = builder.send();
|
|
||||||
resp.then([&](Pistache::Http::Response resp) { response.send(Pistache::Http::Code::Ok, resp.body()); },
|
|
||||||
[&](std::exception_ptr e) { response.send(Pistache::Http::Code::Internal_Server_Error, "Internal Proxy Error"); });
|
|
||||||
Pistache::Async::Barrier<Pistache::Http::Response> b(resp);
|
|
||||||
b.wait_for(std::chrono::seconds(10));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Pistache::Http::Method::Put: {
|
|
||||||
Debug::log(LOG, "Put: Forwarding to {}", FORWARD_ADDR + req.resource());
|
|
||||||
auto builder = client.put(FORWARD_ADDR + req.resource()).body(req.body());
|
|
||||||
for (auto it = req.cookies().begin(); it != req.cookies().end(); ++it) {
|
|
||||||
builder.cookie(*it);
|
|
||||||
}
|
|
||||||
builder.timeout(std::chrono::milliseconds(10000));
|
|
||||||
|
|
||||||
auto resp = builder.send();
|
|
||||||
resp.then([&](Pistache::Http::Response resp) { response.send(Pistache::Http::Code::Ok, resp.body()); },
|
|
||||||
[&](std::exception_ptr e) { response.send(Pistache::Http::Code::Internal_Server_Error, "Internal Proxy Error"); });
|
|
||||||
Pistache::Async::Barrier<Pistache::Http::Response> b(resp);
|
|
||||||
b.wait_for(std::chrono::seconds(10));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Pistache::Http::Method::Delete: {
|
|
||||||
Debug::log(LOG, "Delete: Forwarding to {}", FORWARD_ADDR + req.resource());
|
|
||||||
auto builder = client.del(FORWARD_ADDR + req.resource()).body(req.body());
|
|
||||||
for (auto it = req.cookies().begin(); it != req.cookies().end(); ++it) {
|
|
||||||
builder.cookie(*it);
|
|
||||||
}
|
|
||||||
builder.timeout(std::chrono::milliseconds(10000));
|
|
||||||
|
|
||||||
auto resp = builder.send();
|
|
||||||
resp.then([&](Pistache::Http::Response resp) { response.send(Pistache::Http::Code::Ok, resp.body()); },
|
|
||||||
[&](std::exception_ptr e) { response.send(Pistache::Http::Code::Internal_Server_Error, "Internal Proxy Error"); });
|
|
||||||
Pistache::Async::Barrier<Pistache::Http::Response> b(resp);
|
|
||||||
b.wait_for(std::chrono::seconds(10));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Pistache::Http::Method::Patch: {
|
|
||||||
Debug::log(LOG, "Patch: Forwarding to {}", FORWARD_ADDR + req.resource());
|
|
||||||
auto builder = client.patch(FORWARD_ADDR + req.resource()).body(req.body());
|
|
||||||
for (auto it = req.cookies().begin(); it != req.cookies().end(); ++it) {
|
|
||||||
builder.cookie(*it);
|
|
||||||
}
|
|
||||||
builder.timeout(std::chrono::milliseconds(10000));
|
|
||||||
|
|
||||||
auto resp = builder.send();
|
|
||||||
resp.then([&](Pistache::Http::Response resp) { response.send(Pistache::Http::Code::Ok, resp.body()); },
|
|
||||||
[&](std::exception_ptr e) { response.send(Pistache::Http::Code::Internal_Server_Error, "Internal Proxy Error"); });
|
|
||||||
Pistache::Async::Barrier<Pistache::Http::Response> b(resp);
|
|
||||||
b.wait_for(std::chrono::seconds(10));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
response.send(Pistache::Http::Code::Internal_Server_Error, "Invalid request type for proxy");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const auto HEADERS = req.headers().list();
|
||||||
|
for (auto& h : HEADERS) {
|
||||||
|
// FIXME: why does this break e.g. gitea if we include it?
|
||||||
|
if (std::string_view{h->name()} == "Host") {
|
||||||
|
Debug::log(LOG, "Header in: {}: {} (DROPPED)", h->name(), req.headers().getRaw(h->name()).value());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug::log(LOG, "Header in: {}: {}", h->name(), req.headers().getRaw(h->name()).value());
|
||||||
|
builder.header(h);
|
||||||
|
}
|
||||||
|
builder.timeout(std::chrono::milliseconds(10000));
|
||||||
|
|
||||||
|
// TODO: implement streaming for git's large objects?
|
||||||
|
|
||||||
|
auto resp = builder.send();
|
||||||
|
resp.then(
|
||||||
|
[&](Pistache::Http::Response resp) {
|
||||||
|
const auto HEADERSRESP = resp.headers().list();
|
||||||
|
|
||||||
|
for (auto& h : HEADERSRESP) {
|
||||||
|
if (std::string_view{h->name()} == "Transfer-Encoding") {
|
||||||
|
Debug::log(LOG, "Header out: {}: {} (DROPPED)", h->name(), resp.headers().getRaw(h->name()).value());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug::log(LOG, "Header out: {}: {}", h->name(), resp.headers().getRaw(h->name()).value());
|
||||||
|
response.headers().add(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
response.send(Pistache::Http::Code::Ok, resp.body());
|
||||||
|
},
|
||||||
|
[&](std::exception_ptr e) {
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(e);
|
||||||
|
} catch (std::exception& e) { Debug::log(ERR, "Proxy failed: {}", e.what()); } catch (const std::string& e) {
|
||||||
|
Debug::log(ERR, "Proxy failed: {}", e);
|
||||||
|
} catch (const char* e) { Debug::log(ERR, "Proxy failed: {}", e); } catch (...) {
|
||||||
|
Debug::log(ERR, "Proxy failed: God knows why.");
|
||||||
|
}
|
||||||
|
|
||||||
|
response.send(Pistache::Http::Code::Internal_Server_Error, "Internal Proxy Error");
|
||||||
|
});
|
||||||
|
Pistache::Async::Barrier<Pistache::Http::Response> b(resp);
|
||||||
|
b.wait_for(std::chrono::seconds(10));
|
||||||
|
|
||||||
client.shutdown();
|
client.shutdown();
|
||||||
}
|
}
|
||||||
24
src/headers/gitProtocolHeader.hpp
Normal file
24
src/headers/gitProtocolHeader.hpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include <pistache/http_headers.h>
|
||||||
|
#include <pistache/net.h>
|
||||||
|
|
||||||
|
class GitProtocolHeader : public Pistache::Http::Header::Header {
|
||||||
|
public:
|
||||||
|
NAME("Git-Protocol");
|
||||||
|
|
||||||
|
GitProtocolHeader() = default;
|
||||||
|
|
||||||
|
void parse(const std::string& str) override {
|
||||||
|
m_text = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(std::ostream& os) const override {
|
||||||
|
os << m_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string text() const {
|
||||||
|
return m_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_text = "";
|
||||||
|
};
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "headers/authorization.hpp"
|
#include "headers/authorization.hpp"
|
||||||
#include "headers/xforwardfor.hpp"
|
#include "headers/xforwardfor.hpp"
|
||||||
#include "headers/cfHeader.hpp"
|
#include "headers/cfHeader.hpp"
|
||||||
|
#include "headers/gitProtocolHeader.hpp"
|
||||||
|
|
||||||
#include "debug/log.hpp"
|
#include "debug/log.hpp"
|
||||||
|
|
||||||
@@ -72,6 +73,7 @@ int main(int argc, char** argv, char** envp) {
|
|||||||
|
|
||||||
Pistache::Http::Header::Registry::instance().registerHeader<CFConnectingIPHeader>();
|
Pistache::Http::Header::Registry::instance().registerHeader<CFConnectingIPHeader>();
|
||||||
Pistache::Http::Header::Registry::instance().registerHeader<XForwardedForHeader>();
|
Pistache::Http::Header::Registry::instance().registerHeader<XForwardedForHeader>();
|
||||||
|
Pistache::Http::Header::Registry::instance().registerHeader<GitProtocolHeader>();
|
||||||
|
|
||||||
g_pDB = std::make_unique<CDatabase>();
|
g_pDB = std::make_unique<CDatabase>();
|
||||||
|
|
||||||
|
|||||||
Submodule subprojects/pistache updated: 31ef83778e...67ca233776
Reference in New Issue
Block a user