Copyright © 2017-2019 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
wCME (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_EncryptLet'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.
wCME is based on a port and adaptation of the BSD, C-language coded, acme-client ACME implementation, and is licensed and distributed with OpenBSD.
https://github.com/openbsd/src/tree/master/usr.sbin/acme-clientCopyright (c) 2016, Kristaps Dzonsons kristaps@bsd.lv Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.https://www.openbsd.org/policy.html
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 an Internet-Draft and working document of the Internet Engineering Task Force (IETF).
https://ietf-wg-acme.github.io/acme/
The acme-client application is a stable implementation of the ACME protocol, designed to interact with the Let's Encrypt service for the automated issuance, renewal and revocation of server certificates. ACME is a sufficiently complex and demanding protocol that the adoption of an existing application environment was the only effort- and reliability-effective approach to supporting Let's Encrypt with WASD.
wCME uses acme-client to provide the core of the required functionality, with code modifications for use on [Open]VMS, as well as additional code specifically for VMS web server TLS service certificate management. See Implementation.
While wCME is a contraction of WASD Certificate Management Environment, and the application and documentation unreservedly has a WASD emphasis, it is suitable for use with both WASD and VMS Apache. Apache instruction should be carefully evaluated before use and may require some tweaking depending on local requirements.
Apache-specific variation is indicated by a dashed left margin.
wCME v2.0, even with extensive code changes and minor enhancements, is intended to be completely backward compatible with v1.n (with the exception of the original acme-client-portable CLI).
This application is suitable for building with OpenSSL 1.1.n as well as OpenSSL 1.0.2.
This application is not suitable for VAX due to OpenSSL limitation.
VAX servers may still be provided with
Let's Encrypt certificates using the
https://sslforfree.com
service.
When acting as certificate maintenance agent wCME executes within the detached process, "WCME-overseer". Largely quiescent, once a day (at twenty minutes past midnight) it checks certificate expiry and attempts to renew if required. At the time of writing, Let's Encrypt issues certificates with a ninety day lifetime and wCME begins attempting renewal the recommended thirty days in advance of expiry.
Both successful and failed renewal attempts optionally produce an OPCOM message and/or email to configurable targets. Successful renewal optionally executes an action to (re)load the renewed certificate(s) into the server.
wCME 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). wCME supports one account, potentially managing multiple certificates, per system.
Authority to request a certificate is established by the requesting agent demonstrating control over the corresponding service(s). wCME 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). Successful access (to each of any multiple resources) satisfies Let's Encrypt and certificate issue proceeds.
The majority of the time the system will show only "WCME-overseer" but the multi-process architecture of acme-client means that when processing a certificate request with Let's Encrypt the system will briefly contain the following processes.
00000F13 WCME-overseer HIB 5 600 0 00:00:00.27 729 402 00000F14 WCME-main HIB 5 1445 0 00:00:00.12 337 294 S 00000F15 WCME-netproc LEF 4 1740 0 00:00:00.64 338 463 S 00000E17 WCME-acctproc LEF 4 1710 0 00:00:00.60 270 394 S 00000F18 WCME-chngproc LEF 4 173 0 00:00:00.04 214 290 S 00000F19 WCME-certproc LEF 4 1704 0 00:00:00.63 230 321 S 00000E1A WCME-fileproc LEF 4 173 0 00:00:00.05 222 282 S 00000E1B WCME-dnsproc LEF 5 228 0 00:00:00.08 366 377 S
wCME supports both WASD and VMS Apache, either as the only Web service on a system, or with both in use on a single system. Where a system supports both server environments wCME can maintain certificates for either or both. Where a cluster supports one or both with cluster-common configurations only a single system should execute wCME to manage the certificates across the cluster. Where a cluster supports one or both without cluster-common configuration each system should execute wCME. The logical names WCME_CERT and WCME_LOAD support multiple values allowing certificate location and action for each to be managed somewhat independently. Note that any generated certificate will be installed in each specified location whether it is in use from that location or not.
Certificates remain in the usual location expected by the server and wCME-generated certificates managed independently and then installed in (i.e. copied to) the directory specified by the WCME_CERT logical name. For example:
$ DEFINE /SYSTEM /EXECUTIVE WCME_CERT WASD_ROOT:[LOCAL]
$ DEFINE /SYSTEM /EXECUTIVE WCME_CERT APACHE$ROOT:[CONF.SSL_CRT]
This definition should be added to the startup procedure. In the absence of this logical name generated certificates remain in WCME_ROOT:[SSL].
Certificate files are named using the host name, or first of multiple host names (see below), of the certificate request, made ODS-2 compliant. Let's Encrypt supplies three certificates (the host, the intermediate chain, and the two combined) with the fullchain combined version generally recommended and required to be used by wCME. Using www.example.org for an example:
# WASD_CONFIG_GLOBAL [SSLcert] WASD_ROOT:[LOCAL]FULLCHAIN_WWW_EXAMPLE_ORG.PEM
# WASD_CONFIG_SERVICE [[https://www.example.org]] [ServiceSSLcert] WASD_ROOT:[LOCAL]FULLCHAIN_WWW_EXAMPLE_ORG.PEM
# APACHE$COMMON:[CONF]SSL.CONF SSLCertificateFile /apache$root/conf/ssl_crt/fullchain_www_example_org.pem
wCME automatically appends the private key to the fullchain certificate.
After wCME renews a certificate it can automatically be loaded into the server(s). The logical name WCME_LOAD defines an action performed to accomplish this. In the absence of this logical name the certificate load must be performed manually.
Let's Encrypt ACME version 1 server (the wCME implementation) 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 and so must have an associated cleartext service and be accessible from the Internet (see configuration item 4).
* ACME version 2 has been implemented:https://letsencrypt.org/docs/acme-protocol-updates/wCME (via acme-client) does not (currently) support version 2
Command-line usage is via a foreign verb or MCR.
$ wcme == "$cgi_exe:wcme" $ mcr cgi_exe:wcme
$ wcme == "$apache$common:[cgi-bin]wcme" $ mcr apache$common:[cgi-bin]wcme
wCME requires possssion of SYSPRV for
command-line use.
Account Registration
An account must be registered with Let's Encrypt before wCME can be used for certification. Two private cryptographic keys are created to identify the end-user to the CA and sign data exchanged with the service. This is performed manually at the command-line.
$ wcme /register
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 wCME 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. Multiple host names must be comma-separated.
$ wcme /certify=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=loador for earlier releases
$ httpd /do=restart[=now]
When in detached operation wCME automatically can perform this function. See point 6 in Configuration.
If a non-trivial number of services are to be certified it is suggested that a command procedure be created in WCME_ROOT:[ETC] that contains the necessary command. The can then be edited and executed as required. For example:
$! CERTIFY_EXAMPLE_SERVICES.COM $ wcme = "$cgi_exe:wcme" $ wcme /certify=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/
Functions available from the VMS command-line.
Command-Line | Description | ||
---|---|---|---|
/certify=host(s) | Request Let's Encrypt to issue a certificate that includes the specified host or comma-separated list of hosts. | ||
/check=ca | Test that the CA is accessible by performing a directory query with it. | ||
/check=mail | Test the WCME_MAIL logical name reporting target. | ||
/check=opcom | Test the WCME_OPCOM logical name reporting target. | ||
/check=load | Test the WCME_LOAD logical name certificate (re)load. | ||
/list | List the certificates currently being managed. | ||
/manage | Manually run the certificate management function normally executed once a day by the overseer process. | ||
/version | Display the executable version. |
Requires a minimum VMS V7.1 and if compiling, OpenSSL 1.1.n or 1.0.2.
Basic steps:
Expanded descriptions:
https://wasd.vsm.com.au/wasd/
$ SET DEFAULT WASD_ROOT:[000000] $ UNZIP <location>:WCMEnnn.ZIP $ UNZIP <location>:WCMEnnn-<platform>.ZIP $ SET DEFAULT [SRC.WCME]
$ SET DEFAULT APACHE$COMMON:[000000] $ UNZIP <location>:WCMEnnn.ZIP $ UNZIP <location>:WCMEnnn-<platform>.ZIP $ SET DEFAULT [SRC.WCME]
$ @BUILD_WCME <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:
WASD_ROOT:[SRC.OPENSSL-1_1_1A] WASD_ROOT:[SRC.OPENSSL-1_0_2Q] SYS$COMMON:[OPENSSL] SYS$COMMON:[SSL1] |
For example:$ @BUILD_WCME BUILD WASD_ROOT:[SRC.OPENSSL-1_1_1A]
$ COPY [.OBJ_<platform>]WCME.EXE CGI_EXE:
$ COPY [.OBJ_<platform>]WCME.EXE APACHE$COMMON:[CGI-BIN]
$ WCME_CERT = "WASD_ROOT:[LOCAL]" $ WCME_LOAD = "@WASD_ROOT:[SRC.WCME]WCME_LOAD" $ WCME_MAIL = "SYSTEM,mark.daniel@wasd.vsm.com.au" $ WCME_OPCOM = "CENTRAL,NETWORK" $ @WASD_ROOT:[SRC.WCME]WCME_STARTUP.COM SYSTEM WASD
Execute the wCME startup procedure. For example:
$ @WASD_ROOT:[SRC.WCME]WCME_STARTUP.COM SYSTEM WASD
Ensure the startup procedure and parameters are edited into the system startup.
$ PIPE SHOW SYSTEM | SEARCH SYS$INPUT WCME-OVERSEER $ STOP /ID=pid
$ DIFFERENCE CACERT-release.PEM WCME_ROOT:[ETC]CACERT.PEMand if it does copy the new file as when first configured.
$ @WASD_ROOT:[SRC.WCME]WCME_STARTUP.COM SYSTEM WASD
All logical names must be defined in the SYSTEM table.**
Basic steps:
**WCME_STARTUP.COM can assist with this.
Expanded descriptions:
$ DEFINE /SYSTEM /TRANS=CONCEALED /EXECUTIVE WCME_ROOT - $1$DKA0:[WASD_ROOT.LOCAL.WCME.]
$ DEFINE /SYSTEM /TRANS=CONCEALED /EXECUTIVE WCME_ROOT - $1$DKA0:[SYS0.SYSCOMMON.APACHE.WCME.]
This definition should be added to the server startup procedures prior to starting the application.**
Then the required directory structure must be created.***
$ CREATE /DIRECTORY WCME_ROOT:[ETC] $ CREATE /DIRECTORY WCME_ROOT:[LOG] $ CREATE /DIRECTORY WCME_ROOT:[PRIV] $ CREATE /DIRECTORY WCME_ROOT:[SSL] $ CREATE /DIRECTORY WCME_ROOT:[WWW]
***WCME_SETUP.COM will perform these steps.
$ COPY CACERT-release.PEM WCME_ROOT:[ETC]CACERT.PEM
The CA bundle is used by wCME to verify the authenticity of the Let's Encrypt service used to issue the server certificate.
The bundle will generally be up-to-date with new releases of wCME and also may be refreshed at any time using any reliable source. The cURL project provides such a resource. This is generated from the root certificates used by the Mozilla Foundation for its Firefox product (and others).
https://curl.haxx.se/docs/caextract.html
# WASD_CONFIG_MAP map /.well-known/acme-challenge/* \ /cgi-bin/wcme/.well-known/acme-challenge/* map=once
# APACHE$COMMON:[CONF]HTTPD.CONF <Location "/.well-known/acme-challenge/"> RewriteEngine on RewriteRule /\.well\-known/acme\-challenge/(.*) \ /cgi-bin/wcme/.well-known/acme-challenge/$1 </Location>
Once configured and loaded into the running server the mapping should be tested as described below.
$ DEFINE /SYSTEM /EXECUTIVE WCME_HTTP01 *
The standalone challenge server can be made active temporarily for testing the response using:
$ MCR CGI_EXE:WCME /CHECK=HTTP01
$ MCR APACHE$ROOT:[CGI-BIN]WCME /CHECK=HTTP01
Be sure to test challenge processing using:
for all services to be certified, with each providing a response something like:
Challenge received but no such file or directory.
The challenge received is the important positive indicator.
Anything else requires investigation and correction.
(The no such file or directory is just the absence of a real
Let's Encrypt challenge.)
# WASD_CONFIG_SERVICE [[https://www.example.org]] [ServiceSSLcert] WASD_ROOT:[LOCAL]FULLCHAIN_WWW_EXAMPLE_ORG.PEM
# WASD_CONFIG_GLOBAL [SSLcert] WASD_ROOT:[LOCAL]FULLCHAIN_WWW_EXAMPLE_ORG.PEM
# APACHE$COMMON:[CONF]SSL.CONF SSLCertificateFile /apache$root/conf/ssl_crt/fullchain_www_example_org.pem
The logical name WCME_CERT locates the server configuration directory containing the certificate(s).** For example:
$ DEFINE /SYSTEM /EXECUTIVE WCME_CERT WASD_ROOT:[LOCAL]
The logical name may define multiple values in which case the certificate is installed (i.e. copied to) multiple locations (e.g. WASD and Apache).
$ DEFINE /SYSTEM /EXECUTIVE WCME_CERT WASD_ROOT:[LOCAL],APACHE$ROOT:[CONF.SSL_CRT]Successfully renewed certificates may automatically be loaded into the server by defining the logical name WCME_LOAD containing an action to perform.** A subprocess is spawned to execute the action. The subprocess has SYSPRV and IMPERSONATE (for Apache) authorised. The action can be any DCL but commonly execution of a procedure. The WCME_LOAD.COM procedure provides a certificate reload (i.e. not a restart) for WASD v11.1.0 and later, a server restart for earlier versions, and an Apache restart, based on the environment. Evaluate suitability before use.
$ DEFINE /SYSTEM /EXECUTIVE WCME_LOAD "@WASD_ROOT:[SRC.WCME]WCME_LOAD.COM"
If the logical name does not exist the certificate load must be performed manually. The logical name may define multiple values in which case the certificate load is executed multiple times (e.g. for WASD and Apache).
$ DEFINE /SYSTEM /EXECUTIVE WCME_MAIL "system,smtp%""whomever@wherever""" $ DEFINE /SYSTEM /EXECUTIVE WCME_OPCOM "central,software,oper2"
URI | Description | ||
---|---|---|---|
/cgi-bin/wcme/admin/list | Reports on the current certificate(s) being managed. | ||
/cgi-bin/wcme/admin/log | Reports the current overseer log. | ||
/cgi-bin/wcme/admin/check/ca | Test that the CA is accessible by performing a directory. | ||
/cgi-bin/wcme/admin/check/load | Test WCME_LOAD logical name by performing the action. Caution: may restart server(s) or the like! | ||
/cgi-bin/wcme/admin/check/mail | Test WCME_MAIL logical name by sending a test message. | ||
/cgi-bin/wcme/admin/check/opcom | Test WCME_OPCOM logical name by sending a test 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/wcme/admin/* read,https:
<Location /cgi-bin/wcme/admin> AuthType Basic AuthName "OpenVMS authentication" AuthUserOpenVMS On require valid-user </Location>
wCME uses the PIPE DCL command and requires a minimum of VMS V7.1.
wCME is based on a port of the OpenBSD acme-client ACME implementation. This is an adaptation of an original application with the same name by Kristaps Dzonsons, a variant of which version 1 of wCME was adapted from. This has been moved into the OpenBSD baseline and the original is no longer maintained. To be able to remain current wCME needed to follow, resulting in version 2.
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 BSD-style Unix assumptions regarding the file-system and like. The acme-client application uses several independent processes communicating via a Unix socket IPC to partition functional activity, along with sandbox/jail behaviour to enable and constrain file-system access. wCME enables and controls file-system access to otherwise protected resources by enabling/disabling SYSPRV access within C-RTL file-system wrapper functions. Conditional compilation has general VMS adaptations using #if[n]def __VMS while wCME-specific code uses #if[n]def WCME_IDIOM.
wCME approaches the task of supporting both WASD and VMS Apache by avoiding the specifics of each, operating in an independent directory structure and allowing the sysadmin to direct the certificate products and server actions using logical names.
As WASD is designed to allow and known to be installed on ODS-2 volumes, wCME file-system elements are explicitly constrained to ODS-2 syntax. Apache comes along for the ride. This means alphanumerics, underscores and hyphens in directory and file names, 39+39 character names, with multiple periods converted to underscores, etc. ODS-2 compliance is a significant element of the WCME_IDIOM.
wCME also includes a fit-for-purpose port of the LibreSSL TLS library code, interfaced as best it can be to mainstream OpenSSL. LibTLS is a significant dependency of acme-client. This should NOT be considered a thorough and general-purpose port. LibreSSL is licensed under the same conditions as OpenSSL.
https://github.com/libressl-portable/openbsd/tree/master/src/lib/libtlsThe OpenSSL toolkit stays under a dual license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit. See below for the actual license texts. Actually both licenses are BSD-style Open Source licenses. In case of any license issues related to OpenSSL please contact openssl-core@openssl.org.https://github.com/libressl/libressl/blob/master/src/LICENSE
[{ "type": "urn:acme:error:unauthorized", "detail": "Invalid response from http://host.name/.well-known/acme-challenge/ahZfPYUH1TbtjGC4A6B7SAS--MYez54oYg6ShEJFHVE: 8< snip 8<" "status": 403 }]
In this case the challenge cannot be accessed due to lack of permission of some description. See test challenge processing.
[{ "type": "urn:acme:error:unauthorized", "detail": "Error creating newcert :: authorizations for these names not found or expired: the.host.name", "status": 403 }]
Either the account registration step has been neglected, or Let's Encrypt has expired the account created when originally performed. Undertake the account registration and initial certification steps (again).
A few URLs of interest and example.
Many thanks to Kristaps Dzonsons for making the original acme-client-portable freely available.