Skip to content

Instantly share code, notes, and snippets.

@Schnouki
Created August 13, 2014 07:20
Show Gist options
  • Save Schnouki/dee1766f2c7486dbf712 to your computer and use it in GitHub Desktop.
Save Schnouki/dee1766f2c7486dbf712 to your computer and use it in GitHub Desktop.
SSL client certificate / HTTP basic auth -- dual-method authentication for lighttpd
-- comfy-auth.lua
-- Copyright (c) 2014, Thomas Jost <schnouki@schnouki.net>
--
-- Permission to use, copy, modify, and/or distribute this software for any
-- purpose with or without fee is hereby granted, provided that the above
-- copyright notice and this permission notice appear in all copies.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-- PERFORMANCE OF THIS SOFTWARE.
local mime = require("mime")
local md5 = require("md5")
-- {{{ Basic auth support -- inspired by Yoo, but much simpler (https://github.com/stbuehler/yoo)
function readAuthFile(filename)
local users = {}
local line
local user, realm, pw
for line in io.lines(filename) do
if line:byte(1) ~= "#" then
user, realm, pw = string.match(line, "^([^:]*):([^:]*):(.*)$")
if user and realm then
users[user .. ":" .. realm] = pw
end
end
end
return users
end
Auth = {}
Auth.__index = Auth
function Auth.create()
local authfile = lighty.req_env["COMFY_AUTHFILE"]
local users = {}
if lighty.stat(authfile) then
users = readAuthFile(authfile)
else
print("comfy: Authfile '"..authfile.."' not found")
end
local auth = {}
setmetatable(auth, Auth)
auth.realm = lighty.req_env["COMFY_REALM"]
auth.users = users
return auth
end
function Auth:send_auth_headers()
if self.sent_header then return end
self.sent_header = true
lighty.header["WWW-Authenticate"] = "Basic realm=\"" .. self.realm .. "\""
end
function Auth:basic_check(s)
local user, pass, hash, pw
s = (mime.unb64(s))
user, pass = string.match(s, "([^:]*):(.*)")
if not user or not pass then return false end
for user, hash in pairs(self.users) do
if not hash then hash = "(nil)" end
end
hash = self.users[user .. ":" .. self.realm]
if not hash then return false end
pw = md5.sumhexa(user .. ":" .. self.realm .. ":" .. pass)
if pw == hash then
self.user = user
return true
else
return false
end
end
function Auth:check()
local res = self.checked
if res ~= nil then return res end
res = false
s = lighty.request["Authorization"]
if s and string.lower(string.sub(s, 1, 6)) == "basic " then
res = self:basic_check(string.sub(s, 7))
end
self.checked = res
return res
end
function Auth:enforce()
if not self:check() then
self:send_auth_headers()
return false
end
return true
end
-- }}}
-- {{{ Client SSL certificate check with fallback to basic auth
if lighty.req_env["REMOTE_USER"] then
--print("comfy: Remote user/SSL: " .. lighty.req_env["REMOTE_USER"])
else
local auth = Auth.create()
if auth:enforce() then
--print("comfy: Remote user/basic: " .. auth.user)
lighty.req_env["REMOTE_USER"] = auth.user
else
--print("comfy: 401")
return 401
end
end
-- }}}
server.modules += ( "mod_magnet", "mod_setenv" )
$HTTP["host"] == "my-private-domain.example.com" {
ssl.ca-file = "/etc/lighttpd/ca.pem"
# Authenticate using a client certificate
ssl.verifyclient.activate = "enable"
ssl.verifyclient.enforce = "disable"
ssl.verifyclient.username = "SSL_CLIENT_S_DN_CN"
# Always use HTTPS
$HTTP["scheme"] == "http" {
url.redirect = ( "^/(.*)" => "https://my-private-domain.example.com/$1" )
}
# Basic auth or client certificates with Comfy!
setenv.add-environment = (
"COMFY_AUTHFILE" => "/etc/lighttpd/htdigest",
"COMFY_REALM" => "My private domain"
)
magnet.attract-physical-path-to = ("/etc/lighttpd/comfy-auth.lua")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment