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