Serve Let's Encrypt certificates for stratum TLS on public domains
This commit is contained in:
+46
-43
@@ -15,14 +15,16 @@ import {
|
||||
export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => {
|
||||
// The user's stratum ports are EXTERNAL ports only. Read reactively so
|
||||
// changing them in the Configure action re-runs this and updates the
|
||||
// binding — the same mechanism that adds/removes the TLS interface when TLS
|
||||
// is toggled. The in-container ports stay fixed (see utils.ts), so each
|
||||
// change updates the existing binding rather than orphaning it.
|
||||
// binding. The in-container ports stay fixed (see utils.ts), so each change
|
||||
// updates the existing binding rather than orphaning it.
|
||||
//
|
||||
// The TLS toggle is deliberately NOT read here: both stratum interfaces now
|
||||
// exist unconditionally, so toggling it no longer rebinds anything — it
|
||||
// only changes which certificates stunnel serves, which is main.ts's job.
|
||||
const ports = await storeJson
|
||||
.read((s) => ({
|
||||
stratum: s.stratumPort,
|
||||
stratumTls: s.stratumTlsPort,
|
||||
tlsEnabled: s.tlsEnabled,
|
||||
}))
|
||||
.const(effects)
|
||||
|
||||
@@ -75,57 +77,58 @@ export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => {
|
||||
})
|
||||
receipts.push(await stratumOrigin.export([stratum]))
|
||||
|
||||
// TLS stratum (conditional) — stunnel terminates TLS with the persisted
|
||||
// package-managed certificate and forwards to ckpool's loopback-only
|
||||
// second bind. The OS sees a raw TCP port; TLS lives at the app layer, so
|
||||
// the noSsl scheme is deliberately stratum+ssl.
|
||||
if (ports?.tlsEnabled) {
|
||||
const tlsMulti = sdk.MultiHost.of(effects, stratumTlsHostId)
|
||||
const tlsOrigin = await tlsMulti.bindPort(stratumTlsInternalPort, {
|
||||
protocol: null,
|
||||
preferredExternalPort: externalStratumTlsPort,
|
||||
addSsl: null,
|
||||
secure: { ssl: false },
|
||||
})
|
||||
const stratumTls = sdk.createInterface(effects, {
|
||||
name: i18n('Stratum (TLS)'),
|
||||
id: 'stratum-tls',
|
||||
description: i18n(
|
||||
'TLS-encrypted stratum endpoint (self-signed certificate — see the Stratum TLS Certificate action)',
|
||||
),
|
||||
type: 'api',
|
||||
masked: false,
|
||||
schemeOverride: { ssl: 'stratum+ssl', noSsl: 'stratum+ssl' },
|
||||
username: null,
|
||||
path: '',
|
||||
query: {},
|
||||
})
|
||||
receipts.push(await tlsOrigin.export([stratumTls]))
|
||||
}
|
||||
// TLS stratum — stunnel terminates TLS and forwards to one of ckpool's
|
||||
// loopback-only binds. The OS sees a raw TCP port; TLS lives at the app
|
||||
// layer, so the noSsl scheme is deliberately stratum+ssl.
|
||||
//
|
||||
// This is bound and exported UNCONDITIONALLY, unlike the local-TLS toggle
|
||||
// that used to gate it. A public domain is attached to an interface in the
|
||||
// StartOS UI, so gating the interface on the toggle made the Let's Encrypt
|
||||
// path unreachable for anyone who had not first enabled the self-signed
|
||||
// one — there was nothing to attach the domain to. main.ts decides whether
|
||||
// stunnel actually runs; when neither a certificate nor a domain is
|
||||
// configured the port simply doesn't answer, and the Stratum TLS health
|
||||
// check says so.
|
||||
const tlsMulti = sdk.MultiHost.of(effects, stratumTlsHostId)
|
||||
const tlsOrigin = await tlsMulti.bindPort(stratumTlsInternalPort, {
|
||||
protocol: null,
|
||||
preferredExternalPort: externalStratumTlsPort,
|
||||
addSsl: null,
|
||||
secure: { ssl: false },
|
||||
})
|
||||
const stratumTls = sdk.createInterface(effects, {
|
||||
name: i18n('Stratum (TLS)'),
|
||||
id: 'stratum-tls',
|
||||
description: i18n(
|
||||
'TLS-encrypted stratum endpoint. Miners on a public domain attached here get a CA-issued certificate automatically; on the local network the self-signed certificate is used (see the Stratum TLS Certificate action)',
|
||||
),
|
||||
type: 'api',
|
||||
masked: false,
|
||||
schemeOverride: { ssl: 'stratum+ssl', noSsl: 'stratum+ssl' },
|
||||
username: null,
|
||||
path: '',
|
||||
query: {},
|
||||
})
|
||||
receipts.push(await tlsOrigin.export([stratumTls]))
|
||||
|
||||
// Drop bindings we no longer use — primarily the stratum-tls binding after
|
||||
// the user disables TLS. With fixed internal ports this no longer has to
|
||||
// clean up after port changes (the whole point of keeping them fixed), but
|
||||
// it still matters for the TLS toggle, and it clears orphans left by older
|
||||
// versions of this package that did move the internal port.
|
||||
// Drop bindings we no longer use. With fixed internal ports and an
|
||||
// unconditional TLS binding, all three are now permanent — this only
|
||||
// clears orphans left by older versions of this package, which did move the
|
||||
// internal port and did drop the TLS binding when the toggle was off.
|
||||
await sdk.clearBindings(effects, {
|
||||
except: [
|
||||
{ id: uiHostId, internalPort: uiPort },
|
||||
{ id: stratumHostId, internalPort: stratumInternalPort },
|
||||
...(ports?.tlsEnabled
|
||||
? [{ id: stratumTlsHostId, internalPort: stratumTlsInternalPort }]
|
||||
: []),
|
||||
{ id: stratumTlsHostId, internalPort: stratumTlsInternalPort },
|
||||
],
|
||||
})
|
||||
|
||||
// Exported service interfaces are a SECOND registry, independent of the
|
||||
// bindings above and with its own cleanup effect. Clearing bindings alone
|
||||
// leaves an orphaned interface record behind, which the UI still lists — so
|
||||
// a port change can produce two identical "Stratum" rows. Excluding
|
||||
// 'stratum-tls' when TLS is off also removes that row when the user
|
||||
// disables TLS, rather than leaving a dead endpoint on display.
|
||||
// a port change can produce two identical "Stratum" rows.
|
||||
await effects.clearServiceInterfaces({
|
||||
except: ['ui', 'stratum', ...(ports?.tlsEnabled ? ['stratum-tls'] : [])],
|
||||
except: ['ui', 'stratum', 'stratum-tls'],
|
||||
})
|
||||
|
||||
return receipts
|
||||
|
||||
Reference in New Issue
Block a user