Compare commits
No commits in common. "ceebd536266f3b2ec1c733f809c2fd86029a6945" and "616c17501ad55ff7cc1855ec24c4fd71da881ff1" have entirely different histories.
ceebd53626
...
616c17501a
3 changed files with 11 additions and 64 deletions
47
README.md
47
README.md
|
|
@ -1,22 +1,11 @@
|
||||||
# Mesozoa
|
# Mesozoa
|
||||||
|
|
||||||
Intermediate reverse proxy that protects your server from crawlers by forcing the browser to run proof of work.
|
|
||||||
|
|
||||||
## Why?
|
|
||||||
|
|
||||||
Why not Anubis? Because it provides no build instructions and only supports Docker.
|
Why not Anubis? Because it provides no build instructions and only supports Docker.
|
||||||
|
|
||||||
Why not using Realm completely? Because the hook system is useless and only allows filtering.
|
Why not using Realm completely? Because the hook system is useless and only allows filtering.
|
||||||
|
|
||||||
And because it looked like a fun little project.
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
[Install rustup](https://rustup.rs) and a nightly Rust toolchain.
|
|
||||||
|
|
||||||
# Build executable at ./target/release/mesozoa
|
|
||||||
cargo build --release
|
|
||||||
|
|
||||||
Must be used behind a reverse proxy providing `X-Forwarded-For`.
|
Must be used behind a reverse proxy providing `X-Forwarded-For`.
|
||||||
|
|
||||||
## Challenge protocol
|
## Challenge protocol
|
||||||
|
|
@ -25,28 +14,24 @@ Must be used behind a reverse proxy providing `X-Forwarded-For`.
|
||||||
|
|
||||||
Sent by the server as a cookie.
|
Sent by the server as a cookie.
|
||||||
|
|
||||||
* `secret`: chosen randomly at startup
|
`secret <- chosen randomly, long term`
|
||||||
* `salt`: chosen randomly each time
|
|
||||||
* `timestamp`: UNIX time in seconds, 64 bits, big endian
|
`salt <- chosen randomly, not stored`
|
||||||
* `ua`: `User-Agent` from request header
|
|
||||||
* `ip`: `X-Forwarded-For` from request header (client's IP)
|
`timestamp <- UNIX time in seconds, 64 bits, big endian`
|
||||||
|
|
||||||
|
`ua <- User-Agent from request header`
|
||||||
|
|
||||||
|
`ip <- X-Forwarded-For from request header (client's IP)`
|
||||||
|
|
||||||
`set-cookie: mesozoa-challenge=BASE64(salt || timestamp || SHA3-256(secret || salt || timestamp || ip || "/" || ua))`
|
`set-cookie: mesozoa-challenge=BASE64(salt || timestamp || SHA3-256(secret || salt || timestamp || ip || "/" || ua))`
|
||||||
|
|
||||||
Where `BASE64` is URL-safe unpadded.
|
Where `BASE64` is unpadded.
|
||||||
|
|
||||||
### Challenge verification
|
### Challenge verification
|
||||||
|
|
||||||
Request must contain both cookies `mesozoa-challenge` and `mesozoa-proof`.
|
Request must contain both cookies `mesozoa-challenge` and `mesozoa-proof`.
|
||||||
|
|
||||||
Server checks challenge is correct and timestamp not too old.
|
|
||||||
|
|
||||||
`cookie: mesozoa-proof=nonce`
|
|
||||||
|
|
||||||
`hash = SHA2-256(nonce || challenge)`
|
|
||||||
|
|
||||||
Client must find a `nonce` matching `/[0-9a-zA-Z_-]{8}/` such that `hash` starts with at least some number of zeros (in binary representation, MSB-first).
|
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
### Network handling and HTTP parsing
|
### Network handling and HTTP parsing
|
||||||
|
|
@ -65,18 +50,6 @@ SHA2 (used for PoW) is vulnerable but nonce is at the beginning so this is not a
|
||||||
|
|
||||||
I would like a better PoW: memory-bound and ideally non-parallel. Cuckoo seems a good candidate.
|
I would like a better PoW: memory-bound and ideally non-parallel. Cuckoo seems a good candidate.
|
||||||
|
|
||||||
## Contribution
|
|
||||||
|
|
||||||
Patches and forks are welcome. Send an e-mail to `tuxmain ât zettascript ðøt org`.
|
|
||||||
|
|
||||||
If people are interested, I may switch to a public forge like Codeberg.
|
|
||||||
|
|
||||||
The "A" in GNU AGPL means that if you host a publicly available instance of a modified version, then you should also make the modified source code available to users.
|
|
||||||
For example, this can take the form of a link to a repository in the challenge page or in the protected website.
|
|
||||||
As the challenge page's source code is directly distributed by the server, you can modify it independently.
|
|
||||||
(unless adding a compiled object, like WASM. Then you have to publish its source.)
|
|
||||||
Configuration file can be modified and kept secret, of course.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[Support me via LiberaPay](https://liberapay.com/tuxmain/donate)
|
[Support me via LiberaPay](https://liberapay.com/tuxmain/donate)
|
||||||
|
|
|
||||||
|
|
@ -4,29 +4,9 @@
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<meta name="robots" content="noindex"/>
|
<meta name="robots" content="noindex"/>
|
||||||
<title>Antispam working...</title>
|
<title>Antispam working...</title>
|
||||||
<style type="text/css">
|
|
||||||
html {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
html {
|
|
||||||
background-color: black;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Fighting crawlers</h1>
|
<h1>Fighting crawlers</h1>
|
||||||
<p>
|
|
||||||
This service is protected against abusive crawlers by a proof of work mechanism.<br/>
|
|
||||||
We are sorry for the inconvenience.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Powered by <a href="https://git.txmn.tk/tuxmain/mesozoa" rel="nofollow">Mesozoa</a>,<br/>
|
|
||||||
distributed under license GNU AGPL v3 without any warranty.
|
|
||||||
</p>
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
const target_zeros = 15;
|
const target_zeros = 15;
|
||||||
const sha256 = async (input) => {
|
const sha256 = async (input) => {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ use http::HeaderLineIterator;
|
||||||
use policy::{CompiledPolicies, Policy};
|
use policy::{CompiledPolicies, Policy};
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use realm_syscall::socket2::TcpKeepalive;
|
|
||||||
use regex::bytes::Regex;
|
use regex::bytes::Regex;
|
||||||
use std::{io::Write, net::SocketAddr, time::Duration};
|
use std::{io::Write, net::SocketAddr, time::Duration};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
|
|
@ -235,15 +234,10 @@ async fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn do_proxy(pass_addr: SocketAddr, mut client_stream: TcpStream) {
|
async fn do_proxy(pass_addr: SocketAddr, mut client_stream: TcpStream) {
|
||||||
let keepalive_dur = Duration::from_secs(15);
|
// TODO reuse connections
|
||||||
let mut keepalive = TcpKeepalive::new().with_time(keepalive_dur);
|
|
||||||
keepalive = TcpKeepalive::with_interval(keepalive, keepalive_dur);
|
|
||||||
keepalive = TcpKeepalive::with_retries(keepalive, 3);
|
|
||||||
|
|
||||||
let pass_socket = realm_syscall::new_tcp_socket(&pass_addr).unwrap();
|
let pass_socket = realm_syscall::new_tcp_socket(&pass_addr).unwrap();
|
||||||
|
|
||||||
pass_socket.set_reuse_address(true).ok();
|
pass_socket.set_reuse_address(true).ok();
|
||||||
pass_socket.set_tcp_keepalive(&keepalive).ok();
|
|
||||||
|
|
||||||
let pass_socket = TcpSocket::from_std_stream(pass_socket.into());
|
let pass_socket = TcpSocket::from_std_stream(pass_socket.into());
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue