TL;DR wuCME is WASD's Let's Encrypt certificate management environment.
It manages server certificates for a single system or cluster,
automatically renewing and reloading certificates -- AND MORE.
The Let's Encrypt (LE) project
https://letsencrypt.org
https://en.wikipedia.org/wiki/Let%27s_Encrypt
https://wasd.vsm.com.au/wasd_root/src/wucme/readmore.html
has simplified browser-trusted server certification for the SOHO cohort.
Browsers, increasing the number of hoops to be leapt through before accepting
anything self-signed -- if at all -- were proving an increasing PITA.
Of course it's not just about Internet-facing hosts. "Internal" systems
experience the same untrusted certificate issues. It is also possible to
have an internal system generating its own LE certs -- provided the host name
is externally resolvable.
My X86 VM is for local use only. However, the host name is visible to the
wider Internet (via DynDNS, or Dyn as they prefer to be known these days) but
resolves to a single (not even fixed) address provided by my ISP. The setup
is pretty standard.
:
| +--------+
+~~~>| WASD | proxy
+--------+ +--------+ | +--------+ 192.168.1.3
| LE |<~~~WAN~~~>| router |<~~~LAN~~~+
+--------+ +--------+ | +--------+
+~~~>| WASD | wuCME
| +--------+ 192.168.1.86
:
In addition to other (more general purpose) services, the 192.168.1.3 server
has the following proxy capable service configured.
# WASD_CONFIG_SERVICE
[[https://x86vms.domain.net:80]]
[ServiceBind] 192.168.1.3
[ServiceProxy] enabled
Remember, the SOHO network has a single IP address with multiple host names.
So the wuCME app on the internal network reaches renewal time and makes a
direct request of LE for a renewed certificate. LE authenticates the request
with a challenge which connects to the certificate host name, resolving to
the ISP IP address, and ends up on port 80 of the 192.168.1.3 WASD server
(via router port forwarding rules).
|00:20:08.67 NET 2198 000001 CONNECT MULTIHOME match for 192.168.1.3,80 arrived at 192.168.1.3,80|
The LE challenge request header contains
|00:20:08.80 REQUEST 2776 000002 REQ-HEADER HEADER 199 bytes|
GET /.well-known/acme-challenge/yIIZJXXiCJ0aynim9rn7HGRE_uBY-CgE.QH3pRwq2ZSCeEYlrN-0MNKHxB8zeq-uB-mb5nTt7SLlHfoD5Ox_Qk HTTP/1.1
Host: x86vms.domain.net
8< snip 8<
and so while connected to 168.192.1.3, it processes the virtual service
|00:20:08.67 SERVICE 1749 000002 CONNECT VIRTUAL x86vms.domain.net:80|
The following mapping rules redirect the /.well-known/acme-challenge/ to the
server at 192.168.1.86, then mapping any host not on the LAN to 418 ("I'm a
teapot").
|# WASD_CONFIG_MAP
|[[x86vms.domain.net:80]]
|redirect /.well-known/acme-challenge/* \
| /http://192.168.1.86/cgi-bin/wucme/.well-known/acme-challenge/*
|pass http://192.168.1.86/cgi-bin/wucme/.well-known/acme-challenge/*
|if (remote-addr:192.168.*)
| redirect /* /http://192.168.1.86/*
| pass http://192.168.1.86/*
|endif
|[[x86vms.domain.net:*]]
|pass * "418"
Really quite simple. Here is the complete WATCH report from which the above
snippets were extracted.
|Time_______|Module__|Line|Item__|Category__|Event...|
|00:20:08.67 NET 2198 000001 CONNECT MULTIHOME match for 192.168.1.3,80 arrived at 192.168.1.3,80|
|00:20:08.67 NET 2203 000001 CONNECT ACCEPTED acme-v02.api.letsencrypt.org,47422 on http://klaatu.lan,80 (192.168.1.3) BG11250:|
|++++++++++++++++++++++++++++++++++++++++++++
|00:20:08.67 SERVICE 1749 000002 CONNECT VIRTUAL x86vms.domain.net:80|
|00:20:08.67 REQUEST 4419 000002 REQUEST GET /.well-known/acme-challenge/yiizjxxicj0aynim9rn7hgre_uby-cge.qh3prwq2zsceeylrn-0mnkhxb8zeq-ub-mb5ntt7sllhfod5ox_qk|
|00:20:08.67 REDIRECT 0184 000002 REQUEST REDIRECT /http://192.168.1.86/cgi-bin/wucme/.well-known/acme-challenge/yiizjxxicj0aynim9rn7hgre_uby-cge.qh3prwq2zsceeylrn-0mnkhxb8zeq-ub-mb5ntt7sllhfod5ox_qk|
|00:20:08.67 REQUEST 4419 000002 REQUEST GET http://192.168.1.86/cgi-bin/wucme/.well-known/acme-challenge/yiizjxxicj0aynim9rn7hgre_uby-cge.qh3prwq2zsceeylrn-0mnkhxb8zeq-ub-mb5ntt7sllhfod5ox_qk|
|00:20:08.67 PROXY 0750 000002 PROXY GET 192.168.1.86:80/cgi-bin/wucme/.well-known/acme-challenge/yiizjxxicj0aynim9rn7hgre_uby-cge.qh3prwq2zsceeylrn-0mnkhxb8zeq-ub-mb5ntt7sllhfod5ox_qk|
|00:20:08.67 PROXYNET 0432 000002 PROXY CONNECT 192.168.1.86,80 as 0|
|00:20:08.67 PROXYNET 0823 000002 PROXY CONNECTED 192.168.1.86,80 as 0 BG11259:|
|00:20:08.94 REQUEST 1436 000002 REQUEST STATUS 200 (OK) rx:195 tx:423 bytes 267ms 2,318 B/s|
|--------------------------------------------
And this is the wuCME log containing the complete transaction.
|2023-02-13 00:20:00.86: certificate management
|2023-02-13 00:20:00.93: WASD_LOCAL:WUCME_C_X86VMS_DOMAIN_NET.PEM
|2023-02-13 00:20:01.32: x86vms.domain.net !before: Dec 13 12:52:12 2022 GMT !after: Mar 13 12:52:11 2023 GMT expires: 28 renew: -1
|2023-02-13 00:20:01.37: (5) dka100:[wasd_root.axp-bin.][000000]wucme.exe --uacme --verbose issue x86vms.domain.net
|2023-02-13 00:20:01.38: version WUCME AXP-1.1.9 (1.1.2) (OpenSSL 1.1.1s 1 Nov 2022) starting
|2023-02-13 00:20:01.40: loading key from /wasd_local/wucme_k_account.pem
|2023-02-13 00:20:01.42: loading key from /wasd_local/wucme_k_X86VMS_DOMAIN_net.pem
|2023-02-13 00:20:01.45: checking existence and expiration of /wasd_local/wucme_c_X86VMS_DOMAIN_net.pem
|2023-02-13 00:20:01.47: /wasd_local/wucme_c_X86VMS_DOMAIN_net.pem expires in 28 days
|2023-02-13 00:20:01.49: /wasd_local/wucme_c_X86VMS_DOMAIN_net.pem is due for renewal
|2023-02-13 00:20:01.50: fetching directory at https://acme-v02.api.letsencrypt.org/directory
|2023-02-13 00:20:03.36: retrieving account at https://acme-v02.api.letsencrypt.org/acme/new-acct
|2023-02-13 00:20:05.11: account location: https://acme-v02.api.letsencrypt.org/acme/acct/40234161
|2023-02-13 00:20:05.13: creating new order for x86vms.domain.net at https://acme-v02.api.letsencrypt.org/acme/new-order
|2023-02-13 00:20:06.82: order URL: https://acme-v02.api.letsencrypt.org/acme/order/40234161/164373540256
|2023-02-13 00:20:06.84: retrieving authorization at https://acme-v02.api.letsencrypt.org/acme/authz-v3/202892153246
|2023-02-13 00:20:08.51: challenge=http-01 ident=x86vms.domain.net token=yIIZJXXiC7HGQH3pRwq2ZSRJ0aynim9rnE_uBY-CgE key_auth=yIIZJXXiCJ0aynim9rn7HGRE_uBY-CgE.QH3pRwq2ZSCeEYlrN-0MNKHxB8zeq-uB-mb5nTt7SLlHfoD5Ox_Qk
|2023-02-13 00:20:09.96: starting challenge at https://acme-v02.api.letsencrypt.org/acme/chall-v3/202892153246/gAI5TQ
|2023-02-13 00:20:11.62: polling challenge status at https://acme-v02.api.letsencrypt.org/acme/chall-v3/202892153246/gAI5TQ
|2023-02-13 00:20:18.32: polling challenge status at https://acme-v02.api.letsencrypt.org/acme/chall-v3/202892153246/gAI5TQ
|2023-02-13 00:20:20.01: polling order status at https://acme-v02.api.letsencrypt.org/acme/order/40234161/164373540256
|2023-02-13 00:20:21.69: generating certificate request
|2023-02-13 00:20:21.84: finalizing order at https://acme-v02.api.letsencrypt.org/acme/finalize/40234161/164373540256
|2023-02-13 00:20:23.70: polling order status at https://acme-v02.api.letsencrypt.org/acme/order/40234161/164373540256
|2023-02-13 00:20:25.32: retrieving certificate at https://acme-v02.api.letsencrypt.org/acme/cert/034aa719dc53af387fb2f535547cb20bdd31
|2023-02-13 00:20:27.00: saving certificate to /wasd_local/wucme_c_X86VMS_DOMAIN_net.pem_tmp
|2023-02-13 00:20:27.06: reading private key /wasd_local/wucme_k_X86VMS_DOMAIN_net.pem
|2023-02-13 00:20:27.15: backed up /wasd_local/wucme_c_X86VMS_DOMAIN_net.pem as /wasd_local/wucme_c_X86VMS_DOMAIN_net.pem_2023021300202700
|2023-02-13 00:20:27.16: renaming /wasd_local/wucme_c_X86VMS_DOMAIN_net.pem_tmp to /wasd_local/wucme_c_X86VMS_DOMAIN_net.pem
|2023-02-13 00:20:28.37: wuCME successful x86vms.domain.net renewal
|WASD_LOCAL:WUCME_C_X86VMS_DOMAIN_NET.PEM
|x86vms.domain.net
|2023-02-13 00:20:28.60: load succeeded using internal commands
|2023-02-13 00:20:28.62: 193 files, 1 certs, 1 renewed, 0 failed
VIA PROXY
~~~~~~~~~
And of course it would still be possible for a system blocked from making any
direct connection to the Internet and LE, to proxy out via WASD (or other)
and use WASD (or other) to redirect the challenge to the wuCME system.
+--------+ +--------+ +--------+ +--------+
| LE |<~~~WAN~~~>| router |<~~~LANx~~~>| WASD |<~~~LANy~~~>| WASD |
+--------+ +--------+ +--------+ +--------|
proxy wuCME
This configuration is left as an exercise for the reader :-)
AND MORE
~~~~~~~~
While wuCME is dexterous enough for single systems and clusters of systems,
it can actually perform more complex feats with some simple sleight-of-hand.
When a certificate is successfully updated it is automatically reloaded into
the running server(s). The server process log shows this happening
|%HTTPD-I-CONTROL, 13-FEB-2023 00:20:28, 00000338 X86VMS HTTP$NOBODY "wuCME-load", 'ssl=cert=load'
|%HTTPD-I-SSL, klaatu.lan:443
|-SSL-I-CERT, wasd_root:[local]wucme_c_x86vms_domain_net.pem
|-SSL-W-STRICT, HSTS transport security disabled
8< snip 8<
A poorly documented (but working) capability extends this reload to other
activities, even privileged ones. A multi-valued logical name provides for a
first LOAD activity, followed by another, and potentially another... In this
example two.
|$ show log wucme_load /table=lnm$system
|
|(LNM$SYSTEM_TABLE)
|
| "WUCME_LOAD" = "+"
| = "@pmdf_root:[table]wasd_cert_to_pmdf.com"
|
|(LNM$SYSCLUSTER_TABLE)
The first value indicates to perform a standard cert load.
The second value, to execute the specified DCL procedure, which can be
reasonably capable.
|$! WASD_CERT_TO_PMDF.COM
|$! see logical name WUCME_LOAD
|$ SET PROCESS /PRIV=(SYSPRV,CMKRNL)
|$ IF F$MODE() .EQS. "BATCH"
|$ THEN
|$ COPY WASD_ROOT:[LOCAL]WUCME_C_THE_HOST_NAME.PEM -
| PMDF_ROOT:[TABLE]MAIL-PUB.PEM;0 /PROTECTION=W:R
|$ COPY WASD_ROOT:[LOCAL]WUCME_K_THE_HOST_NAME.PEM -
| PMDF_ROOT:[TABLE]MAIL-PRIV.PEM;0 /PROTECTION=W
|$ PMDF RESTART DISPATCH
|$ SUBMIT/USER=SYSTEM SYS$STARTUP:PMAS_STARTUP.COM
|$ ELSE
|$ PROCEDURE = F$ENVIRONMENT("PROCEDURE")
|$ SUBMIT/USER=SYSTEM 'PROCEDURE'
|$ ENDIF
This paradigm could be extended to COPY (whatever that entails) the license
negotiated and processed by the (intermediate) system to the end-user system.
The applications are manifold.
* Further detail in [SRC.WUCME]WUCERTMAN.C CertManLoad().
Can require INSTALL with extended privileges. YMMV!
** NOTE: some of the actual data in these examples are redacted, some fudged,
and some completely made up (for illustrative purposes).
This item is one of a collection at
https://wasd.vsm.com.au/other/#occasional
|