Advanced Namespace Tools blog 13 January 2017

Setting up Werc+Pegasus Again

Joy of Repeating Previous Werc Work

9gridchan.info was one of the first few sites to use Uriel's werc for content. Back then it was plan9port only, and even after it was available on Plan 9, it took me awhile to switch over. It wasn't until 2015 that I converted all my webstuff to run on Plan 9 exclusively. The biggest challenge was setting up the Pegasus web server to use werc. I have no particular reason for not using rc-httpd, I will probably start doing so at some point, but for the moment, I'm using a mix of original plan 9 httpd, pegasus, and tcp80 to do my web-serving.

I'm working through a series of updates and improvements to my infrastructure, so I wanted to get another node running basically the same setup as doc.9gridchan.info does. I remember it being tricky to get going, and even with a working setup, recreating the work was fairly tricky. Here is how it went:

Setting up Pegasus

At the time of this writing, the original Pegasus website at http://plan9.aichi-u.ac.jp is down. I hope it comes back up soon, but at least archive.org has a lot of the content mirrored. Pegasus has a fairly complicated setup process, so it was necessary to consult the online documentation frequently. As I write this post, archive.org is having server issues, so >< and @@ to all that. Anyway...

Start out by downloading, untarring, and mking the stuff in pegasus-2.8e.tar. This is the pegasus server itself, and the mon utility. I wanted to be able to serve https with a certificate chain, which required a small patch to pegasus, borrowing the code from the original plan9 httpd:

--- httpdorig.c	Fri Jan 13 07:18:29 2017
+++ httpd.c	Fri Jan 13 07:21:32 2017
@@ -93,6 +93,7 @@
 static char *webmnt; // to be "/usr/web/mnt"
 static int binding; // 0: main document, 1: virtual host doc, 2: user's doc
 static char *certificate;
+PEMChain *certchain;
 static uchar *cert;
 static int certlen;
 static int ntossed;
@@ -168,6 +169,11 @@
 		if(cert == nil)
 			sysfatal("reading certificate: %r");
 		break;
+	case 'd':
+		certchain = readcertchain(EARGF(usage()));
+		if (certchain == nil)
+			sysfatal("reading certificate chain: %r");
+		break;
 	case 'm':
 		mntok = 1;
 		break;
@@ -479,6 +485,8 @@
 				memset(&conn, 0, sizeof(conn));
 				conn.cert = cert;
 				conn.certlen = certlen;
+				if (certchain != nil)
+					conn.chain = certchain;
 				data = tlsServer(data, &conn);
 				if(data < 0){// added by Kenar
 					syslog(0, HTTPLOG, "error: tlsServer certificate=%s: %r", certificate);

After pegasus and mon were built, it was time to set up /usr/web the way pegasus expects. The example directory in pegasus has a framework provided.

mkdir /usr/web
dircp pegasus-2.8e/example/usr/web /usr/web

There are several configuration files that go in other places in the system, some which replace provided defaults. Make any needed backup copies and then:

cp pegasus-2.8e/example/lib/namespace.httpd /lib/namespace.httpd
cp pegasus-2.8e/example/sys/lib/httpd.conf /sys/lib/httpd.conf
cp pegasus-2.8e/example/sys/lib/httpd.rewrite /sys/lib/httpd.rewrite
touch /sys/log/blacklist /sys/log/http
chmod 666 /sys/log/blacklist /sys/log/http

Pegasus also expects to have the domain of incoming http requests in /lib/ndb/local. This could potentially cause conflicts with your existing auth setup. If you don't have a domain, I imagine it works to set the dom= to the ip. On the server I was just configuring, I have this entry in /lib/ndb/local:

sys=tip ip=108.61.229.146
	dom=tip.9gridchan.info

Without this, pegasus will answer every request with the message: "No dom". This doesn't seem to be mentioned in the pegasus documentation, fortunately examining the source code let me figure it out. I added the following entries to /sys/lib/httpd.rewrite, which dont even make sense to me because I have nothing but an unused index.html in the tip subdirectory, but they seem to be important for making things work:

http://tip	*/usr/web/tip
/	*/usr/web/tip

The content pegasus serves is taken from /usr/web/doc - so I added an index.html file there with the content "FOOMP" and tested the webserver according to the recommended invocation via mon:

cpu% b=/amd64/bin
cpu% $b/mon -du web $b/httpd -uM

Note that this is after the compiled pegasus was placed in /amd64/bin/httpd - the original plan9 httpd is at ip/httpd/httpd so there is no conflict. With this running, when I went to http://tip.9gridchan.info I saw "FOOMP" as desired.

Setting up Werc

This part involved a bit of vague, unhelpful information; sorry. I remembed that when I was setting up doc.9gridchan.info I had to make a couple modifications to the werc source code. I didn't remember what they were, so I just tarred up the werc directory and copied it over to the other server so I would import them automatically. Werc has been updated since I did the setup, so my particular changes might not be relevant or needed to the latest werc distribution. I will try to get a diff of them anyway, and also try out the newest werc and see what happens - at some point. Right now I just want to finish documenting what I did. [Later note: it seems like the only change is right at the beginning of werc.rc - I added "cd bin" immediately after the shell bang.]

There is some kind of weirdness with pegasus/werc involving the working directory of the invocation. I end up using two copies of the werc tree, which is probably a silly and inefficient way to do things. I untarred my werc in /usr/web/doc so its files were under /usr/web/doc/werc, then I made copies of the werc directories into /usr/web/doc/apps, /usr/web/doc/bin, /usr/web/doc/etc, /usr/web/doc/lib, /usr/web/doc/pub, /usr/web/doc/sites, /usr/web/doc/tpl and /usr/web/doc/_werc.

In /usr/web/doc/sites (not /usr/web/doc/werc/sites) I put a copy of the doc.9gridchan.info directory, and then made tip.9gridchan.info another copy of it. The invocation of cgi in pegasus is controlled by /usr/web/etc/handler so I created it to look like this, matching the subdirectories of the tip.9gridchan.info dir:

/them*	-	+	/doc/werc/bin/werc.rc
/blog*	-	+	/doc/werc/bin/werc.rc
/antfarm*	-	+	/doc/werc/bin/werc.rc
/old*	-	+	/doc/werc/bin/werc.rc
/guide*	-	+	/doc/werc/bin/werc.rc

With this done, things were almost working - I got an error of "markdown: invalid execution header" or something like that, which was because the default markdown formatter included in the version of werc that I had was a 386 plan 9 executable and I was running on amd64 on this node. Fortunately, an awk alternative was available (and I believe is now the default) so I just had to edit the /usr/web/doc/etc/initrc file used by werc to change the default by uncommenting the md2html.awk entry and commenting out the original markdown:

formatter=(md2html.awk)
#formatter=(fltr_cache markdown.pl)
#formatter=(markdown)

With this done, things were working ok, and all that remained was to restart pegasus and have it serve https using a cert I got from letsencrypt, as explained in another blog post:

cpu% k=/usr/glenda/stash/factotumkey
cpu% c=/usr/glenda/stash/tip.9gridchan.info.crt
cpu% $b/mon -du web -r $k $b/httpd -uM -p443 -c $c -d $c

And now https://tip.9gridchan.info/blog and friends are online.