Best practices in Gemini servers

Sean Conner sean at conman.org
Tue Sep 10 03:07:41 BST 2019
  To get away from the text formatting argument (which I think comprises
over half the messages so far) I thought I might ask for some guidelines as
to the best practice for Gemini servers.  I came up with these as I was
reworking my Gemini server (getting ready to release it, as I've had one
person already request a copy).  So here we go:

  I have this bit of code to check the request:

	  if loc.scheme ~= 'gemini'
	  or loc.host   ~= CONF.network.host
	  or loc.port   ~= CONF.network.port then
	    log(ios,59,"",reply(ios,"59\t",MSG[59],"\r\n"))
	    ios:close()
	    return
	  end

  I'm currently returning a "Bad Request" for this, if the protocol, host
and port don't match what is currently configured on my server.  The other
possible status is "Proxy Request Refused".  My server doesn't do proxy
requests.  What should the proper status code be?  Is "Bad Request" fine
here?

  Next.  Each request is run through a series of checks to see if a resource
has moved or is gone:

	redirect =
	{
	  permanent = { ... } -- list of permenent redirects
	  temporary = { ... } -- list of tempoarary redirects
	  gone      = { ... } -- list of gone items
	}

  For any given list, it's checked in the order presented, but the ordering
of the major sections is currently defined as: temporary, permanent then
gone.  I keep going back and forth if this shouldn't be: gone, permanent,
temporary or even if it matters.

  Next.  The actual order of things is:

	read request
	check redirects
	check authorization
	check handlers

  Each of the checks consist of the path matched against a pattern, and the
first match wins.  So for instance (the path fields, for instance, are Lua
patterns---think regex):

	redirect =
	{
	  permanent =
	  {
	    { '^/source%-code$'     , "/sourcecode/"   } ,
	    { '^/source%-code/(.*)' , "/sourcecode/$1" } ,
	  }
	},

	authorization =
	{
	  {
	    path   = '^/private/',
	    status = 61,
	    check  = function(issuer,subject,url) ... end,
	  },
	
	  {
	    path   = '^/conman%-labs%-private/',
	    status = 62,
	    check  = function(issuer,subject,url) ... end,
	  },
	},

	handler =
	{
	  {
	    path   = '^(/hilo/)(.*)',
	    module = "hilo",
	  },
	
	  {
	    path   = '^/test/wrap(%;?(%d*))',
	    module = "wrap",
	  },
	
	  {
	    path      = ".*",
	    module    = "filesystem",
	    directory = "gemini.conman.org",
	    no_access =
	    {
	      "^%.",  -- no to any dot files
	      "%~$",  -- no to any backup files
	    },
	  },

  So given a path like "/private/Welcome.txt", there are no redirects that
match, so it's not redirected.  It does match one of the authorization
patterns, so a check for the certificate is made, and if it passes that
(it's done that way so that the individual handlers don't have to handle
authentication themselves), then it goes through the handlers to match the
last one, pointing to the "filename" handler.  My question here, or more of
a concern really, is I think I have the ordering wrong---I think I should
check authorization first before redirection checking, because not doing so
may lead to leaking of protected resource names.

  So my question here, does it make sense to have the order be:

	check request
	check authorization
	check redirection
	check handlers

to prevent possible leaking of data?  I'm thinking yes, but wouldn't mind
seeing a discussion.

  -spc (So anyway, back to our regularly schedule talk on text formatting ... )


Archive home | About this archive