Skip to content

refactor: acme.json extraction#2274

Merged
polarathene merged 17 commits intodocker-mailserver:masterfrom
polarathene:refactor/acme-extraction
Nov 3, 2021
Merged

refactor: acme.json extraction#2274
polarathene merged 17 commits intodocker-mailserver:masterfrom
polarathene:refactor/acme-extraction

Conversation

@polarathene
Copy link
Copy Markdown
Member

@polarathene polarathene commented Oct 31, 2021

Description

This has been extracted from #2245 as it does not need to be part of that PR.

It has additionally extracted the python logic to an actual python file that I've refactored and made compatible with Python 3, as Python 2 is EOL and no longer in newer versions of Debian. Required for #2116 to pass tests.

Split into scoped commits with messages if it helps with review :)

Commit Summary:

check-for-changes.sh

  • Prevent SSL_DOMAIN silently skipping when value has wildcard prefix *. (at least this was known as a bugfix when originally committed in linked PR).
  • Improved inlined docs for maintainers.
  • Additional logging for debugging.

helper-functions.sh:_extract_certs_from_acme:

  • Fail if the input arg ($CERT_DOMAIN, aka the FQDN) provided for extraction is empty.
  • Use $CERT_DOMAIN in place of $HOSTNAME and $1 for a consistent value (previously could mismatch, eg with SSL_DOMAIN defined).
  • The conditional is now only for handling extraction failure (key or cert value is missing from extraction).
  • Log an actual warning or success (debug) based on outcome.
  • Don't use SSL_DOMAIN with wildcard value for the mkdir letsencrypt directory name (wildcard prefix *. is first stripped instead).

acme_extract (new python utility for acme.json handling):

  • Extracted out into a python script that can be treated as a utility in the $PATH like other helper scripts. It can now be used and optionally tested directly instead of via helper-functions.sh.
Interacting with the python script has following output:
$ acme_extract --help
usage: acme_extract [-h] (--key | --cert) <filepath> <FQDN>

Traefik acme.json key and cert extractor utility.

positional arguments:
  <filepath>  Path to acme.json
  <FQDN>      FQDN to match in a certificate 'main' or 'sans' field

optional arguments:
  -h, --help  show this help message and exit
  --key       Output the key data to stdout
  --cert      Output the cert data to stdout

--key or --cert is actually required, so minor mistake by the argparser Python module with it's generated help text above:

$ acme_extract ./acme.json mail.example.com
usage: testy.py (--key | --cert) <filepath> <FQDN>
acme_extract: error: one of the arguments --key --cert is required

$ acme_extract
usage: testy.py (--key | --cert) <filepath> <FQDN>
acme_extract: error: the following arguments are required: <filepath>, <FQDN>

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change that does improve existing functionality)

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • If necessary I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

The extraction python code is effectively the same, except for what is now `args.requested` var.

Extracted out into a python script that can be treated as a utility in the `$PATH` like other helper scripts. It can now be used and optionally tested directly instead of via `helper-functions.sh`.

Added an arg parser that provides better experience with help output and error/parsing checks out of the box. Should be easier to maintain now.

Updated to be compatible with Python 3, required for upgrade to Debian 11 Bullseye.
Refactored to be less heavy in width, especially with the final nested conditional check, which was extracted out to a separate function call.

Added basic error handling when a key lookup fails, which was more useful when removing the nested if statements.
- Only use the conditional for handling the failure if key or cert value is missing from extraction.
- Emit an actual warning or success debug log.
- Use the FQDN arg `$CERT_DOMAIN` here in place of `$HOSTNAME` and `$1`, since that's what we matched via the python script.
`-h` / `--help` output was accidentally disabled, I forgot to remove this option.
Comment thread target/bin/acme_extract Outdated
Co-authored-by: Frederic Werner <[email protected]>
@polarathene

This comment has been minimized.

The added logs and timing helps debug changedetector service behaviour, especially after multiple changes.

The timing from the start and end of a detected change event helps establish how long it took to process which is quite helpful in tests that need to wait for changes.
While fairly verbose, the added comments should assist maintainers to more quickly be informed on the functionality.

Added more logs help with debugging.
Without quote wrapping the variables in the for declaration, `*.example.test` did not seem to be attempted. `mail.example.test` would be tried followed by `example.test` if no match was found.

I'm a little surprised that shellcheck lint did not pick up on that. I have opted to use an array instead where the lint will actually advise to quote wrap vars.
@polarathene

This comment has been minimized.

@polarathene
Copy link
Copy Markdown
Member Author

polarathene commented Nov 1, 2021

If there's anything else I can do to make this PR easier to review, please let me know.

check-for-changes.sh is mostly the same apart from the for loop input fix with quoted vars, it just adds better debugs and inline docs. Same with helper-functions.sh (extracted python script + fixes).


I know the python might be uncomfortable to maintainers here (I'm not a fan of Python myself, but that argparser is much nicer than the shell equivalent I think 😅 ), If you check the commit history it originally is similar to the python embedded in the shell scripts currently (which is the same code repeated twice with a minor tweak for which JSON field to return), I just reworked it to be DRY.

  • Python official argparser docs were quite helpful building that part out.
  • The addition of try/except error handling for KeyError is in the unexpected event of bad acme.json data, I referenced this SO answer for that, the error is ignored (doesn't output traceback and failure to stdout/stderr) and we return an empty string instead which is the current fail handling check done in helper-functions.sh.
  • If you haven't noticed it, there is a details section in the PR description that can be expanded to show examples of the Python CLI features now available (not that anyone is really going to use that unless debugging). It does help ensure the expected args are received and in the correct format, so if any refactor in helper-functions.sh makes a mistake it should be fairly visible and easier for maintainer to resolve.

Comment thread target/scripts/check-for-changes.sh
Comment thread target/scripts/helper-functions.sh Outdated
fi

mkdir -p "/etc/letsencrypt/live/${CERT_DOMAIN}/"
echo "${KEY}" | base64 -d > "/etc/letsencrypt/live/${CERT_DOMAIN}/key.pem" || exit 1
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a question, if that is the intended behaviour: When this command fails, then check-for-changes should exit with errorcode 1. Is that correct?
Supervisor will then start check-for-changes again.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't author this line originally, but I guess that's what happens? 😅

I'm not quite sure how it'd fail if the directory was created and the value is not empty, other than maybe not being valid base64 encoded data for some reason?

It would end up in a bit of a loop then (startup script also calls this method in setup-stack.sh), without an error logged? (or that comes from the base64 decode error prior to exit?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quite sure how it'd fail

Thought the same. But was wondering why there is this explicit || exit 1

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without an error logged?

When supervisor restarts a service, you'll see something similar to this at STDOUT:

2021-11-01 23:13:08,348 INFO exited: update-check (terminated by SIGTERM; not expected)
2021-11-01 23:13:08,350 INFO spawned: 'update-check' with pid 3534
2021-11-01 23:13:08,436 INFO success: update-check entered RUNNING state, process has stayed up for > than 0 seconds (startsecs)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But was wondering why there is this explicit || exit 1

It was introduced in July 2020 via this PR, there is no discussion about it or mention in the relevant commit message.

Comment thread target/scripts/helper-functions.sh
@polarathene

This comment has been minimized.

@polarathene polarathene self-assigned this Nov 2, 2021
@polarathene polarathene added area/scripts kind/bug kind/improvement Improve an existing feature, configuration file or the documentation labels Nov 2, 2021
@polarathene polarathene added this to the v10.3.0 milestone Nov 2, 2021
Copy link
Copy Markdown
Member

@wernerfred wernerfred left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@polarathene polarathene merged commit e807631 into docker-mailserver:master Nov 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/scripts kind/improvement Improve an existing feature, configuration file or the documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants