mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 23:14:53 +09:00
Remove automatic creation of TLS certificate
The automatic creation of a self-signed certificate while nice presents several problems. To make connections not reject the self-signed certificate the validation of TLS certificates are wholesale disabled, decreasing the security even when properly set up and trusted certificates are used. To make the created self-signed certificate pass validation it is necessary for it to list the host names it's valid for and to copy it to each machine that intends to connect to the server. These two steps complicate automating the process in a reliable way and will likely lead to confused users seeking to disable TLS certificate validation as the easy way out to problems. For these reasons, remove the automating creation of a broke self-signed certificate, fix validation of certificates and document how to set up TLS properly. This makes HTTPS recommended when accessing over the internet, but still something which must be explicitly configured.
This commit is contained in:
1 parent
a094a06cd1
commit
3592ff3269
16 files changed
+343
-179
No files matched your search
+2
-2
@@ -54,11 +54,10 @@ Version 2.0.0
|
||||
- Added metric mapping of slave and instance ids to their names, allowing
|
||||
them to be displayed by name in queries that join with the mapping.
|
||||
- Added option to configure timeout for exported Prometheus metrics.
|
||||
- Added option to set address to bind HTTP(S) port to.
|
||||
|
||||
### Changes
|
||||
|
||||
- Fixed sslCert and sslPrivKey entries being ignored in the config.
|
||||
- Changed ssl key creation to be done at startup instead of on npm install.
|
||||
- Added error handling during master startup.
|
||||
- Factorio game and rcon port now defaults to a random port above 49151.
|
||||
- Removed unimplemented mods update command.
|
||||
@@ -180,6 +179,7 @@ Version 2.0.0
|
||||
- Removed usage of socket.io entirely in favor of a plain WebSocket connection.
|
||||
- Renamed clusterioMod plugin to subspace_storage.
|
||||
- Master no longer creates secret-api-token.txt.
|
||||
- Removed automatic creation of self-signed TLS certificate.
|
||||
|
||||
|
||||
Version 1.2.4
|
||||
|
||||
@@ -206,21 +206,21 @@ with either . or .. (which will then be resolved to an absolute path).
|
||||
|
||||
## Configure Master Server
|
||||
|
||||
By default the master server will create a self signed TLS certificate
|
||||
and listen on HTTPS on port 8443. You can change the port used with the
|
||||
command
|
||||
By default the master server will listen for HTTP on port 8080. You can
|
||||
change the port used with the command
|
||||
|
||||
npx clusteriomaster config set master.https_port 1234
|
||||
npx clusteriomaster config set master.http_port 1234
|
||||
|
||||
If you plan to make your cluster available externally set the address
|
||||
that it will be accessible under with, for example
|
||||
|
||||
npx clusteriomaster config set master.external_address https://192.0.2.4:8443/
|
||||
npx clusteriomaster config set master.external_address http://203.0.113.4:1234/
|
||||
|
||||
Change the url to reflect the IP, protocol, and port the master server
|
||||
is accessible under, dns names are also supported. If proxyed under a
|
||||
sub-path include the sub path as well and make sure the URL ends with a
|
||||
slash.
|
||||
is accessible under, dns names are also supported. If you're planning
|
||||
on making the master server accessible on the internet it's recommended
|
||||
to set up TLS, see the [Setting Up TLS](/docs/setting-up-tls.md)
|
||||
document for more details.
|
||||
|
||||
You can list the config of the master server with the `npx
|
||||
clusteriomaster config list` command. See the [readme for
|
||||
@@ -228,43 +228,6 @@ clusteriomaster config list` command. See the [readme for
|
||||
information.
|
||||
|
||||
|
||||
### Hosting Behind Proxy
|
||||
|
||||
If you want to host the master server behind a reverse proxy the
|
||||
recommended setup is to disable HTTPS and host it on a high port
|
||||
over HTTP to which you point your proxy to. For example to configure
|
||||
port 8080 as the HTTP port for the master server.
|
||||
|
||||
npx clusteriomaster config set master.https_port # unsets https_port
|
||||
npx clusteriomaster config set master.http_port 8080
|
||||
|
||||
The proxy can then forward requests to a sub-path like `/cluster/` to
|
||||
`http://localhost:8080/`.
|
||||
|
||||
Additianlly WebSocket connections to api/socket relative to the sub-path
|
||||
the interface is hosted under also needs to be forwarded. See below for
|
||||
the specifics of setting this up on Apache.
|
||||
|
||||
|
||||
#### Apache Config
|
||||
|
||||
To proxy with Apache make sure that proxy_module, proxy_http_module and
|
||||
proxy_wstunnel_module is loaded in the config, then add the following
|
||||
directives to wherever is the approriate place for your flavour of
|
||||
Apache config organization.
|
||||
|
||||
```apache
|
||||
<Location /cluster/>
|
||||
ProxyPass "http://localhost:8080/"
|
||||
</location>
|
||||
<Location /cluster/api/socket>
|
||||
ProxyPass "ws://localhost:8080/api/socket"
|
||||
</Location>
|
||||
```
|
||||
|
||||
Pay attention to the use of trailing slashes, they are important.
|
||||
|
||||
|
||||
### Setting up an admin account
|
||||
|
||||
Before you can manage the cluster you need to bootstrap an admin account
|
||||
|
||||
+63
-17
@@ -19,7 +19,7 @@ Port to host HTTP server on. If set to null no HTTP server will be
|
||||
exposed. At least one of this and master.https_port needs to be set to
|
||||
a port.
|
||||
|
||||
Defaults to null.
|
||||
Defaults to 8080.
|
||||
|
||||
|
||||
### master.https_port
|
||||
@@ -29,7 +29,10 @@ master.tls_private_key as the certificate and private key for the HTTPS
|
||||
server. If set to null no HTTPS server will be exposed. At least one
|
||||
of this and master.http_port needs to be set to a port.
|
||||
|
||||
Defaults to 8443.
|
||||
See the [Setting up TLS](/docs/setting-up-tls.md) document for a guide
|
||||
to setting up HTTPS with Clusterio.
|
||||
|
||||
Defaults to null.
|
||||
|
||||
|
||||
### master.bind_address
|
||||
@@ -54,29 +57,20 @@ Defaults to null meaning assume localhost.
|
||||
|
||||
### master.tls_certificate
|
||||
|
||||
TLS certificate to use for the HTTPS server when master.https_port is
|
||||
configured. If neither this nor the configured master.tls_private_key
|
||||
exists a self signed certificate is automatically created.
|
||||
Path to TLS certificate to use for the HTTPS server when
|
||||
master.https_port is configured.
|
||||
|
||||
Defaults to "database/certificates/cert.crt".
|
||||
|
||||
|
||||
### master.tls_private_key
|
||||
|
||||
TLS private key to use for the HTTPS server when master.https_port is
|
||||
configured. If neither this nor the configured master.tls_certificate
|
||||
exists a self signed certificate is automatically created.
|
||||
Path to TLS private key to use for the HTTPS server when
|
||||
master.https_port is configured.
|
||||
|
||||
Defaults to "database/certificates/cert.key".
|
||||
|
||||
|
||||
### master.tls_bits
|
||||
|
||||
Number of bits to use when creating a self signed certificate.
|
||||
|
||||
Defaults to 2048.
|
||||
|
||||
|
||||
### master.auth_secret
|
||||
|
||||
Base64 encoded authentication secret used to verify tokens issued to
|
||||
@@ -178,7 +172,7 @@ Defaults to "instances".
|
||||
|
||||
URL to connect to the master server to.
|
||||
|
||||
Defaults to "https://localhost:8443/".
|
||||
Defaults to "http://localhost:8080/".
|
||||
|
||||
|
||||
### slave.master_token
|
||||
@@ -191,6 +185,17 @@ create a new slave config with the correct url and token in it.
|
||||
Defaults to "enter token here".
|
||||
|
||||
|
||||
### slave.tls_ca
|
||||
|
||||
Path to certificate in PEM format to use for validating a TLS connection
|
||||
to the master server. If you have a self signed certificate on the
|
||||
master server you will need to copy the certificate to your slaves and
|
||||
set it as the certificate authority with this option.
|
||||
|
||||
Defaults to null meaning use Node.js's default set of trusted
|
||||
certificate authorities.
|
||||
|
||||
|
||||
### slave.public_address
|
||||
|
||||
External address instances hosted on this slave can be accessed on.
|
||||
@@ -211,7 +216,7 @@ Defaults to 5.
|
||||
|
||||
|
||||
Instance Configuration
|
||||
======================
|
||||
----------------------
|
||||
|
||||
### instance.name
|
||||
|
||||
@@ -419,3 +424,44 @@ need to be enabled on the master server in order for them to be loaded
|
||||
on instances.
|
||||
|
||||
Defaults to true
|
||||
|
||||
|
||||
Control Configuration
|
||||
---------------------
|
||||
|
||||
### control.master_url
|
||||
|
||||
URL to connect to the master server to.
|
||||
|
||||
Defaults to null meaning complain about it not being set and exit.
|
||||
|
||||
|
||||
### control.master_token
|
||||
|
||||
Access token used for authenticating with the master server. You can
|
||||
generate an access token with `clusteriomaster bootstrap
|
||||
generate-user-token <username>`, or use the `clusteriomaster bootstrap
|
||||
create-ctl-config <username>` to create a new ctl config with the
|
||||
correct url and token in it.
|
||||
|
||||
Defaults to null meaning complain about it not being set and exit.
|
||||
|
||||
|
||||
### control.tls_ca
|
||||
|
||||
Path to certificate in PEM format to use for validating a TLS connection
|
||||
to the master server. If you have a self signed certificate on the
|
||||
master server you will need to copy the certificate to the computer you
|
||||
run clusterctl from and set it as the certificate authority with this
|
||||
option.
|
||||
|
||||
Defaults to null meaning use Node.js's default set of trusted
|
||||
certificate authorities.
|
||||
|
||||
|
||||
### control.reconnect_delay
|
||||
|
||||
Duration in seconds to wait before attempting to reconnect with the
|
||||
master server after the connection is dropped.
|
||||
|
||||
Defaults to 2.
|
||||
@@ -0,0 +1,112 @@
|
||||
Setting up TLS
|
||||
==============
|
||||
|
||||
By default Clusterio will listen for connections over HTTP which is fine
|
||||
for local networks but over the Internet it should idealy be over HTTPS
|
||||
as there are some credentials that could be exposed to eavesdroppers.
|
||||
The recommended setup is to have an ordinary HTTPS server software like
|
||||
Apache or Nginx to act as a reverse proxy in front of the Clusterio
|
||||
master server. The reverse proxy would then be set up with a TLS
|
||||
certificate from a Certificate Authoritiy like [Let's
|
||||
Encrypt](https://letsencrypt.org). Alternatively you can have Clusterio
|
||||
listen for connections over HTTPS directly by providing it with a TLS
|
||||
certificate and private key.
|
||||
|
||||
Note that a TLS certificate signed by a recognized Certificate Authority
|
||||
in general requires that you own a domain. See the last section for how
|
||||
to create and use a self-signed certificate for an IP address if you
|
||||
don't have a domain.
|
||||
|
||||
|
||||
Using a reverse proxy
|
||||
---------------------
|
||||
|
||||
Configure the proxy to forward requests over HTTP to the port the master
|
||||
server is listening to so that for example a request to
|
||||
`https://example.com/instances/` is forwarded to
|
||||
`http://localhost:8080/instances/`. It is possible to host the
|
||||
interface under a sub-path, in that case a request under the sub-path
|
||||
should be mapped to the master server with the sub-path stripped out.
|
||||
For example `https://example.com/sub/path/instances/` would be mapped to
|
||||
`http://localhost:8080/instances`.
|
||||
|
||||
Additianlly WebSocket connections to `/api/socket` also needs to be
|
||||
forwarded. Typically this requires explicit configuration and support
|
||||
for forwarding WebSocket connections. If the interface hosted under a
|
||||
sub-path the `/api/socket` address is relative to the sub-path and the
|
||||
sub-path also needs to be stripped out when forwarded.
|
||||
|
||||
You can also optionally set the master server to listen only on the
|
||||
loopback interface to prevent it being directly connected to from the
|
||||
outside.
|
||||
|
||||
npx clusteriomaster config set master.bind_address 127.0.0.1
|
||||
|
||||
|
||||
### Apache Config
|
||||
|
||||
To proxy with Apache make sure that `proxy_module`, `proxy_http_module`
|
||||
and `proxy_wstunnel_module` is loaded in the config, then add the
|
||||
following directives to wherever is the approriate place for your
|
||||
flavour of Apache config organization.
|
||||
|
||||
```apache
|
||||
<Location /sub/path/>
|
||||
ProxyPass "http://localhost:8080/"
|
||||
</location>
|
||||
<Location /sub/path/api/socket>
|
||||
ProxyPass "ws://localhost:8080/api/socket"
|
||||
</Location>
|
||||
```
|
||||
|
||||
Replace `/sub/path/` with the location on the proxy you want to host the
|
||||
Clusterio interface under. If the whole domain is to be dedicated to
|
||||
the Clusterio interface then `/sub/path/` should be just `/`. Pay
|
||||
attention to the use of trailing slashes, as they are important.
|
||||
|
||||
|
||||
Hosting HTTPS directly from Node.js
|
||||
-----------------------------------
|
||||
|
||||
Listening on HTTPS is supported by the Clusterio master server via the
|
||||
`master.https_port` option provided paths to a certificate file and a
|
||||
private key file is configured for the `master.tls_certificate` and
|
||||
`master.tls_private_key` options. Both of these are expected to be
|
||||
unencrypted PEM files.
|
||||
|
||||
|
||||
Creating and using a self-signed certificate
|
||||
--------------------------------------------
|
||||
|
||||
Create a self-signed certificate with OpenSSL by running
|
||||
|
||||
openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 3650 -subj /CN=localhost -addext "subjectAltName=IP:<ip>"
|
||||
|
||||
substituting `<ip>` with the IP address the server will be accessed
|
||||
under, for example `IP:203.0.113.4`. Multiple addresses can be given by
|
||||
supplying a comma sepparated list of `IP:address` pairs and DNS names
|
||||
can also be specified using the `DNS:example.com` format. You need to
|
||||
supply every name you want to connect to the server under the
|
||||
subjectAltName field, for example to connect to the HTTPS port locally
|
||||
via an address like `https://localhost:4443/` you need to add
|
||||
`DNS:localhost`.
|
||||
|
||||
This creates a private key file named `key.pem` and a public certificate
|
||||
file named `cert.pem` and these are used for the
|
||||
`master.tls_certificate` and `master.tls_private_key` config options
|
||||
respectively on the master server:
|
||||
|
||||
npx clusteriomaster config set master.tls_certificate cert.pem
|
||||
npx clusteriomaster config set master.tls_private_key key.pem
|
||||
|
||||
Additionally you will need copy the `cert.pem` file to all of the
|
||||
computers you want to set up as slaves and configure it as the
|
||||
`slave.tls_ca` option:
|
||||
|
||||
npx clusterioslave config set slave.tls_ca cert.pem
|
||||
|
||||
You will also need to copy the `cert.pem` file to all of the computers
|
||||
you want to remotely manage the cluster via clusterctl and configure it
|
||||
as `control.tls_ca` option:
|
||||
|
||||
npx clusterioctl control-config set control.tls_ca cert.pem
|
||||
+9
-2
@@ -549,8 +549,8 @@ debugCommands.add(new libCommand.Command({
|
||||
* @private
|
||||
*/
|
||||
class ControlConnector extends libLink.WebSocketClientConnector {
|
||||
constructor(url, reconnectDelay, token) {
|
||||
super(url, reconnectDelay);
|
||||
constructor(url, reconnectDelay, tlsCa, token) {
|
||||
super(url, reconnectDelay, tlsCa);
|
||||
this._token = token;
|
||||
}
|
||||
|
||||
@@ -765,9 +765,16 @@ async function startControl() {
|
||||
return;
|
||||
}
|
||||
|
||||
let tlsCa = null;
|
||||
let tlsCaPath = controlConfig.get("control.tls_ca");
|
||||
if (tlsCaPath) {
|
||||
tlsCa = await fs.readFile(tlsCaPath);
|
||||
}
|
||||
|
||||
let controlConnector = new ControlConnector(
|
||||
controlConfig.get("control.master_url"),
|
||||
controlConfig.get("control.reconnect_delay"),
|
||||
tlsCa,
|
||||
controlConfig.get("control.master_token")
|
||||
);
|
||||
let control = new Control(controlConnector, controlPlugins);
|
||||
|
||||
@@ -24,6 +24,7 @@ MasterGroup.define({
|
||||
description: "Port to listen for HTTP connections on, set to null to not listen for HTTP connections.",
|
||||
type: "number",
|
||||
optional: true,
|
||||
initial_value: 8080,
|
||||
});
|
||||
MasterGroup.define({
|
||||
name: "https_port",
|
||||
@@ -31,7 +32,6 @@ MasterGroup.define({
|
||||
description: "Port to listen for HTTPS connection on, set to null to not listen for HTTPS connections.",
|
||||
type: "number",
|
||||
optional: true,
|
||||
initial_value: 8443,
|
||||
});
|
||||
MasterGroup.define({
|
||||
name: "bind_address",
|
||||
@@ -53,7 +53,6 @@ MasterGroup.define({
|
||||
description: "Path to the certificate to use for HTTPS.",
|
||||
type: "string",
|
||||
optional: true,
|
||||
initial_value: "database/certificates/cert.crt",
|
||||
});
|
||||
MasterGroup.define({
|
||||
name: "tls_private_key",
|
||||
@@ -61,14 +60,6 @@ MasterGroup.define({
|
||||
description: "Path to the private key to use for HTTPS.",
|
||||
type: "string",
|
||||
optional: true,
|
||||
initial_value: "database/certificates/cert.key",
|
||||
});
|
||||
MasterGroup.define({
|
||||
name: "tls_bits",
|
||||
title: "TLS Bits",
|
||||
description: "Number of bits to use for auto generated TLS certificate.",
|
||||
type: "number",
|
||||
initial_value: 2048,
|
||||
});
|
||||
MasterGroup.define({
|
||||
name: "auth_secret",
|
||||
@@ -155,7 +146,7 @@ SlaveGroup.define({
|
||||
name: "master_url",
|
||||
description: "URL to connect to the master server at",
|
||||
type: "string",
|
||||
initial_value: "https://localhost:8443/",
|
||||
initial_value: "http://localhost:8080/",
|
||||
});
|
||||
SlaveGroup.define({
|
||||
name: "master_token",
|
||||
@@ -163,6 +154,12 @@ SlaveGroup.define({
|
||||
type: "string",
|
||||
initial_value: "enter token here",
|
||||
});
|
||||
SlaveGroup.define({
|
||||
name: "tls_ca",
|
||||
description: "Path to Certificate Authority to validate TLS connection to master against.",
|
||||
type: "string",
|
||||
optional: true,
|
||||
});
|
||||
SlaveGroup.define({
|
||||
name: "public_address",
|
||||
description: "Public facing address players should connect to in order to join instances on this slave",
|
||||
@@ -337,6 +334,12 @@ ControlGroup.define({
|
||||
type: "string",
|
||||
optional: true,
|
||||
});
|
||||
ControlGroup.define({
|
||||
name: "tls_ca",
|
||||
description: "Path to Certificate Authority to validate TLS connection to master against.",
|
||||
type: "string",
|
||||
optional: true,
|
||||
});
|
||||
ControlGroup.define({
|
||||
name: "reconnect_delay",
|
||||
title: "Reconnect Delay",
|
||||
|
||||
@@ -144,10 +144,11 @@ class WebSocketBaseConnector extends events.EventEmitter {
|
||||
* @memberof module:lib/link
|
||||
*/
|
||||
class WebSocketClientConnector extends WebSocketBaseConnector {
|
||||
constructor(url, reconnectDelay) {
|
||||
constructor(url, reconnectDelay, tlsCa = null) {
|
||||
super();
|
||||
|
||||
this._url = url;
|
||||
this._tlsCa = tlsCa;
|
||||
|
||||
// The following states are used in the client connector
|
||||
// new: Not connected
|
||||
@@ -243,11 +244,9 @@ class WebSocketClientConnector extends WebSocketBaseConnector {
|
||||
this._socket = new WebSocket(url);
|
||||
|
||||
} else {
|
||||
this._socket = new WebSocket(url, {
|
||||
// For now we do not verify TLS certificates since the default setup is
|
||||
// to create a self-signed certificate.
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
let options = {};
|
||||
if (this._tlsCa) { options.ca = this._tlsCa; }
|
||||
this._socket = new WebSocket(url, options);
|
||||
}
|
||||
|
||||
this._attachSocketHandlers();
|
||||
@@ -317,7 +316,19 @@ class WebSocketClientConnector extends WebSocketBaseConnector {
|
||||
|
||||
this._socket.onerror = event => {
|
||||
// It's assumed that close is always called by ws
|
||||
logger.error(`SOCKET | Error: ${event.message || "unknown error"}`);
|
||||
let code = !event.error ? "" : `, code: ${event.error.code}`;
|
||||
logger.error(`SOCKET | Error: ${event.message || "unknown error"}${code}`);
|
||||
if (event.error) {
|
||||
// Abort connection if certificate validation failed
|
||||
if ([
|
||||
"CERT_HAS_EXPIRED",
|
||||
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
||||
"ERR_TLS_CERT_ALTNAME_INVALID",
|
||||
].includes(event.error.code)) {
|
||||
this.emit("error", new libErrors.AuthenticationFailed(event.error.message));
|
||||
this._state = "closing";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this._socket.onopen = () => {
|
||||
|
||||
@@ -48,7 +48,6 @@ let db = {
|
||||
};
|
||||
|
||||
// homebrew modules
|
||||
const generateSslCert = require("./src/generate_ssl_cert");
|
||||
const HttpCloser = require("./src/HttpCloser");
|
||||
const routes = require("./src/routes");
|
||||
const libDatabase = require("@clusterio/lib/database");
|
||||
@@ -1955,14 +1954,11 @@ async function startServer() {
|
||||
|
||||
let tls_cert = masterConfig.get("master.tls_certificate");
|
||||
let tls_key = masterConfig.get("master.tls_private_key");
|
||||
// Create a self signed certificate if the certificate files doesn't exist
|
||||
if (httpsPort && !await fs.exists(tls_cert) && !await fs.exists(tls_key)) {
|
||||
await generateSslCert({
|
||||
bits: masterConfig.get("master.tls_bits"),
|
||||
sslCertPath: tls_cert,
|
||||
sslPrivKeyPath: tls_key,
|
||||
doLogging: true,
|
||||
});
|
||||
|
||||
if (httpsPort && (!tls_cert || !tls_key)) {
|
||||
throw new libErrors.StartupError(
|
||||
"tls_certificate and tls_private_key must be configure in order to use https_port"
|
||||
);
|
||||
}
|
||||
|
||||
// Add routes for the web interface
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
"use strict";
|
||||
const forge = require("node-forge");
|
||||
const fs = require("fs-extra");
|
||||
const util = require("util");
|
||||
|
||||
const { logger } = require("@clusterio/lib/logging");
|
||||
|
||||
const hrtime = process.hrtime.bigint;
|
||||
|
||||
|
||||
module.exports = async function({
|
||||
bits = 2048,
|
||||
sslCertPath,
|
||||
sslPrivKeyPath,
|
||||
} = {}){
|
||||
logger.info(`Generating SSL certificate with ${bits} bits at ${sslCertPath}`);
|
||||
|
||||
let startNs = hrtime();
|
||||
let keypair = await util.promisify(forge.pki.rsa.generateKeyPair)({ bits: bits, e: 0x10001 });
|
||||
logger.info(`Generated ${bits}bit keypair in: ${Number(hrtime()-startNs) / 1e6} ms`);
|
||||
|
||||
startNs = hrtime();
|
||||
let cert = forge.pki.createCertificate();
|
||||
cert.publicKey = keypair.publicKey;
|
||||
cert.serialNumber = "01";
|
||||
|
||||
cert.validity.notBefore = new Date();
|
||||
cert.validity.notAfter = new Date(Date.now()+(1000*60*60*24*365)); // valid for one year
|
||||
|
||||
// XXX This should probably be limited to the actual hostname
|
||||
let attrs = [{ name: "commonName", value: "*" }];
|
||||
cert.setSubject(attrs);
|
||||
cert.setIssuer(attrs);
|
||||
|
||||
// self-sign certificate
|
||||
cert.sign(keypair.privateKey);
|
||||
|
||||
// Write certificate and private key to pem files
|
||||
await fs.outputFile(sslPrivKeyPath, forge.pki.privateKeyToPem(keypair.privateKey));
|
||||
await fs.outputFile(sslCertPath, forge.pki.certificateToPem(cert));
|
||||
|
||||
logger.info(`Generated certificate in: ${Number(hrtime()-startNs) / 1e6} ms`);
|
||||
};
|
||||
+13
-3
@@ -890,8 +890,12 @@ class InstanceConnection extends libLink.Link {
|
||||
}
|
||||
|
||||
class SlaveConnector extends libLink.WebSocketClientConnector {
|
||||
constructor(slaveConfig, pluginInfos) {
|
||||
super(slaveConfig.get("slave.master_url"), slaveConfig.get("slave.reconnect_delay"));
|
||||
constructor(slaveConfig, tlsCa, pluginInfos) {
|
||||
super(
|
||||
slaveConfig.get("slave.master_url"),
|
||||
slaveConfig.get("slave.reconnect_delay"),
|
||||
tlsCa
|
||||
);
|
||||
this.slaveConfig = slaveConfig;
|
||||
this.pluginInfos = pluginInfos;
|
||||
}
|
||||
@@ -1593,7 +1597,13 @@ async function startSlave() {
|
||||
return;
|
||||
}
|
||||
|
||||
let slaveConnector = new SlaveConnector(slaveConfig, pluginInfos);
|
||||
let tlsCa = null;
|
||||
let tlsCaPath = slaveConfig.get("slave.tls_ca");
|
||||
if (tlsCaPath) {
|
||||
tlsCa = await fs.readFile(tlsCaPath);
|
||||
}
|
||||
|
||||
let slaveConnector = new SlaveConnector(slaveConfig, tlsCa, pluginInfos);
|
||||
let slave = new Slave(slaveConnector, slaveConfig, pluginInfos);
|
||||
|
||||
// Handle interrupts
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFJTCCAw2gAwIBAgIUIGyavTA8+spUJ/Tb3gHmTnwXOI0wDQYJKoZIhvcNAQEL
|
||||
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIxMDEwMzIwNTU1MFoXDTMxMDEw
|
||||
MTIwNTU1MFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAg8AMIICCgKCAgEApHfLDSqc2+kdAW8LF8Z1z68VriWEJ+wNkS7rCm8AICe/
|
||||
vWYfywWDv4Kw0vzPgoGe7wmURYSS7r5i7MhFzqH4bf0B6wCIFWw2VwFqhgB94hWA
|
||||
ellyIYC5ntV8RBT1RYbqSsBEJR5HKA/hFdiAf5gSIiZNnjtNT5JJLROlufD30IUQ
|
||||
7teHyrbCF7/JYeVFrdisRBZg6XgMvHX6sXR4BaLkLTl6YBEe+vWkkOd9lN4pnTzG
|
||||
PXSxdg4sG4QbCWsANv4bJnWuPC4rd3a/AKqonXumtBErFqluv3ylM6VY8R5E6DrQ
|
||||
Wk7wjLphUXPsi8gkMAO4SY4/+CI26jFbk/5HI2baQ90jm8AOoUG6erEu5OU3a7dR
|
||||
9uxIqebd9+S9FCxU9dstYiMbRcmG699Wi/mF/taWna1CdOqw7l8WRXp9q0CTqaVe
|
||||
a9HdhYiSsimhOttNBD02seyKUQtUw8bw3TUeZofglqcjiP06DGH959z25NjMkiC6
|
||||
b745QZ3LfzbPpSP3Ax9heHlti0Pl5LnCOIIdyk3DPDz1/xshDTXS2d18k+wpW0O0
|
||||
rqOydCwzMTJjsHYYpOcqC93qfHuX/NEqNLbnOfM8SEZWqaM6ueW7tAoQc+BaHA8i
|
||||
AWtgtMhFuaDrHifeIIm8FLXdyXQChL8WhpeyZa3GeSnUdCVzl+2gjSQZpaGO/fEC
|
||||
AwEAAaNvMG0wHQYDVR0OBBYEFDi1Q63Txpc4NAbFf1HXlWgm+vlJMB8GA1UdIwQY
|
||||
MBaAFDi1Q63Txpc4NAbFf1HXlWgm+vlJMA8GA1UdEwEB/wQFMAMBAf8wGgYDVR0R
|
||||
BBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4ICAQAuOwG6NFMh
|
||||
VI7ipUVsXSOBX+qTd5FnOCCTyV5UixgcDAnEZ87poH0ehI7IOia4xnhUXQsJTTaq
|
||||
ho/S2hXeRuyq5Sn0RP3Zqa5IzULJWr9hQ5ajc4VuYxzhsh72gzWTJRKZRZBIL5jt
|
||||
VrkS6qFG4QY4i8IAzfwDiJLhl21iEv23idbBE+yUwJNFnnuMxyllOo7fMFwB3Wij
|
||||
maEaAamLd2Ocv38bstimCqtKVwg5sWMdaeELzyPtY0HnCNr1ILTW4+wMjgKwUE5z
|
||||
rnwgF2d03eV2xaNY8asl5mMzJbKpz5CfD6r8KAZ2ZQM8QN+demmexZU8tMp0X3Hz
|
||||
nVlBu48Ecuv5XTU1F8S2a0UoRoadIw2AtydFW6V31XtAivwrhIsz8xVkE9XiJ8c0
|
||||
QpMw7bPPeQieFiSAUnRThd/5qdzeep7Vu/CtTht85rT7aqTPdPHiWUdL6x7M1Mi8
|
||||
vatHGI0cvhAMXFNKAuvCFknEhUCwAKwIHfoWGsa08Kn4LhACLOd67tA45+Rdz1m5
|
||||
w6Ea1zO8bfhUr/3qxod+nr9kAuN5pN3JELzIwIYGuEsp7OGWr0Wc2QRKMlDcTTIU
|
||||
Xmd29Vcl5F3OwbBUmoJIqUKsENKJ0jG0DuW1svBnpRcs5wVQzPCDV/fPm3pe0c2c
|
||||
KFYDk2Qa1jY9BZVyVEFJdwfVlh5iZwu0Iw==
|
||||
-----END CERTIFICATE-----
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/bash
|
||||
openssl req -x509 -newkey rsa:4096 -keyout key.pem -nodes -out cert.pem -days 3650 -subj /CN=localhost -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
|
||||
@@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
lEgQcwoqtWDAeenVEKdo2GiojTqk9g==
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -134,9 +134,10 @@ before(async function() {
|
||||
await exec("node ../../packages/master config set master.auth_secret TestSecretDoNotUse");
|
||||
await exec("node ../../packages/master config set master.http_port 8880");
|
||||
await exec("node ../../packages/master config set master.https_port 4443");
|
||||
await exec("node ../../packages/master config set master.tls_bits 1024");
|
||||
await exec("node ../../packages/master config set master.heartbeat_interval 0.25");
|
||||
await exec("node ../../packages/master config set master.connector_shutdown_timeout 2");
|
||||
await exec("node ../../packages/master config set master.tls_certificate ../../test/file/tls/cert.pem");
|
||||
await exec("node ../../packages/master config set master.tls_private_key ../../test/file/tls/key.pem");
|
||||
|
||||
await exec("node ../../packages/ctl plugin add ../../plugins/global_chat");
|
||||
await exec("node ../../packages/ctl plugin add ../../plugins/research_sync");
|
||||
@@ -145,16 +146,18 @@ before(async function() {
|
||||
|
||||
await exec("node ../../packages/master bootstrap create-admin test");
|
||||
await exec("node ../../packages/master bootstrap create-ctl-config test");
|
||||
await exec("node ../../packages/ctl control-config set control.tls_ca ../../test/file/tls/cert.pem");
|
||||
|
||||
masterProcess = await spawn("master:", "node ../../packages/master run", /All plugins loaded/);
|
||||
|
||||
let createArgs = "--id 4 --name slave --generate-token";
|
||||
await execCtl(`slave create-config ${createArgs}`);
|
||||
|
||||
await execCtl("slave create-config --id 4 --name slave --generate-token");
|
||||
await exec(`node ../../packages/slave config set slave.factorio_directory ${path.join("..", "..", "factorio")}`);
|
||||
await exec("node ../../packages/slave config set slave.tls_ca ../../test/file/tls/cert.pem");
|
||||
|
||||
slaveProcess = await spawn("slave:", "node ../../packages/slave run", /SOCKET \| registering slave/);
|
||||
|
||||
let controlConnector = new TestControlConnector(url, 2);
|
||||
let tlsCa = await fs.readFile("test/file/tls/cert.pem");
|
||||
let controlConnector = new TestControlConnector(url, 2, tlsCa);
|
||||
controlConnector.token = controlToken;
|
||||
control = new TestControl(controlConnector);
|
||||
await controlConnector.connect();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use strict";
|
||||
const assert = require("assert").strict;
|
||||
const events = require("events");
|
||||
const fs = require("fs-extra");
|
||||
const jwt = require("jsonwebtoken");
|
||||
|
||||
const { TestControlConnector, TestControl, get, exec, url } = require("./index");
|
||||
@@ -8,10 +9,14 @@ const { TestControlConnector, TestControl, get, exec, url } = require("./index")
|
||||
let token = jwt.sign({ aud: "user", user: "test" }, "TestSecretDoNotUse");
|
||||
|
||||
describe("Integration of lib/link", function() {
|
||||
let tlsCa;
|
||||
let control;
|
||||
let controlConnector;
|
||||
before(async function() {
|
||||
tlsCa = await fs.readFile("test/file/tls/cert.pem");
|
||||
});
|
||||
it("should emitt an error if authentication failed", async function() {
|
||||
controlConnector = new TestControlConnector(url, 0.2);
|
||||
controlConnector = new TestControlConnector(url, 0.2, tlsCa);
|
||||
controlConnector.token = "gibberish";
|
||||
control = new TestControl(controlConnector);
|
||||
await assert.rejects(
|
||||
@@ -21,7 +26,7 @@ describe("Integration of lib/link", function() {
|
||||
});
|
||||
|
||||
it("should connect with proper credentials", async function() {
|
||||
controlConnector = new TestControlConnector(url, 0.2);
|
||||
controlConnector = new TestControlConnector(url, 0.2, tlsCa);
|
||||
controlConnector.token = token;
|
||||
control = new TestControl(controlConnector);
|
||||
await controlConnector.connect();
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
"use strict";
|
||||
const assert = require("assert");
|
||||
const fs = require("fs-extra");
|
||||
const path = require("path");
|
||||
|
||||
const generateSslCert = require("@clusterio/master/src/generate_ssl_cert");
|
||||
|
||||
describe("generate_ssl_cert", function() {
|
||||
let testDir = path.join("temp", "test", "certTest");
|
||||
before(async function() {
|
||||
await fs.remove(testDir);
|
||||
});
|
||||
|
||||
it("Creates a folder with a .crt and .key file in it", async function() {
|
||||
this.timeout(1000);
|
||||
// eslint-disable-next-line no-process-env
|
||||
if (process.env.FAST_TEST) {
|
||||
this.skip();
|
||||
}
|
||||
|
||||
let certPath = path.join(testDir, "cert.crt");
|
||||
let privKeyPath = path.join(testDir, "cert.key");
|
||||
|
||||
await generateSslCert({
|
||||
bits: 512, // This is too small for real world usage, but faster to test
|
||||
sslCertPath: certPath,
|
||||
sslPrivKeyPath: privKeyPath,
|
||||
});
|
||||
|
||||
assert(await fs.exists(certPath), "cert file was not created");
|
||||
assert(await fs.exists(privKeyPath), "key file was not created");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user