wµCME

Version 1.1.9, 17th September 2024

Copyright © 2020-2024 Mark G. Daniel
This program, comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it under the
conditions of the GNU GENERAL PUBLIC LICENSE, version 3, or any later version.
http://www.gnu.org/licenses/gpl.txt

Contents




wµCME  (pronounced "wack-mee") is a Certificate Management Environment for the Let's Encrypt ™ service.

"The objective of Let's Encrypt and the ACME protocol is to make it possible to set up an HTTPS server and have it automatically obtain a browser-trusted certificate, without any human intervention [and at no cost]. This is accomplished by running a certificate management agent on the web server."

https://www.letsencrypt.org
https://en.wikipedia.org/wiki/Let's_Encrypt

Let's Encrypt is a trademark of the Internet Security Research Group. All rights reserved.

Use of Let's Encrypt requires compliance with the terms and conditions described in the Subscriber Agreement document which can be found at the top the following URL.

https://letsencrypt.org/repository/

wµCME is based on a port and adaptation of uacme ACME v2 implementation by Nicola Di Lieto, licensed and distributed under GPLv3.

https://github.com/ndilieto/uacme
https://github.com/ndilieto/uacme/blob/master/COPYING

Automatic Certificate Management Environment (ACME) describes a protocol that a certification authority (CA) and an applicant can use to automate the process of verification and certificate issuance. The protocol also provides facilities for other certificate management functions, such as revocation. At the time of writing it is a Proposed Standard and working document of the Internet Engineering Task Force (IETF).

https://ietf-wg-acme.github.io/acme/

The uacme application is a lightweight implementation of the ACME v2 protocol, designed to interact with the Let's Encrypt service for the automated issuance, renewal and revocation of server certificates.

wµCME uses uacme to provide the core of the required functionality, with code modifications for use on [Open]VMS, as well as additional code specifically for WASD web server TLS service certificate management. See Implementation.

wµCME is a contraction of WASD µ Certificate Management Environment. The "u" in uacme is not explained but is assumed to be a mu as used for micro, meaning small. Everywhere other than documentation the name reads "wucme".

wµCME is tailored for WASD. Apache has mod_md for ACME.

How it Operates

wµCME has three roles

When acting as certificate management agent wµCME executes within the process named "wuCME-active". This process is created and managed by the server. wµCME uses the DLM to ensure only a single process manages certificates with multi-instance nodes and clusters (except if the logical name WUCME_ACTIVE is defined – see below). In such environments there may be "wuCME-standby" processes waiting for circumstances requiring another to become active. Largely quiescent, once a day (at twenty minutes past midnight) wµCME checks certificate expiry and attempts to renew if required. Let's Encrypt issues certificates with a ninety day lifetime and wµCME begins attempting renewal the recommended thirty days advance of expiry.

Authority to request a certificate is established by the requesting agent demonstrating control over the corresponding domain name (service). wµCME employs the ACME http-01 challenge, where a unique and cryptographically exchanged resource is accesssed by Let's Encrypt using a well-known URI on the standard cleartext service (port 80). wµCME will deploy an ad hoc port 80 service if one is not available. Successful access (to each of any multiple domain names) satisfies Let's Encrypt and certificate issue proceeds. To perform this function wµCME is activated as a script.

wµCME generates and maintains two private cryptographic keys. One to identify a unique account with Let's Encrypt and sign HTTP transactions. Another to sign the Certificate Signing Request (CSR) when requesting a certificate, and subsequently to authorise use of that generated certificate by the server (i.e. the traditional private key). wµCME supports one account, potentially managing multiple certificates, per system.

Both successful and failed renewal attempts optionally produce an OPCOM message and/or email to configurable targets. Successful renewal executes an action to (re)load the renewed certificate(s) into the server.

Usage

Certificates remain in the usual WASD_ROOT:[LOCAL] location and wµCME-generated certificates are placed directly into this directory. To locate the certificates elsewhere define the WUCME_HERE logical name. For example

$ DEFINE /SYSTEM /EXECUTIVE WUCME_HERE ELSE:[WHERE]

If required the definition should be added to the startup procedure.

All wµCME certificate files are prefixed with with WUCME_C_ and key files with WUCME_K_. Domain-specific files are followed by the host name, or first of multiple host names (see below), of the certificate request, made ODS-2 compliant. Using www.example.org for an example:

# WASD_CONFIG_GLOBAL
[SSLcert] WASD_ROOT:[LOCAL]WUCME_C_WWW_EXAMPLE_ORG.PEM
or
# WASD_CONFIG_SERVICE
[[https://www.example.org]]
[ServiceSSLcert] WASD_ROOT:[LOCAL]WUCME_C_WWW_EXAMPLE_ORG.PEM

wµCME always appends the private key to the certificate.

Certificate Load

After wµCME renews a certificate it is automatically loaded into the server(s).

Multiple Hosts

wµCME does not support wildcard certificates but will issue a certificate for multiple hosts (at time of writing, up to one hundred) by using altnames. This means that a single certificate may be used and managed for multiple, or even all, services of a site. However, each one will be individually challenged (see 2. The ACME http-01 challenge...) and must be accessible from the Internet. And of course wµCME also supports multiple certificates allowing for a very large pool of Let's Encrypt -secured sites per server.

Command-Line

Command-line usage is via a foreign verb or MCR.

$ wucme == "$cgi_exe:wucme"
$ mcr cgi_exe:wucme

wµCME requires account SYSPRV for command-line use.

Account Registration

An account must be registered with Let's Encrypt before wµCME can be used for certification. A private cryptographic key is created to identify the end-user to the CA. This is performed manually at the command-line. Optionally, an email address may be supplied.

$ wucme register webmaster@example.com

Generally only required the once, it can be done again at any time to (re)create an(other unique) account. If this happens an initial certification must also be redone for all managed services.

Initial Certification

Before wµCME can be used for maintenance an initial certification must be made. This is performed manually at the command-line. It is also necessary when the number or composition of services changes. Failure to maintain the currency of a certificate will likely result in failure at next automated renewal.

First, be sure to test challenge processing for all services to be certified, as described in 2. The ACME http-01 challenge....

Multiple host names can be space or comma-separated.

$ wucme issue www.example.org

Once a certificate or certificates are successfully generated they need to be loaded into the running server. For WASD v11.1.0 or later this may be performed using

$ httpd /do=ssl=cert=load
or for earlier releases
$ httpd /do=restart[=now]

When in detached operation wµCME automatically will perform the certificate load function.

The initial certification step is used for each certificate to be requested.
This can be done at time of initial installation and any time following that to provision additional services.
Remember that each certificate may support up to one hundred sites.

With a non-trivial number of sites to be certified it is suggested that a command procedure be created in WASD_ROOT:[LOCAL] that contains the necessary command. The can then be edited and executed as required. For example:

$! CERTIFY_EXAMPLE_SERVICES.COM
$ wucme = "$cgi_exe:wucme"
$ wucme issue www.example.org,two.example.org,three.example.org,-
four.example.org,five.example.org,six.example.org,seven.example.org,-
eight.example.org,nine.example.org,ten.example.org

Note that Let's Encrypt has limits on the number of certifications in a given period.

https://letsencrypt.org/docs/rate-limits/

Other Functions

Functions available from the VMS command-line.

Command-Line Description
certificates List the certificates currently being managed.
check /ca Test that the CA is accessible by performing a directory query with it.
check /http01 Activate the internal challenge responder binding to port 80.
check /log Display the content of the most recent "wuCME-active" process log.
check /mail Test the WUCME_MAIL logical name reporting target.
check /opcom Test the WUCME_OPCOM logical name reporting target.
issue host(s) Request Let's Encrypt to issue a certificate that includes the specified host or comma-separated list of hosts.
manage Manually run the certificate management function normally executed once a day by the "wuCME-active" process.
revoke file Request Let's Encrypt to revoke the certificate contained in the specified file.
version Display the executable version.

Raw uacme

The underlying uacme CLI and functionality is available by prefixing the command-line parameters with a --uacme option.

The UACME(1) Manual Page is available at https://ndilieto.github.io/uacme/ and locally as a PDF.

Installation

Basic steps:

  1. obtain kit(s)
  2. unzip kit(s)
  3. build application
  4. copy executable
  5. configure
  6. application startup
  7. certification

Expanded descriptions:

  1. Obtain the kit(s) from
    https://wasd.vsm.com.au/wasd/
  2. The source, and optionally object modules, should be UNZIPed.
    $ SET DEFAULT WASD_ROOT:[SRC]
    $ UNZIP <location>:WUCMEnnn.ZIP
    $ UNZIP <location>:WUCMEnnn-<platform>.ZIP
    $ SET DEFAULT [SRC.WUCME]
    
  3. A build requires two parameters.
    $ @BUILD_WUCME <p1> <p2>
    
    Parameter Purpose Description
    P1 build mode BUILD (compile then link), COMPILE or LINK
    P2 SSL package root Directory specification of SSL package. Examples:
    SSL111$ROOT:[000000]
    SSL3$ROOT:[000000]
    WASD_ROOT:[SRC.OPENSSL-1_1_1E]
    SYS$COMMON:[OPENSSL]
    Requires OpenSSL 1.1.1 or later
    For example:
    $ @BUILD_WUCME BUILD SSL111$ROOT:[000000]
    

  4. Copy the application executable to the script location.
    $ COPY [.OBJ_<platform>]WUCME.EXE CGI_EXE:
    

  5. Configure the application.

  6. The CGI-BIN:[000000]WUCME.EXE image must be INSTALLed with SYSPRV and SYSLCK privileges.
    The WUCME_STARTUP.COM procedure will perform this.

    This procedure also will accept DCL symbols representing the runtime logical names and these may be used to customise the site startup. For example:

    $ WUCME_MAIL = "SYSTEM,mark.daniel@wasd.vsm.com.au"
    $ WUCME_OPCOM = "CENTRAL,NETWORK"
    $ @WASD_ROOT:[SRC.WUCME]WUCME_STARTUP.COM
    

    Execute the wµCME startup procedure. For example:

    $ @WASD_ROOT:[SRC.WUCME]WUCME_STARTUP.COM
    

    Ensure the startup procedure and parameters are edited into the system startup.

  7. Request the initial certificate as described in Account Registration and Initial Certification above.

    When successfully certified restart the server to use the fresh Let's Encrypt certificate(s).

Update

Configuration

Sites have all manner of local requirements and mapping rules, often associated with multiple independent hosting services. With non-trivial sites there is no single recipe to support all aspects of wµCME functionality. Essential is the Let's Encrypt challenge mapping and the proctoring of the CGI-BIN:[000000]WUCME.EXE application. Optional—but most useful—is providing browser access to the administration functions.

All logical names must be defined in the SYSTEM table.**

Basic steps:

  1. proctored application
  2. responding to the challenge
  3. service certificate specification
  4. reporting logical names
  5. authorisation for administration URIs

**WUCME_STARTUP.COM can assist with this.

Expanded descriptions:

  1. The persistent wµCME process managing certificate renewal is a proctored script. The WASD server manages and maintains this process using the configuration
    # WASD_CONFIG_GLOBAL
    [DclScriptProctor]
    1 /cgi-bin/wucme /cgi-bin/wucme
    

    A mapping rule is required to activate this as a script. This can be a simple, global rule allowing the activation of scripts located in CGI-BIN:[000000].

    # WASD_CONFIG_MAP
    exec /cgi-bin/* /cgi-bin/*
    

    If this is not implemented on a site, a constrained rule can be provided allowing activation of only proctored scripts and the wµCME executable in particular.

    # WASD_CONFIG_MAP
    if (request-method:)
       exec /cgi-bin/* /cgi-bin/*
    endif
    

    This process appears in the system as "wuCME-active". The server needs to be restarted at initial configuration and thereafter automatically maintains an instance of this process. Also see Catch-all Config for a combined challenge and script mapping for all services.

  2. The ACME http-01 challenge used by wµCME must be provided by port 80 service(s) on the server system.

    For either approach, be sure to test challenge processing for all services to be certified.

  3. Services must use the wµCME certificate naming convention. For example:
    # WASD_CONFIG_SERVICE
    [[https://www.example.org]]
    [ServiceSSLcert] WASD_ROOT:[LOCAL]WUCME_C_WWW_EXAMPLE_ORG.PEM
    
    or
    # WASD_CONFIG_GLOBAL
    [SSLcert] WASD_ROOT:[LOCAL]WUCME_C_WWW_EXAMPLE_ORG.PEM
    
  4. Successful and unsuccessful attempts to renew certificates are logged, and optionally also reported via MAIL and OPCOM. The MAIL and OPCOM targets are both configured using logical names.** Each can contain multiple, comma-separated targets, with a MAIL target anything that can be addressed at the command-line. By default OPCOM reports to CENTRAL and may be disabled completely by defining the logical name as NONE. These should be added to the startup procedure. For example.
    $ DEFINE /SYSTEM /EXECUTIVE WUCME_MAIL "system,smtp%""whomever@wherever"""
    $ DEFINE /SYSTEM /EXECUTIVE WUCME_OPCOM "central,software,oper2"
    
  5. Clustered systems running wµCME have either a single WASD installation on a shared, cluster-mounted disk, or multiple independent WASD installation directories. wµCME assumes a single installation shared among the possibly multiple architecture systems, and that a single, active wµCME process can manage all the certificates in use.

    Where multiple installations exist, each with their own WASD_ROOT:[LOCAL] certificate repository, then one system per repository must have the logical name WUCME_ACTIVE defined.

    $ DEFINE /SYSTEM /EXECUTIVE WUCME_ACTIVE *
    

    Where all clustered systems are independent a cluster-wide logical could be used.

    $ DEFINE /CLUSTER /EXECUTIVE WUCME_ACTIVE *
    
  6. The application (script) can be accessed using a browser for administrative purposes.
    URI Description
    /cgi-bin/wucme/admin/cert Reports on the current certificate(s) being managed.
    /cgi-bin/wucme/admin/log Reports the current "wuCME-active" log.
    /cgi-bin/wucme/admin/check/ca Test that the CA is accessible by performing a directory.
    /cgi-bin/wucme/admin/check/challenge Test the challenge responder, either local mapped port 80 or internal ad hoc responder.
    /cgi-bin/wucme/admin/check/load Test certificate (re)load.
    /cgi-bin/wucme/admin/check/mail Test WUCME_MAIL logical name by sending a message.
    /cgi-bin/wucme/admin/check/opcom Test WUCME_OPCOM logical name by sending a message.

    The admin path (only) must be subject to authorisation. For example:

    # WASD_CONFIG_AUTH
    ["VMS credentials"=WASD_VMS_RW=id]
    /httpd/-/admin/* r+w,https:
    /cgi-bin/wucme/admin/* read,https:
    

Catch-all Config

From the wµCME beachhead…Triage at the bleeding edge. Individual sites can be non-trivial. Not all resources, including general scripting access, may be present on all services. The following rules, placed at the very beginning of the configuration file, will provide all wµCME-required resources to all services of an installation, in a manner constrained to wµCME usage.

# WASD_CONFIG_MAP
[[*]]
# ACME challenge processing
redirect /.well-known/acme-challenge/* \
         /cgi-bin/wucme/.well-known/acme-challenge/*
# wuCME proctor and script activation
if (request-method: || request-uri:/cgi-bin/wucme/*)
   exec /cgi-bin/* /cgi-bin/* map=once
endif

If blanket authorisation is applied to a service requiring challenge processing, the following configuration may be used to exempt the challenge.

# WASD_CONFIG_AUTH
[[*]]
# ACME challenge processing
[NONE]
**/.well-known/acme-challenge/* read

Implementation

The port required an adaptation of C-language code to operate with VMS, DECC compiler and the post-VMS-v7.0 C-RTL, as well as some Unix assumptions regarding the file-system and like. The wµCME executable provides file-system access to otherwise protected resources using INSTALLed SYSPRV. Conditional compilation has general VMS adaptations using #if[n]def __VMS while wµCME-specific code uses #if[n]def IS_WUCME.

As WASD is designed to allow and known to be installed on ODS-2 volumes, wµCME file-system elements are explicitly constrained to ODS-2 syntax, in particular, non- file type periods are substituted with underscores, making the host name an.example.net into an_example_net in the file-system.

While uacme uses the cURL library to perform its HTTP transactions, the issues with having cURL as a dependency on VMS systems has necessitated wµCME implementing its own HTTP client.

As of version 1.1 uacme includes a standalone tls-alpn-01 challenge (RFC8737) utility which is not ported or used by wµCME.

Problems?

Releases

v1.1.9  14-SEP-2023
•  improve LE server polling by backoff
•  bugfix; discard unsupported challenges
•  pre-compiled object module kits must be linked against SSL3
•  move /wasd_root/local specification to use logical name /wasd_local
v1.1.8  15-JAN-2023
•  make crypto.c is_ip() more "reliable"
•  can be compile+linked using SSL3 …
   however when using pre-compiled object module kits must be linked against SSL111
•  remove documentation on migrating from wCME
•  if installed wµCME v1.1.7 is working fine there is no need to update to v1.1.8!
v1.1.7  24-OCT-2021
•  where a clustered system has an independent WASD installation
•  supports x86-64 VMS — if building Using IA64-hosted X86 Cross-Complier
•  unlike the rest of WASD, wµCME remains under the GPL due to fundamental incompatibilities
   https://www.apache.org/licenses/GPL-compatibility.html
v1.1.6  01-MAR-2021
•  store challenge using LNM$SYSCLUSTER instead of LNM$SYSTEM
    (allowing a load-distributed cluster transparent response)
v1.1.5  06-JAN-2021
•  LE connection verification less specific, more adaptable
v1.1.4  03-AUG-2020
•  bugfix; CLI cannot use CGI variables
•  further refinement of configuration documentation
v1.1.3  14-JUL-2020
•  probing port 80 use SERVER_NAME not localhost
v1.1.2  04-JUL-2020
•  refine missing INSTALLed privilege reporting
•  further refinement of configuration documentation
v1.1.1  18-JUN-2020
•  refine script activation
•  refine configuration and migration documentation
v1.1.0  03-JUN-2020
•  barely made it into the wild and WUCME_LOAD needed enhancement
   (see WUCERTMAN.C CertManLoad())
v1.0.0  01-JUN-2020
•  initial release

References

A few URLs of interest and example.

Acknowlegements

Many thanks to Nicola Di Lieto for making uacme freely available.