Problem using webroot local folder - hash file not created
-
I'm trying to set up the acme plugin with the webroot local folder method in conjunction with haproxy and the LUA script from [https://github.com/janeczku/haproxy-acme-validation-plugin/blob/master/acme-http01-webroot.lua](link url).
Unfortunately, the acme plugin doesn't seem to be able to write the token file. I've checked the logs, both general and the acme log mentioned below, but nothing shows up. The directory in question exists as shown below. I've been scratching my head for a couple hours trying to solve this, with no luck. Any help would be appreciated
ls -al /tmp/haproxy_chroot/haproxywebroot/.well-known/acme-challenge/ total 8 drwxrw-rw- 2 root wheel 64 Aug 28 17:25 . drwxr-xr-x 3 root wheel 64 Aug 28 15:52 .. -rw-rw-rw- 1 root wheel 43 Aug 28 17:25 _s1qu-iKQquucEl99yMlOHgdssWW1LklUQe5cYnaDeM
I created the file above just to verify the script can access it. It's a different challenge file.
Here's the log from the acme client starting on the line it should have written the token file to:
put token at: /tmp/haproxy_chroot/haproxywebroot/.well-known/acme-challenge/4DTeprjHhqNIE_K8EJncCuoBfaONNJ_Rt1Db_rN5PbM [Sun Aug 28 18:01:38 PDT 2022] Pending, The CA is processing your order, please just wait. (1/30) [Sun Aug 28 18:01:41 PDT 2022] Found domain http api file: /tmp/acme/[redacted].cx//httpapi/pfSenseacme.sh [Sun Aug 28 18:01:41 PDT 2022] [redacted].cx:Verify error:99.78.100.117: Invalid response from http://[redacted].cx/.well-known/acme-challenge/4DTeprjHhqNIE_K8EJncCuoBfaONNJ_Rt1Db_rN5PbM: 404 [Sun Aug 28 18:01:41 PDT 2022] Please check log file for more details: /tmp/acme/[redacted].cx/acme_issuecert.log
-
Place a file like test.html with content "hello" in /tmp/haproxy_chroot/haproxywebroot/.well-known/acme-challenge/
and in http://[redacted].cx/.well-known/acme-challenge/Now, from the outside ( !!! ) check :
http://[redacted].cx/.well-known/acme-challenge/test.html
http://[redacted].cx/test.htmlCan you open / see these files ?
Also, when it says "4DTeprjHhqNIE_K8EJncCuoBfaONNJ_Rt1Db_rN5PbM" created,
was the file visible in tmp/haproxy_chroot/haproxywebroot/.well-known/acme-challenge/
use console or SFTP to check.
Did it contain a token ?edit : why would you need a LUA script from an external source to make acme.sh work ?
-
Now, from the outside ( !!! ) check :
The file is served as expected from an outside connection (phone with wifi disabled)
Also, when it says "4DTeprjHhqNIE_K8EJncCuoBfaONNJ_Rt1Db_rN5PbM" created,
was the file visible in tmp/haproxy_chroot/haproxywebroot/.well-known/acme-challenge/The file does not exist after the script fails. It's possible the acme script cleans up the file before I could check? I haven't tracked down the script source to troubleshoot there... That's probably the best course of action if all else fails.
find / -name "*4DTeprjHhqNIE_K8EJncCuoBfaONNJ_Rt1Db_rN5PbM*" returns no results
edit : why would you need a LUA script from an external source to make acme.sh work ?
I'm using HAProxy with an LUA script backend to serve the request. I've looked it over and it's harmless. It simply verifies the existence of the challenge file that the acme script wrote and returns the contents.
Is there a better way to serve the file from pfsense directly? I'm trying to avoid additional dependencies for certificate generation/renewal.
-
@bill-harris said in Problem using webroot local folder - hash file not created:
The file does not exist after the script fails. It's possible the acme script cleans up the file before I could check? I haven't tracked down the script source to troubleshoot there... That's probably the best course of action if all else fails.
I tracked down the location of the acme script and it does indeed clean up the token file by the time it's done. After removing that bit of code, I was able to verify that it's properly writing the file in the expected location. My problem is that the LUA script is failing to open the file and thus can't read/serve it from HAProxy. I'll figure it out and post the solution whether it's a filesystem or LUA permission thing.
-
@gertjan Looks like lua under haproxy is forbidden from doing file io because it blocks.. Time to figure out how to configure the local pfsense nginx process to serve the file.
-
FYI just today that script gave me hassles until it... just started to work.
Same script by Jan Broer as always, same config as always.
pfsense: 2.6.0-RELEASE (amd64) acme: 0.7.3 haproxy: 0.61_7
Frontend configuration:
ACL configuration
ACL Name: url_acme_http01 Expression: "Path starts with:" Value: /.well-known/acme-challenge/
Actions
Action: http-request lua service Condition acl names: METH_GET url_acme_http01 lua-function: acme-http01
Lua script in case I lose it again:
-- ACME http-01 domain validation plugin for Haproxy 1.6+ -- copyright (C) 2015 Jan Broer -- -- usage: -- -- 1) copy acme-webroot.lua in your haproxy config dir -- -- 2) Invoke the plugin by adding in the 'global' section of haproxy.cfg: -- -- lua-load /etc/haproxy/acme-webroot.lua -- -- 3) insert these two lines in every http frontend that is -- serving domains for which you want to create certificates: -- -- acl url_acme_http01 path_beg /.well-known/acme-challenge/ -- http-request use-service lua.acme-http01 if METH_GET url_acme_http01 -- -- 4) reload haproxy -- -- 5) create a certificate: -- -- ./letsencrypt-auto certonly --text --webroot --webroot-path /var/tmp -d blah.example.com --renew-by-default --agree-tos --email my@email.com -- acme = {} acme.version = "0.1.1" -- -- Configuration -- -- When HAProxy is *not* configured with the 'chroot' option you must set an absolute path here and pass -- that as 'webroot-path' to the letsencrypt client acme.conf = { ["non_chroot_webroot"] = "" } -- -- Startup -- acme.startup = function() core.Info("[acme] http-01 plugin v" .. acme.version); end -- -- ACME http-01 validation endpoint -- acme.http01 = function(applet) local response = "" local reqPath = applet.path local src = applet.sf:src() local token = reqPath:match( ".+/(.*)$" ) if token then token = sanitizeToken(token) end if (token == nil or token == '') then response = "bad request\n" applet:set_status(400) core.Warning("[acme] malformed request (client-ip: " .. tostring(src) .. ")") else auth = getKeyAuth(token) if (auth:len() >= 1) then response = auth .. "\n" applet:set_status(200) core.Info("[acme] served http-01 token: " .. token .. " (client-ip: " .. tostring(src) .. ")") else response = "resource not found\n" applet:set_status(404) core.Warning("[acme] http-01 token not found: " .. token .. " (client-ip: " .. tostring(src) .. ")") end end applet:add_header("Server", "haproxy/acme-http01-authenticator") applet:add_header("Content-Length", string.len(response)) applet:add_header("Content-Type", "text/plain") applet:start_response() applet:send(response) end -- -- strip chars that are not in the URL-safe Base64 alphabet -- see https://github.com/letsencrypt/acme-spec/blob/master/draft-barnes-acme.md -- function sanitizeToken(token) _strip="[^%a%d%+%-%_=]" token = token:gsub(_strip,'') return token end -- -- get key auth from token file -- function getKeyAuth(token) local keyAuth = "" local path = acme.conf.non_chroot_webroot .. "/.well-known/acme-challenge/" .. token local f = io.open(path, "rb") if f ~= nil then keyAuth = f:read("*all") f:close() end return keyAuth end core.register_init(acme.startup) core.register_service("acme-http01", "http", acme.http01)