Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hostname option for generating self-signed certificates on the fly. #200

Merged
merged 33 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
98605a4
Document the intent.
danfuzz Dec 13, 2023
f4b4bbd
Add `selfSigned`.
danfuzz Dec 13, 2023
63961b5
Clarify.
danfuzz Dec 13, 2023
db470d1
Also cover wildcard `*.localhost` and the common IP4/6 addresses.
danfuzz Dec 13, 2023
d446a47
Update certs.
danfuzz Dec 13, 2023
152fc79
Extract IP patterns.
danfuzz Dec 13, 2023
7f5a36a
Tweak.
danfuzz Dec 13, 2023
156cf70
Add `checkHostname()`.
danfuzz Dec 13, 2023
34c4c95
Allow for filter functions.
danfuzz Dec 13, 2023
0dccdfd
Use `checkHostname()`.
danfuzz Dec 13, 2023
f9d010f
Make these private.
danfuzz Dec 13, 2023
98d1063
Simplify.
danfuzz Dec 13, 2023
9d7beba
Make it a simple property.
danfuzz Dec 13, 2023
b7db6ef
Make it a simple property.
danfuzz Dec 13, 2023
9b1312b
Changelog.
danfuzz Dec 13, 2023
57261a9
Fix terminology.
danfuzz Dec 14, 2023
76b6901
Move.
danfuzz Dec 14, 2023
d52e253
Canonicalize.
danfuzz Dec 14, 2023
7554c9c
Fix terminology.
danfuzz Dec 14, 2023
d2f7efc
Fix indentation.
danfuzz Dec 14, 2023
7521fef
Fix variable shadowing. (Appease the linter.)
danfuzz Dec 14, 2023
fa42c84
Get `allowAny` to work.
danfuzz Dec 14, 2023
a8717aa
Expand tests.
danfuzz Dec 14, 2023
de5b290
Allow IP addresses as hostnames.
danfuzz Dec 14, 2023
42e95af
Rework `parseMount()`.
danfuzz Dec 14, 2023
8fca890
Remove `checkMount()`.
danfuzz Dec 14, 2023
193eb5e
Add `::1` as registered hostname.
danfuzz Dec 14, 2023
a044515
Changelog.
danfuzz Dec 14, 2023
2956e33
Leave a blatant TODO.
danfuzz Dec 14, 2023
4245801
Fix lint-noticed problems.
danfuzz Dec 14, 2023
bf3a410
Actually support `selfSigned`.
danfuzz Dec 15, 2023
5602ccf
Demo `selfSigned`.
danfuzz Dec 15, 2023
b8b6660
Update lock file.
danfuzz Dec 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ Breaking changes:
Other notable changes:
* Extracted base class `BaseSystem` from `UsualSystem`, to help avoid code
duplication in downstream projects.
* Mostly minor tweaks and improvements, throughout the codebase.
* Added ability to make one or more hostnames use automatically-generated
self-signed certificates.
* A bunch of cleanup / rationalization around the configuration code, especially
with hostname and IP address parsiong.
* Other mostly minor tweaks and improvements, throughout the codebase.
* Pulled in improved `bashy-lib`, and adjusted accordingly.

### v0.5.19 -- 2023-12-04
Expand Down
33 changes: 21 additions & 12 deletions doc/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,29 @@ one-element array.
### `hosts`

`hosts` is a list of hostname bindings. These map possibly-wildcarded hostnames
to certificate-key pairs to use to authenticate an endpoint as those hosts.
to certificate-key pairs to use to authenticate an endpoint as those hosts. Each
entry has the following bindings:

* `hostnames` — A list of one or more hostnames to recognize; this is
required. Hostnames are allowed to start with a `*` to indicate a wildcard of
_any number of subdomains, including zero._ Note that this is unlike how
wildcards work in the underlying certificates, where a `*` denotes exactly
one subdomain. And, to be clear, the hostname `*` will match _any_ hostname
at all, with any number of subdomains.
* `certificate` — PEM format string containing the certificate to use for
this entry. This is required if `selfSigned` is absent or `false`.
* `privateKey` — PEM format string containing the private key to use for
this entry. This is required if `selfSigned` is absent or `false`.
* `selfSigned` — Optional boolean, which, if `true`, causes the system to
generate a self-signed certificate for this entry. This is mostly useful in
testing scenarios, and more specifically when running a server on your
development machine, e.g. and commonly responding on `localhost`.

```js
const hosts = [
{
hostnames: ['localhost', '*'],
certificate: '-----BEGIN CERTIFICATE-----...',
privateKey: '-----BEGIN PRIVATE KEY-----...'
hostnames: ['localhost', '*'],
selfSigned: true
},
{
hostnames: ['*.example.com'],
Expand All @@ -51,19 +66,13 @@ const hosts = [
];
```

Hostnames are allowed to start with a `*` to indicate a wildcard of _any number
of subdomains, including zero._ Note that this is unlike how wildcards work in
the underlying certificates, where a `*` denotes exactly one subdomain. And, to
be clear, the hostname `*` will match _any_ hostname at all, with any number of
subdomains.

This section is only required if at least one endpoint is to respond to
host-authenticated protocols (which is nearly always, at least in standalone
uses).

**Note:** If you want to keep the text of the keys and certificates out of the
main configuration file, then the thing to do is just use the standard Node `fs`
package to read the contents.
main configuration file, then a reasonablhy easy tactic is to use the standard
Node `fs` package to read the contents of files named in the configuration.

### `services`

Expand Down
6 changes: 5 additions & 1 deletion etc/example-setup/config/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ const RUN_DIR = `${VAR_DIR}/run`;
// Host / certificate bindings.
const hosts = [
{
hostnames: ['localhost', '*'],
hostnames: ['localhost'],
selfSigned: true
},
{
hostnames: ['*', '127.0.0.1', '::1'],
certificate: await readFile('localhost-cert.pem'),
privateKey: await readFile('localhost-key.pem')
}
Expand Down
52 changes: 26 additions & 26 deletions etc/example-setup/config/localhost-cert.pem
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE5TCCAs2gAwIBAgIJAKK7fgygdpVgMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0yMzEyMDUyMzAzMzdaFw0yNDEyMDQyMzAzMzdaMBQx
MIIFCjCCAvKgAwIBAgIJANsBjdDNTUTqMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0yMzEyMTMxOTI2MDJaFw0yNDEyMTIxOTI2MDJaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
ggIBANcwRW55wKSUexKBSjyy26TyBc/iLESPJbyWWiud8SmpaKLwGQ2zTMXJ3wIj
8DzVjpGQitRMHqJubovCpjkmXTd5pJbd1H5CGUILi3sWovHbXbOUjnUJtqkvOViR
LKlFZvAKvf4lZs4iXWXkCjCXszF2mRGvf+j+QSR/1DHZRjX26RnnqJnx621+IEkk
NMd37SQIMLxhslKRYfPKocfQddZxJTEZ2cO3+b5ReLkhdj/TosRend1UyYAVNgGd
/UGoIEayA0EA6106fw01zYM5WnQu6U6cLdYS7sIAZfQPiD28Gm0vzlnNsAzhhxpr
h+H0dxH1BkEk/9DRWiUTCHSf15kb69IMTKr0FwDQDpFYv7kbl1GpG+ey5xpyCd1Y
zqORwGHO+VgHCK/b7KjZqdXHluxhCPA3RkmLBdR8OjE1JRSqbMBb7cybZxT7rZbk
Bs8FM/C1xSJmgCwuvCf5upNroqnW495GeZkKc4rNjGTFZ8mSs4lrf04ZmRdnc/qL
2ouyvJnnzFOkAvjfsGTQiebMco8GV1eceWIwBPE8km77uY3hEnAC+84U5pj+LKr7
pzQ0fHpNbAAubJepTh2XnNp/xr8Nd52U0tjl9hbki6wmdIGAMQ8gkuLQw+Sia9mA
hbmuNP+zxdrOykw7QUQ6N4pATjiPR7x9JhYO3Hk2IPZVp+TPAgMBAAGjOjA4MBQG
A1UdEQQNMAuCCWxvY2FsaG9zdDALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYB
BQUHAwEwDQYJKoZIhvcNAQELBQADggIBAAnDvKxG29MZU37Ae9Xa4i10PVr8ricE
kaucafMgz1zYXZz2aaCXylwikZ4jAeYOQf4MrghrfBisy6L0dlPyWY8yK1qOStzu
qauaz26XgmRqTN2lKvTJ6vwv7+iA+AZJqtpS4lkt6WSpIGo/GznV32T6wPhL9gPs
cO0YRUeHbbqDtNUIiFnMddteAHOO2kvONOYrXIYERYf+MtM4Fkd1h10X2Rip7oD1
v/O5wzlb8RK4z82QTxdO9SPpyiGVvmJ6p9h2t4UTQo0/6D6/dkZz9E8iDijS0hJz
pLLO+bKL00UW3NO/bsXWKzDGOaBkIFC5pa6IgmjDGUYKwNJkEy4CBk+MTBVCoUMm
hLU2vouvV+qSulZGQRgsuQqyXdf7tUmw4vpZs6o+/NncoZZ+YLACe6v+yxDM+bMU
GHytISAG2o/IPoFoiXjfb46T0nmoXAxTFiOR/EqHsOWUdaqOH+srskoLULe0rXIy
evHtjEhXLjooDt1M5FQu1a6LEVQU1SZyQF8b+bqzcqBmpuLg5mRjrpceoZNjqtsg
Af2W9lSrz1hPnwtjFMxrJRMM5NCAZLXXdx0VSV1pAKhy4HN9E9yVSNcgBy0KLfBy
RGTRFT3LDgZcSFOAYq+8ktocUkKDadHkzHU/jFnFPmQaNZpWxY+fJj6pHG0Zy/5W
WCEqrolSlUhu
ggIBAOSIwCV6cM0vGDQ9Z1EkpS7ufdWXj2WBmcWpoFPjt5wASHRrsU9Y7Nnpp18k
jkuZycSxfzZ4YdzOSlyAHHumtoGhSDVRP34JLtAMrUNeB2//w7+05n1UCsB1YFam
zUDHmrhr7zmSG/yojntuH0xUyRrK5Pgy7XQsucIQWn3a69VdFIQQELwUCWd8ZmJO
kGoD2AZMLblD3y8VnRnMSGUacuZj1qcJN3UxYm7xbIjDSh8UiuJUHFAeZ1QhQ9ut
DeVE7DRrbkHn67DPW5yep5/xmJJYJxh4aNgkJsn57EtNBK3AqnITeJwcEln1CkU+
SnNBo0DwovI25fAbu2COUAKn1BgaoRhME0TM9M4DBcf3GcKb8nc9WUCVKILuADVT
KBY4GPMbigtFlQFPV4a9xVA+2dY3h8br9nG9yBdkyAVyghRUEbBS9rzW4IL9EmSW
P5aNdgSwqaX6JBbpqn+I/S+R+GNMjTAaREkkbhmCJ9sLkPjfLZbFjgB7BmNRGa7y
VvGxOHMiRL3gs+qeolcQphYOnt/gYotdX6V8/vxUKaFcPERkU88Y/XACHSkb5YSM
ktPpPGdR2RBT2WH9JSKPYTaM3nB5URU6b6vYT7bKRTrNGkPazhvlYco+WoWPwVE2
bzMJloiRQDUFAWISONacoX8Y0DY3ejVBOGJI8itZ36+CPdtpAgMBAAGjXzBdMDkG
A1UdEQQyMDCCCWxvY2FsaG9zdIILKi5sb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAA
AAAAAAAAAAEwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqG
SIb3DQEBCwUAA4ICAQCC1SwknHIzCX7915R1vA5i163/Osqeg4c4Z4AAE03OOc1A
NEZIOdhbwBet/E4aD5sNYIrmiqTr9KkfbFYLuf8YD99qZlz1ZJwZNKFLyAJxZC4l
K9X1hvWLVsVJvC+yzO4pV6Tj0ybedhxJRsSFwPb8vsgsY6QdFOyMJG37hTySagEZ
IoEQq8kFQXJNbty5T9UdcBD4TryN+Ls5lQTf6qENxMpKTOik/UucK8XcWRwrlyLB
OKyBkijQMtxsVLp559cXUJ+F0xZ+ArlyLbVre4vRT74XiaWmW61Sd/R0fsraVQRG
K/T+uaEjLfhyPRysVqwExLZWnUCtvKjQmlXchw2xTZtfJxhhyXK3AsthvDVCF+mt
SwdCybJbN0HUXQttHvq2am1nnTjQ4BDcRb76ylbgrZVT/egc+RkB6PjSi58ewFaT
pAITGQ4JLCRb4N/TNkUWLQ4tsW+XD/vbTx25x6PEWyISx/lZvkgGbJjtmAyy9pkM
Zwhk3hjicjosS16KGA9WgfyZo7gMdqVN7WCuRB3aDavTPJzvmsjOkb4QpBPbII12
eNxXjwCJDZb/milRuczwwprVarUxIW8f8pFeJ4aM1pM+5w2rrWl1sdRaVB04BZX6
cNGIwhTwTrCSmiWkRHfBgf2Q4Oth1aACTW1v8Ys4tjfaWjYTBKuDD7n5XbBO/w==
-----END CERTIFICATE-----
100 changes: 50 additions & 50 deletions etc/example-setup/config/localhost-key.pem
Original file line number Diff line number Diff line change
@@ -1,52 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
JwT8J/eMeshZApTdzO+e4r0MS9YhGg==
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
MzY+MOyH3+g5WZB9W2VCav9NXj1RDw==
-----END PRIVATE KEY-----
8 changes: 4 additions & 4 deletions scripts/lib/lactoserv/make-localhost-cert
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ define-usage --with-help $'
${name} [<opt> ...]

Makes a certificate and associated key which can be used when serving HTTPS
(or HTTP2 maybe?) from `localhost`. The files are stored in the built output
directory. The certificate needs to be added as a trusted root to the web
browser.
and HTTP2 from `localhost`. The files are stored in the built output directory.
To be used seamlessly, the certificate needs to be added as a trusted
certificate the web browser or explicitly accepted when presented.

For more information, see:
<https://letsencrypt.org/docs/certificates-for-localhost/>
Expand Down Expand Up @@ -75,7 +75,7 @@ config='
distinguished_name = dn
x509_extensions = x509_ext
[x509_ext]
subjectAltName = DNS:localhost
subjectAltName = DNS:localhost,DNS:*.localhost,IP:127.0.0.1,IP:::1
keyUsage = digitalSignature
extendedKeyUsage = serverAuth
'
Expand Down
5 changes: 4 additions & 1 deletion src/app-config/export/EndpointConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ export class EndpointConfig extends NamedConfig {
services = {}
} = config;

this.#hostnames = Util.checkAndFreezeStrings(hostnames, Uris.HOSTNAME_PATTERN);
this.#hostnames = Util.checkAndFreezeStrings(
hostnames,
(item) => Uris.checkHostname(item, true));

this.#interface = Object.freeze(Uris.parseInterface(iface));
this.#mounts = MountConfig.parseArray(mounts);
this.#protocol = Uris.checkProtocol(protocol);
Expand Down
60 changes: 49 additions & 11 deletions src/app-config/export/HostConfig.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright 2022-2023 the Lactoserv Authors (Dan Bornstein et alia).
// SPDX-License-Identifier: Apache-2.0

import { MustBe } from '@this/typey';

import { BaseConfig } from '#x/BaseConfig';
import { Certificates } from '#x/Certificates';
import { Uris } from '#x/Uris';
Expand All @@ -11,16 +13,19 @@ import { Util } from '#x/Util';
* Configuration representation for a "host" item, that is, a thing that
* defines the mapping from one or more names to a certificate / key pair.
*
* Accepted configuration bindings (in the constructor).
*
* * `{string|string[]} hostnames` -- Names of the hosts associated with this
* entry. Names can in the form `*.<name>` to match any subdomain of `<name>`,
* or `*` to be a complete wildcard (that is, matches any name not otherwise
* mentioned).
* mentioned). Required.
* * `{string|Buffer} certificate` -- The certificate chain for `hostnames`, as
* PEM-encoded data.
* PEM-encoded data. Required if `selfSigned` is absent or `false`.
* * `{string|Buffer} privateKey` -- The private key associated with
* `certificate`, as PEM-encoded data.
*
* Accepted configuration bindings (in the constructor). All are required:
* `certificate`, as PEM-encoded data. Required if `selfSigned` is absent or
* `false`.
* * `{boolean} selfSigned` -- Optional indicator of whether this entry should
* use a self-signed certificate.
*/
export class HostConfig extends BaseConfig {
/** @type {string[]} The hostnames in question. */
Expand All @@ -32,6 +37,9 @@ export class HostConfig extends BaseConfig {
/** @type {string} The private key, as PEM-encoded data. */
#privateKey;

/** @type {boolean} Is this to be a self-signed certificate? */
#selfSigned;

/**
* Constructs an instance.
*
Expand All @@ -40,11 +48,29 @@ export class HostConfig extends BaseConfig {
constructor(config) {
super(config);

const { hostnames, certificate, privateKey } = config;
const { hostnames, certificate, privateKey, selfSigned = false } = config;

this.#hostnames = Util.checkAndFreezeStrings(hostnames, Uris.HOSTNAME_PATTERN);
this.#certificate = Certificates.checkCertificateChain(HostConfig.#bufferFilter(certificate));
this.#privateKey = Certificates.checkPrivateKey(HostConfig.#bufferFilter(privateKey));
this.#hostnames = Util.checkAndFreezeStrings(
hostnames,
(item) => Uris.checkHostname(item, true));

this.#selfSigned = MustBe.boolean(selfSigned);

if (selfSigned) {
if ((certificate !== undefined) && (certificate !== null)) {
throw new Error('Cannot use `certificate` with `selfSigned === true`.');
}
if ((privateKey !== undefined) && (privateKey !== null)) {
throw new Error('Cannot use `certificate` with `selfSigned === true`.');
}
this.#certificate = null;
this.#privateKey = null;
} else {
this.#certificate =
Certificates.checkCertificateChain(HostConfig.#bufferFilter(certificate));
this.#privateKey =
Certificates.checkPrivateKey(HostConfig.#bufferFilter(privateKey));
}
}

/**
Expand All @@ -55,16 +81,28 @@ export class HostConfig extends BaseConfig {
return this.#hostnames;
}

/** @returns {string} The certificate, as PEM-encoded data. */
/**
* @returns {?string} The certificate as PEM-encoded data, or `null` if
* {@link #selfSigned} is `true`.
*/
get certificate() {
return this.#certificate;
}

/** @returns {string} The private key, as PEM-encoded data. */
/**
* @returns {?string} The private key as PEM-encoded data, or `null` if
* {@link #selfSigned} is `true`.
*/
get privateKey() {
return this.#privateKey;
}

/** @returns {boolean} Is this entry to use a self-signed certificate? */
get selfSigned() {
return this.#selfSigned;
}


//
// Static members
//
Expand Down
Loading