- Network
- A
dumbproxy — what's new?
dumbproxy was already mentioned on tekkix in one of my previous articles. It is an HTTP proxy server that works over TLS, directly compatible with browsers and browser extensions, and has a significant number of additional features that extend its applicability and ease of use.
New Features
Over the past few months, the project has grown significantly. Let's go through the new features!
Authentication
In addition to basic login-password authentication and certificate authentication, it is now possible to use HMAC codes transmitted as login and password. At first glance, this is similar to JWT with HS256 signatures, but in a more compact, binary format.
This allows for centralized authorization on multiple instances of dumbproxy without the need to interact with the central authorization service for each access request. The client independently receives a login and password from the central service, which will be valid for some time, while such a login and password are not stored anywhere and are transmitted to the dumbproxy server by the client at the time of the request and can be verified without network interactions with the central authorization server. This feature may potentially interest developers of proxy services based on dumbproxy and allows it to be used as a proxy backend in such services.
Less significant: a certificate blacklist has been added when using certificate authentication.
Scripting
Some stages of request processing in dumbproxy can now be defined by JS scripts. Currently, this includes request filtering (allowing or denying access) and selecting an upstream proxy or outgoing network interface.
Request Filtering
Let's illustrate how this looks in practice. For example, here is an access script that replicates the rules from the standard Squid config, which restricts connections to "safe" ports:
// Deny unsafe ports for HTTP and non-SSL ports for HTTPS.
const SSL_ports = [
443,
]
const Safe_ports = [
80, // http
21, // ftp
443, // https
70, // gopher
210, // wais
280, // http-mgmt
488, // gss-http
591, // filemaker
777, // multiling http
]
const highPortBase = 1025
function access(req, dst, username) {
if (req.method == "CONNECT") {
if (SSL_ports.includes(dst.port)) return true
} else {
if (dst.port >= highPortBase || Safe_ports.includes(dst.port)) return true
}
return false
}
To apply it, just set the -js-access-filter
option with the path to the script.
Choosing an upstream proxy or outgoing address
It works similarly to the previous one, let's look at the examples.
Need to send onion domains to Tor? Easy.
// Redirect .onion hidden domains to Tor SOCKS5 proxy
function getProxy(req, dst, username) {
if (dst.originalHost.replace(/\.$/, "").toLowerCase().endsWith(".onion")) {
return "socks5://127.0.0.1:9050"
}
return ""
}
Spread traffic across multiple machines with address change every 10 minutes? Easy.
const serverList = [
"https://USERNAME:[email protected]:443",
"https://USERNAME:[email protected]:443",
"https://USERNAME:[email protected]:443",
"https://USERNAME:[email protected]:443",
"https://USERNAME:[email protected]:443",
"https://USERNAME:[email protected]:443",
"https://USERNAME:[email protected]:443",
"https://USERNAME:[email protected]:443",
]
const rotationPeriod = 600 // 10 minutes
function getProxy() {
const ts = Math.floor(Date.now() / 1000)
return serverList[Math.floor(ts / rotationPeriod) % serverList.length]
}
Send traffic to some domains in a VPN tunnel? Well... if the PBR rules for choosing the routing table based on the outgoing address are already ready, then please.
function getProxy(req, dst, username) {
const normalizedHostname = dst.originalHost.replace(/\.$/, "").toLowerCase()
if (normalizedHostname === "2ip.ru" || normalizedHostname.endsWith(".2ip.ru")) {
return "set-src-hints://?hints=10.2.0.2"
}
return ""
}
Caching issued certificates
dumbproxy has long supported automatic certificate issuance for its domain name, and a simple certificate cache in the local directory was used for this. However, now the capabilities in this area have been expanded:
Now Redis and Redis Cluster can be used as storage.
An optional in-process memory caching layer is available.
Optional cache encryption with
AEAD encryption (XChaCha20Poly1305)
Moving the certificate cache to external storage paves the way for stateless deployments. That is, the server does not store any state locally and can be deleted/recreated every 10 minutes without problems with (re)issuing certificates. By the way, free Redis instances on https://cloud.redis.io/ do a good job, the options file looks something like this:
OPTIONS=-auth basicfile://?path=/var/lib/dumbproxy/dumbproxy.htpasswd -autocert -autocert-local-cache-ttl 24h -autocert-cache-redis redis://default:xxxxxxxx@redis-16273.c528.europe-west3-1.gce.redns.redis-cloud.com:16273/0
DUMBPROXY_CACHE_ENC_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Features for operators of large installations
Local DNS cache
An option has appeared that forcibly caches the correspondence of domain names to addresses. It is mainly needed by proxy operators with many clients who for some reason use public DNS resolvers like 8.8.8.8 and 1.1.1.1, and receive negative responses from them for the high frequency of requests.
Instead, I would rather advise deploying a local knot-resolver or unbound with a cache, but this is not acceptable for everyone.
Bandwidth limit for each user
This feature is useful for the same operators of large installations to more fairly distribute the channel between users, and in general to make overconsumption (for example, YouTube in 4k) impractical.
There are a few more little things that I have not mentioned, but it is better to leave this to the attentive readers of the documentation and talk about deployment.
New deployments
And deploying dumbproxy with authorization and generally accepted TLS certificates is now very simple. You don't even need to log into the server via SSH!
For hosting providers that support cloud-init (24.3.1 or newer, take ubuntu 24.10 - you won't go wrong), it is enough to specify the following config when creating a virtual machine (don't forget to replace USERNAME
and PASSWORD
with real ones!):
## template: jinja
#cloud-config
write_files:
- source:
{% if v1.machine == 'aarch64' %}
uri: https://github.com/SenseUnit/dumbproxy/releases/download/v1.20.0/dumbproxy.linux-arm64
{% else %}
uri: https://github.com/SenseUnit/dumbproxy/releases/download/v1.20.0/dumbproxy.linux-amd64
{% endif %}
path: /usr/local/bin/dumbproxy
permissions: '0755'
- content: |
OPTIONS=-auth basicfile://?path=/etc/dumbproxy.htpasswd -autocert -bind-address :443
path: /etc/default/dumbproxy
- content: |
[Unit]
Description=Dumb Proxy
Documentation=https://github.com/SenseUnit/dumbproxy/
After=network.target network-online.target
Requires=network-online.target
[Service]
EnvironmentFile=/etc/default/dumbproxy
User=root
Group=root
ExecStart=/usr/local/bin/dumbproxy $OPTIONS
TimeoutStopSec=5s
PrivateTmp=true
ProtectSystem=full
LimitNOFILE=20000
[Install]
WantedBy=default.target
path: /etc/systemd/system/dumbproxy.service
runcmd:
- [dumbproxy, -passwd, /etc/dumbproxy.htpasswd, USERNAME, PASSWORD]
- [systemctl, daemon-reload]
- [systemctl, enable, --now, --no-block, dumbproxy.service]
Just in case, if there are any problems with orientation, the window for cloud-init usually looks something like this:
It remains to attach a domain name to the machine's IP address and everything is ready. If there is no domain name, here is a list of ideas where to get it:
https://www.dnshome.de/ — choose only subdomains *.dnshome.de, on the rest there may be problems with certificate issuance limits.
It can be said that this list of free domain names is exhaustive. Any others (e.g. nip.io) are not present in the public domain suffix list and will have problems with certificate issuance due to limits.
You can recall how to configure clients in the previous post (archive).
Write comment