Zirias
Joined 3 years ago
Joined 3 years ago
* Performance improvements by adding support for epoll() (with singalfd and timerfd), kqueue() and solaris event ports, and also by fixing a few design issues
* Server side sessions are gone, instead signed Json Web Tokens are implemented -> saves lots of RAM
Among other things, swad can do what Anubis can do, that's why it's in the title. It's much smaller though, and proof-of-work-crypto-challenge-to-defeat-bots is just ONE module, the other modules offering more regular means of authentication: by calling some external tool, by checking a bcrypt hash from a file, or by asking PAM.
Contrary to Anubis, it's NOT a reverse proxy but designed to work with nginx' auth_request facility instead, might also work with other reverse proxies if they offer something similar. Swad is written in C, compiles to a 200-300kiB (depending on compiler and platform) binary, needs only zlib, OpenSSL (LibreSSL) and optionally libpam. Doing some stress testing with 1000 distinct simulated clients, I managed to bump up the resident set on my FreeBSD machine to 100MiB, but no more. Without heavy load, it typically stays at much less.
The general architecture of swad is a reactor with attached thread pool. A HTTP request is parsed (to headers and body) in the main thread, then a thread job is created to run the handler pipeline (including response compression, cookie management, form and query string parsing, etc) that's expected to add a HTTP response to some context object, once that's finished, the main thread takes over again sending that out to the client.
During my stress test mentioned above, I was kind of surprised to see the main (reactor) thread almost maxing out one core, while the pool threads (running request pipelines) didn't have too much to do. Well, serving a small login form and processing login requests indeed isn't THAT much work. Maybe it might make sense to have more than one "reactor thread"? Could this work, e.g. by just listening on the same socket from multiple threads?
I use it myself on my private server to protect my package build logs (!) from hungry AI bots. WTF...
There's a background story. For a few months, I was wondering why most of the time, I couldn't activate my camera in MS Teams meetings (for work) without completely killing my audio ... obviously a bandwidth issue with my upstream that hasn't been there before. Then I stumbled across some article, I think it was ars technica, describing a horrible problem with misbehaving AI bots crawling for data with no limits and no respect (and even trying disguises), causing extreme amounts of traffic and corresponding problems for opensource projects hosting their own infrastructure. Gave me the idea to finally check my nginx logs again. Ok, it was full of bots. The interesting thing is, I don't host any opensource code there. What they downloaded were build logs of my "poudriere" (a package builder for FreeBSD), which can get very large, and I just had them public because there was no reason to hide them ... well until now. WTF do those bots learn from that, uhm, "shit scrolling by"?
So, what to do here? Although I really felt inclined to try one of the nice and naughty tools to poison these bots (IDK, is build logs already some kind of poisoning?), it was clear the pragmatic thing to do is add authentication. Well, you can easily add some HTTP "Basic" auth, but that's far from great. You're typically limited to checking credentials against some "password file", and worse, e.g. Microsoft already decided that "Basic" is "insecure" (oh dear, really? I don't think so as long as you use TLS) and disabled it by default in their "Edge" browser. And chances are you can't change that when using some device you got from your employer.
I did have some old dusty code implementing some very limited HTTP stuff in C lying around that was never published (only used it for experiments with some darknet "hidden services"). I thought it might be fun to implement something. Yep, it was fun. And call me stubborn, but I like doing stuff in plain C, small, efficient, few dependencies. Finally this old HTTP code got some polish and is released as part of something actually useful.
For completeness, I'm well aware I could have taken a very different road. "Just" install some identity provider implementing OIDC. For example keycloak. Well sure, that's awesome stuff. And huge and complex. If you don't have a massively distributed deployment of services and don't intend to ever federate with other organizations, it just felt like using the H bomb to kill a fly.
You can't randomly mix languages in source code, and any other choice of common language than english would exclude a LOT more people from the project.
All of this isn't related to i18n/l10n of your application at all. People not knowing english is a much more relevant factor when talking about user interfaces. I actually plan to localize my Xmoji tool eventually, it's just postponed until more important stuff is done (I mean, I assume most people would get together enough english to be able to use an emoji keyboard after all, but of course, especially seaching would benefit from l10n).
> Also, there seem to be no sane API for managing layouts or switching > them programatically
Layouts only exist as "data" as far as the X server is concerned, clients must fetch them and map themselves from the key codes. Libraries like xkbcommon (or even grandfather Xlib) do the job for you. That said, there are APIs to modify the mapping (by publishing messages/events) as you wish. The ugliness is, apart from the fact that you're forced to fiddle with the mapping at all, that you can't guarantee another client will process everything in sequence. It might apply a new mapping before processing all its queued key press events. That's why my code adds delays between fiddling with the mapping and sending the events.
Yes, it's extremely ugly. Still, at least for me, it works. Try it out ;)
And yes, Windows is doing better here, there's a Unicode-flavor of keyboard events available.
That was the main reason for me to pull in harfbuzz. At least it "just resolves" those sequences to glyph indices.
Still, in practice, it will only work with some applications...
Sending fake key events is unfortunately "hacky". You need to temporarily switch the keyboard mapping, and many X applications have races between applying a new mapping and processing key press events, that's why I added (now configurable, new release soon) delays... and also, I found applications (e.g. chromium) having issues to correctly combine ZWJ sequences from individual key events, so I added more "hacks" to help with that, also just made configurable.
To sidestep these issues, my tool also offers transfer via "primary selection": Just middle-click an emoji to select it, and middle-click again where you want to insert it. Downside: Two clicks instead of just one.
Of course you can do a lot of things with "input methods" (like popping up some "picker" only when wanted). But then you'll depend on X clients being aware of them in some way, e.g. by implementing the XIM protocol. Lots of applications have that (even xterm), so it might be what you prefer and that's fine. My method with faked key press events is definitely hacky, but will work with any X client, that's the point of it ;)
Might be overkill to tune such a simple tool for performance, but I hope to reuse the code eventually for other web services.
Features: Authentication with PAM, a htpasswd-like file with bcrypt hashes, executing some external tool, AND "proof-of-work" crypto challenge (to block malicious bots).