Compare commits
2 commits
616c17501a
...
ceebd53626
| Author | SHA1 | Date | |
|---|---|---|---|
| ceebd53626 | |||
| 07ae82809e |
3 changed files with 64 additions and 11 deletions
47
README.md
47
README.md
|
|
@ -1,11 +1,22 @@
|
|||
# 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 using Realm completely? Because the hook system is useless and only allows filtering.
|
||||
|
||||
And because it looked like a fun little project.
|
||||
|
||||
## 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`.
|
||||
|
||||
## Challenge protocol
|
||||
|
|
@ -14,24 +25,28 @@ Must be used behind a reverse proxy providing `X-Forwarded-For`.
|
|||
|
||||
Sent by the server as a cookie.
|
||||
|
||||
`secret <- chosen randomly, long term`
|
||||
|
||||
`salt <- chosen randomly, not stored`
|
||||
|
||||
`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)`
|
||||
* `secret`: chosen randomly at startup
|
||||
* `salt`: chosen randomly each time
|
||||
* `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))`
|
||||
|
||||
Where `BASE64` is unpadded.
|
||||
Where `BASE64` is URL-safe unpadded.
|
||||
|
||||
### Challenge verification
|
||||
|
||||
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
|
||||
|
||||
### Network handling and HTTP parsing
|
||||
|
|
@ -50,6 +65,18 @@ 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.
|
||||
|
||||
## 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
|
||||
|
||||
[Support me via LiberaPay](https://liberapay.com/tuxmain/donate)
|
||||
|
|
|
|||
|
|
@ -4,9 +4,29 @@
|
|||
<meta charset="utf-8"/>
|
||||
<meta name="robots" content="noindex"/>
|
||||
<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>
|
||||
<body>
|
||||
<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">
|
||||
const target_zeros = 15;
|
||||
const sha256 = async (input) => {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use http::HeaderLineIterator;
|
|||
use policy::{CompiledPolicies, Policy};
|
||||
|
||||
use rand::Rng;
|
||||
use realm_syscall::socket2::TcpKeepalive;
|
||||
use regex::bytes::Regex;
|
||||
use std::{io::Write, net::SocketAddr, time::Duration};
|
||||
use tokio::{
|
||||
|
|
@ -234,10 +235,15 @@ async fn main() {
|
|||
}
|
||||
|
||||
async fn do_proxy(pass_addr: SocketAddr, mut client_stream: TcpStream) {
|
||||
// TODO reuse connections
|
||||
let keepalive_dur = Duration::from_secs(15);
|
||||
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();
|
||||
|
||||
pass_socket.set_reuse_address(true).ok();
|
||||
pass_socket.set_tcp_keepalive(&keepalive).ok();
|
||||
|
||||
let pass_socket = TcpSocket::from_std_stream(pass_socket.into());
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue