Althttpd

Standalone mode in production?
Login

Standalone mode in production?

Standalone mode in production?

(1) By sodface on 2023-07-01 13:12:01 [link] [source]

When I first started using althttpd a few years ago, I packaged it for Alpine Linux. Alpine uses the openrc init system so I included openrc style init scripts for starting/managing althttpd as a service, eg. rc-service althttpd restart.

Alpine doesn't have inetd/xinetd available as a standalone package. There is a busybox version of inetd available if you install the busybox-extras package, which I did not know at the time I packaged althttpd, so basically, on Alpine, using the openrc init scripts, you need to specify --port in the command line arguments which means you are running in "standalone" mode.

The althttpd.md file notes:

Althttpd can also operate stand-alone. Althttpd itself listens on port 80 for incoming HTTP requests (or 443 for incoming HTTPS requests), then forks a copy of itself to handle each inbound connection. Each connection is still handled using a separate process. The only difference is that the connection-handler process is now started by a master althttpd instance rather than by xinetd.

Based on that, the openrc approach seemed to me to be basically equivalent to xinetd so that's how I've been running it.

Although this comment has always been present in the source code, I only just realized that this is, for me, an undesirable side effect of running in standalone mode in production:

In stand-alone mode (when the --port, --page, or --popup options are used) if neither the HTTP_HOST.website nor "default.website" directories exist, then files are served directly from the directory from which althttpd was launched. ... In one-connection mode (when launched from xinetd or similar) an error is raised if "default.website" does not exist.

I have a couple folders at the same level as the *.website folders and I do not have a default.website folder because there is a DNS entry out there for some domain I don't own that points to the IP address of my hosted virtual server and I didn't want my site content returned under that domain name (which it was when I did have a default.website entry).

Because of these sort of unconnected factors, I've ended up exposing content I did not intend to. If you knew the folder and filename for the assets at the same level as the *.website folders, you could specify those directly in the browser and althttpd would return them, as designed.

I realize this is operator error on my part. I'm not sure how I'm going to proceed. I think the possible solutions are:

  1. Reconfigure to use the inetd functionality provided by busybox-extras
  2. Package xinetd for myself or Alpine proper, though xinetd seems to be unmaintained
  3. Patch althttpd to return 404 if there's no *.website match
  4. Change distros (not!)

I'm using option 3 right now.

(2.2) By sodface on 2023-07-02 16:05:25 edited from 2.1 in reply to 1 [link] [source]

Now running in standalone mode with --ipshun as before but with the below patch applied. It appears to be working as intended, which is to enforce that HTTP_HOST must explicitly match a .website folder or default.website, otherwise the requester IP is shunned and the connection closed.

--- ./althttpd.c
+++ ./althttpd.c
@@ -2990,11 +2990,10 @@
   if( stat(zLine,&statbuf) || !S_ISDIR(statbuf.st_mode) ){
     sprintf(zLine, "%s/default.website", zRoot);
     if( stat(zLine,&statbuf) || !S_ISDIR(statbuf.st_mode) ){
-      if( standalone ){
-        sprintf(zLine, "%s", zRoot);
-      }else{
-        NotFound(350);  /* LOG: *.website permissions */
-      }
+      UnlinkExpiredIPBlockers();
+      BlockIPAddress();
+      closeConnection = 1;
+      althttpd_exit();
     }
   }
   zHome = StrDup(zLine);

(3) By anonymous on 2023-07-02 16:10:27 in reply to 2.2 [link] [source]

Why don't you make a default website that just returns a blank page or an error message?

(4) By sodface on 2023-07-02 17:11:25 in reply to 3 [link] [source]

Well, without a default.website, althttpd returns a 404 which is basically the same thing right?

To my (possibly faulty) thinking, a client that's making requests which don't explicitly match any of my .website entries is malicious and doesn't deserve the courtesy of a proper response, whether that's an error message or a blank page etc.

So by just immediately shunning them and closing the connection I'm hoping to:

  • Keep the noise out of my log
  • Fix my issue with hung processes (a regular 404 reply leaves the connection open)

I don't know if this is the best way to go about or if there will be unintended side effects, but as I said, I don't see any reason to respond politely (or at all) to obviously bogus requests.

(5.3) By sodface on 2023-07-02 18:52:20 edited from 5.2 in reply to 3 [source]

Sorry, I think I should clarify a bit since I've made a couple of conflicting statements in this thread. In the first post I said:

3 Patch althttpd to return 404 if there's no *.website match

And then in my last reply I said:

Well, without a default.website, althttpd returns a 404 which is basically the same thing right?

With an unpatched althttpd running in standalone mode, the logic (as I read it) is to look at the HTTP_HOST value in the client request and after some character substitutions, try to match it to .website directory. If no match, then use default.website if it exists. If it doesn't, then finally use "." as the content root.

Unless you know a specific path in "." and include that in the non-matching request (or there's an /home, /index, /index.html, /index.cgi, which I didn't have), you'll get a 404, so as far as I know every bogus request that wasn't matching a .website folder got a 404 back.

For example, my cheesy website server root looks like:

├── ipshun
├── log
├── sh
├── sodface_com.website
└── www_sodface_com.website -> sodface_com.website

If you were to go to http://168.235.110.154 you wouldn't match a .website folder and since there's no default.website althttpd starts looking for content at the level depicted above, eg ".".

Since there's no index.html etc, you'd get a 404 back unless you requested something specific like http://168.235.110.154/log/althttpd.log and then you'd be able to download my log file.

So when I said:

3 Patch althttpd to return 404 if there's no *.website match

I really meant always return a 404 when there's no *.website match. In my current solution I'm not even doing that, I'm just shunning and closing the connection.

(6.1) By sodface on 2023-07-02 18:41:45 edited from 6.0 in reply to 3 [link] [source]

Apologies again but even in my attempt to clarify I think I'm missing your point.

Why don't you make a default website that just returns a blank page or an error message?

An empty default.website folder should indeed ensure that there's always a match, would prevent access to content from the --root folder and would return a 404 for the junk requests. So that probably is a good suggestion in lieu of patching.

However, it doesn't keep the noise out of the logs, it doesn't shun (as quickly), and (in my case) it would still respond with a 404 to a request for a domain that I don't even own, which rubs me the wrong way.

Hopefully that's the last of my spam for a bit. No promises!

(7) By anonymous on 2023-07-03 08:17:12 in reply to 6.1 [link] [source]

Yes, that is what I was suggesting. But further, you could just drop a "home" executable in the default.website folder that returns a 422 http error code and drops the client IP into the ipshun folder directly, if you really wanted. Even a simple bash script would work for that. Or redirect permanent to a YouTube video of babyshark. Whatever floats your boat.