Certbot to provide webroot-path to the --manual-auth-hook and --manual-cleanup-hook and event if it's auth or cleanup

Hi, I am trying to implement custom DNS verification via golang.
It would be really nice if certbot passes CERTBOT_WEBROOT_PATH environment variable if it was invoked with it. The reason that I'd need this is to save 1 DNS request.

The command below will try to verify staging.example.com via HTTP and *.staging.example.com via DNS. My current workaround is to manually pass DOCUMENT_ROOT=/var/www/apps/qsandbox/staging.example.com/htdocs followed by the command below but it's not ideal.
Any ideas?

certbot certonly \
--manual \
--test-cert \
--webroot-path /var/www/apps/qsandbox/staging.example.com/htdocs \
--manual-auth-hook /home/slavi/orbisius_dns_manager \
--manual-cleanup-hook /home/slavi/orbisius_dns_manager \
--email admin+apps+qsandbox+stg@example.com \
--rsa-key-size 4096 \
--agree-tos \
--manual-public-ip-logging-ok \
-d staging.example.com \
-d *.staging.example.com | tee -a ssl_tests.log

Welcome to the community @lordspace

I'm not sure why a webserver folder is important for a DNS challenge. Your webserver is not used for that.

There are a number of variables passed to the auth hooks. Isn't one of them enough? See

certbot --help manual

manual in this case is help on the manual method - not displaying the manual :slight_smile:

3 Likes

Thanks Mike for the reply.
As I have said with the command above it does a mixed validation.

For staging.example.com certbot does HTTP challenge as I don't want to specify preferred challenge.
Also by using HTTP I am saving 2 DNS API calls (one to create and one to delete the record)

For the wildcard *.staging.example.com yes, DNS challenge is required and webroot is not needed indeed.

It seems certbot calls my --manual-auth-hook for both validations that's why I need to have document root for the first validation.

as a workaround I could do both via DNS but that can slow things down as I have to wait 30 seconds after each TXT record.

Also it would be nice if certbot provides the event to the hooks e.g. auth or cleanup or something like that. I am using the same program for both hooks and my program decides to do if the challenge files exist.

Does it make sense now?

Ah, yes, clearer now thanks. I was thrown off by your above comment.

First, are you setting up a hosting service or integration for others? Because otherwise saving a minute every 60 days seems a small price to pay. I doubt Certbot devs time is best spent for this.

I do have a suggestion. Make your own script let's call it mycert.sh. mycert would accept a domain name and folder as parameters. mycert would then set the DOCUMENT_ROOT folder you currently use (or some other name). Then it would invoke certbot.

Creating two hook scripts seems an easy work-around to knowing if it is auth or cleanup.

That's all I have.

4 Likes

Hi Mike,

It would be 1-2 new environment variables. I'd expect this to be 2-5 lines of code with the checks.
The change is so small that even tests don't have to be run :slight_smile:

I do have some sort of hosting service.
I prefer my own integration rather than using the default plugins as I want to trigger my own hooks later on

Certbot code is in github. You could create a PR for the change you seek :slight_smile:

4 Likes

That is an unsound idea and practice. :roll_eyes:

3 Likes

I know but you got the idea what I meant.
I created a github ticket just in case

1 Like

I was actually suggesting you make the Pull Request. That's the actual code change you request. Needs to include the code, any help and docs, and usually tests.

That makes it easier for the devs to update it. That said, the certbot team at EFF is small and you can see many PR's are outstanding
https://github.com/certbot/certbot/pulls

If you want something working today, it is best to work on your own solution. Perhaps along the lines I suggested.

EDIT: @lordspace You might also want to consider using a different ACME client. Maybe acme.sh provides better integration options for you. It supports many more DNS API's (although not oribisius) and I am pretty sure it allows mixed challenge types.

3 Likes

Thanks. I found a 3rd workaround. My program to test for /var/www/vhosts/domain.com/htdocs

P.S. orbisius is my company name

2 Likes

Actually, the Certbot dev team recommends to talk to the devs on Mattermost or by using an issue first before opening a PR. This is due to the limited resources of the dev team, leading to many PRs not being reviewed.

That said, I don't think Certbot is able to perform different challenges in one run. It might use cached valid authorizations from previous runs, ending up with a "mixed" challenge/authz order, but I don't think it can do multiple new challenges with different types. I was incorrect. Certbot actually does automatically use dns-01 for wildcard certs and uses the default http-01 challenge for the non-wildcard certificates. So your request makes much sense indeed :slight_smile:

An alternative option is to hardcode the webroot path in your authorization script or figure it out in the script somehow if not hardcoded. By checking the presence of the CERTBOT_TOKEN env var, you can see if the challenge being used is the http-01 challenge (if present) or not (if absent).

That said, it shouldn't be too hard for Certbot to introduce a few variables to make the auth hooks more manageable I'd say.

4 Likes

Great! Yes. I am exactly using CERTBOT_TOKEN to tell if it's an http challenge.
I'd prefer to not hardcode any paths because I am going to use that program in several of my products.

I so agree certbot should populate the hook environment with the various command line args…

But I think we should have a template response for the “feature requests” topic that directs people to GitHub. There are several of these per week.

2 Likes

While I understand the sentiment, I'd rather have the discussion here first than to possibly pollute the Certbot Github repo with issues doomed to be closed anyway.

3 Likes

These simple modifications add an env var CERTBOT_WEBROOT_PATH with the value of the --webroot-path option:

diff --git a/certbot/certbot/_internal/plugins/manual.py b/certbot/certbot/_internal/plugins/manual.py
index a5c9559e4..6a8790ff5 100644
--- a/certbot/certbot/_internal/plugins/manual.py
+++ b/certbot/certbot/_internal/plugins/manual.py
@@ -196,9 +196,14 @@ permitted by DNS standards.)
             "CERTBOT_REMAINING_CHALLENGES": str(len(achalls) - achalls.index(achall) - 1),
         }
         if isinstance(achall.chall, challenges.HTTP01):
+            if len(self.config.namespace.webroot_path) == 1:
+                env['CERTBOT_WEBROOT_PATH'] = self.config.namespace.webroot_path[0]
+            else:
+                env['CERTBOT_WEBROOT_PATH'] = None
             env['CERTBOT_TOKEN'] = achall.chall.encode('token')
         else:
             os.environ.pop('CERTBOT_TOKEN', None)
+            os.environ.pop('CERTBOT_WEBROOT_PATH', None)
         os.environ.update(env)
         _, out = self._execute_hook('auth-hook', achall.domain)
         env['CERTBOT_AUTH_OUTPUT'] = out.strip()
@@ -242,6 +247,8 @@ permitted by DNS standards.)
                 env = self.env.pop(achall)
                 if 'CERTBOT_TOKEN' not in env:
                     os.environ.pop('CERTBOT_TOKEN', None)
+                if 'CERTBOT_WEBROOT_PATH' not in env:
+                    os.environ.pop('CERTBOT_WEBROOT_PATH', None)
                 os.environ.update(env)
                 self._execute_hook('cleanup-hook', achall.domain)
         self.reverter.recovery_routine()

It only works when just one webroot-path is present, as it's possible with the webroot plugin to specifify multiple webroots for different FQDNs. However, that's way too much work for these few minutes, so I've just added a check for the number of webroot paths: if a single one is present, provide it as env var, if there is zero or more than 1: ignore it :rofl:

5 Likes

Cool! :smiley: great job! Simple is good. If only one webroot is specified then pass it. Perfect!

2 Likes

I thought about the name for the other env variable the instead of event it's better to be hook which could be auth or cleanup

CERTBOT_MANUAL_HOOK
or
CERTBOT_MANUAL_HOOK_NAME

You can also check for the presence of the variable CERTBOT_AUTH_OUTPUT. If it's present, it was a cleanup, if it's absent, it was an auth hook. Note that the variable can also be empty when the auth hook didn't generate any output.

4 Likes

Certbot also has a webroot-map argument.

I can't speak for the Certbot team, but nearly every project I've been a part of would require a PR for CERTBOT_WEBROOT_PATH to also handle CERTBOT_WEBROOT_MAP.

I don't think this code would pass a PR though. As @Osiris said:

Looking quickly at the code, it seems that certbot generates an internal mapping of domains to webroots based on both the '-map' and -path args. IMHO, a PR would need to pull the path out of that to be accepted.

3 Likes

I'm pretty sure it won't indeed :stuck_out_tongue: That wasn't my goal fortunately :slight_smile: Just a quick 'n dirty example of something that might work in practice.

3 Likes