Sync with master
This commit is contained in:
12
.gitignore
vendored
12
.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
# Don't check in SSH keys!
|
||||
*.pem
|
||||
|
||||
# Various directories
|
||||
# Exclude User-Customized Directories
|
||||
config/
|
||||
db/
|
||||
drop/
|
||||
@@ -11,5 +11,13 @@ mail/
|
||||
node_modules/
|
||||
docs/_site/
|
||||
docs/.sass-cache/
|
||||
|
||||
docs/.jekyll-cache/
|
||||
|
||||
# Ignore Web Assets not included with enigma-bbs
|
||||
www/*
|
||||
www/assets/*
|
||||
!www/otp_register_template.html
|
||||
!www/reset_password.template.html
|
||||
|
||||
# Runtime Environment
|
||||
.venv
|
||||
|
||||
32
README.md
32
README.md
@@ -8,7 +8,7 @@ ENiGMA½ is a modern BBS software with a nostalgic flair!
|
||||
Below are just some of the features ENiGMA½ supports out of the box:
|
||||
* **Multi platform** — Anywhere [Node.js](https://nodejs.org/) runs likely works (known to work under Linux, FreeBSD, OpenBSD, OS X and Windows)
|
||||
* Unlimited multi node support (for all those BBS "callers"!)
|
||||
* **Highly customizable** via [HJSON](http://hjson.org/) based configuration, menus, and themes in addition to JavaScript based [mods](./docs/_docs/modding/existing-mods.md)
|
||||
* **Highly customizable** via [HJSON](https://hjson.github.io/) based configuration, menus, and themes in addition to JavaScript based [mods](./docs/_docs/modding/existing-mods.md)
|
||||
* [MCI support](./docs/_docs/art/mci.md) for lightbars, toggles, input areas, and so on plus many other other bells and whistles
|
||||
* Telnet, **SSH**, and both secure and non-secure [WebSocket](https://en.wikipedia.org/wiki/WebSocket) access built in! Additional servers are easy to implement
|
||||
* [CP437](http://www.ascii-codes.com/) and UTF-8 output
|
||||
@@ -87,31 +87,9 @@ ENiGMA has been tested with many terminals. However, the following are suggested
|
||||
* Alpha for the [FTN-style configuration guide](https://medium.com/@alpha_11845/setting-up-ftn-style-message-networks-with-enigma%C2%BD-bbs-709b22a1ae0d)!
|
||||
* Huge shout out to [cognitivegears ](https://github.com/cognitivegears) for the various fixes, improvements, and **removing the need for cursor position reports** providing a much better terminal experience!
|
||||
|
||||
...and so many others! This project would be nothing without the BBS and artscene communities!
|
||||
...and so many others! This project would be nothing without the BBS and art scene communities!
|
||||
|
||||
[](https://star-history.com/#nuskooler/enigma-bbs&Date)
|
||||
|
||||
## License
|
||||
Released under the [BSD 2-clause](https://opensource.org/licenses/BSD-2-Clause) license:
|
||||
|
||||
Copyright (c) 2015-2024, Bryan D. Ashby
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Released under [BSD 2-clause](https://opensource.org/licenses/BSD-2-Clause). See [LICENSE.TXT](LICENSE.TXT)
|
||||
93
autoexec.sh
Executable file
93
autoexec.sh
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Environment
|
||||
ENIGMA_INSTALL_DIR=${ENIGMA_INSTALL_DIR:=$HOME/enigma-bbs}
|
||||
AUTOEXEC_LOGFILE="$ENIGMA_INSTALL_DIR/logs/autoexec.log"
|
||||
TIME_FORMAT=`date "+%Y-%m-%d %H:%M:%S"`
|
||||
|
||||
# Mise en place
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
export PATH="$HOME/.local/share/mise/shims:$PATH"
|
||||
export PATH="$HOME/.local/share/mise/installs/python/latest/bin:$PATH"
|
||||
|
||||
# Environment Versions
|
||||
ENIGMA_NODE_VERSION=${ENIGMA_NODE_VERSION:=$(toml get --toml-path=$ENIGMA_INSTALL_DIR/mise.toml tools.node)}
|
||||
ENIGMA_PYTHON_VERSION=${ENIGMA_PYTHON_VERSION:=$(toml get --toml-path=$ENIGMA_INSTALL_DIR/mise.toml tools.python)}
|
||||
|
||||
# Validate Environment
|
||||
DEPENDENCIES_VALIDATED=1
|
||||
|
||||
# Shared Functions
|
||||
log() {
|
||||
echo "${TIME_FORMAT} " "$*" >> $AUTOEXEC_LOGFILE
|
||||
}
|
||||
|
||||
# If this is a first run, the log path will not yet exist and must be created
|
||||
if [ ! -d "$ENIGMA_INSTALL_DIR/logs" ]
|
||||
then
|
||||
mkdir -p $ENIGMA_INSTALL_DIR/logs
|
||||
fi
|
||||
|
||||
log "START:"
|
||||
log "- PATH: $PATH"
|
||||
log "- CURRENT DIR: ${PWD##}"
|
||||
|
||||
if ! command -v "mise" 2>&1 >/dev/null
|
||||
then
|
||||
log "mise is not in your PATH"
|
||||
log "ERROR END"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v "node" 2>&1 >/dev/null
|
||||
then
|
||||
log "Node environment is not in your PATH"
|
||||
log "ERROR END"
|
||||
exit 1
|
||||
else
|
||||
NODE_VERSION=$(node --version | tee /dev/null)
|
||||
log "- NODE VERSION: $NODE_VERSION"
|
||||
if [[ $NODE_VERSION != "v$ENIGMA_NODE_VERSION."* ]]; then
|
||||
log "Node version found in your PATH is $NODE_VERSION, was expecting v$ENIGMA_NODE_VERSION.*; you may encounter compatibility issues"
|
||||
DEPENDENCIES_VALIDATED=0
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! command -v "python" 2>&1 >/dev/null
|
||||
then
|
||||
log "Python environment is not in your PATH"
|
||||
log "ERROR END"
|
||||
exit 1
|
||||
else
|
||||
PYTHON_VERSION=$(python --version | tee /dev/null)
|
||||
log "- PYTHON VERSION: $PYTHON_VERSION"
|
||||
if [[ $PYTHON_VERSION != "Python $ENIGMA_PYTHON_VERSION"* ]]; then
|
||||
log "Python version found in your PATH is $PYTHON_VERSION, was expecting Python $ENIGMA_PYTHON_VERSION.*; you may encounter compatibility issues"
|
||||
DEPENDENCIES_VALIDATED=0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Validate whether we are good to Start
|
||||
if [ "$DEPENDENCIES_VALIDATED" == "0" ]; then
|
||||
if [ -v ENIGMA_IGNORE_DEPENDENCIES ] && [ "${ENIGMA_IGNORE_DEPENDENCIES}" == "1" ]; then
|
||||
log "ENIGMA_IGNORE_DEPENDENCIES=1 detected, starting up..."
|
||||
else
|
||||
log "NOTE: Please re-run with 'ENIGMA_IGNORE_DEPENDENCIES=1 /path/to/autoexec.sh' to force startup"
|
||||
log "ERROR END"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start BBS
|
||||
log "Starting ENiGMA½"
|
||||
~/enigma-bbs/main.js
|
||||
result=$?
|
||||
|
||||
# Determine whether a Startup Crash Occurred
|
||||
# if [ $result -eq 0 ]; then
|
||||
# # TODO: Notify via SMS / Email of Startup Failure
|
||||
# fi
|
||||
|
||||
log "ENiGMA½ exited with $result"
|
||||
log "END"
|
||||
exit $result
|
||||
29
core/bbs.js
29
core/bbs.js
@@ -85,23 +85,18 @@ function main() {
|
||||
// then it's a fatal error
|
||||
//
|
||||
if (err) {
|
||||
errorDisplayed = true;
|
||||
console.error(`Configuration error: ${err.message}`); // eslint-disable-line no-console
|
||||
|
||||
if ('ENOENT' === err.code) {
|
||||
if (configPathSupplied) {
|
||||
console.error(
|
||||
'Configuration file does not exist: ' + configFile
|
||||
);
|
||||
} else {
|
||||
configPathSupplied = null; // make non-fatal; we'll go with defaults
|
||||
}
|
||||
} else {
|
||||
errorDisplayed = true;
|
||||
console.error(`Configuration error: ${err.message}`); // eslint-disable-line no-console
|
||||
if (err.hint) {
|
||||
console.error(`Hint: ${err.hint}`);
|
||||
}
|
||||
if (err.configPath) {
|
||||
console.error(`Note: ${err.configPath}`);
|
||||
}
|
||||
console.error("\nConfiguration file does not exist: '{configFile}'\n\nIf this is a new installation please run './oputil.js config new' from the enigma-bbs directory");
|
||||
}
|
||||
|
||||
if (err.hint) {
|
||||
console.error(`Hint: ${err.hint}`);
|
||||
}
|
||||
if (err.configPath) {
|
||||
console.error(`Note: ${err.configPath}`);
|
||||
}
|
||||
}
|
||||
return callback(err);
|
||||
@@ -134,7 +129,7 @@ function main() {
|
||||
|
||||
if (err && !errorDisplayed) {
|
||||
console.error('Error initializing: ' + util.inspect(err));
|
||||
return process.exit();
|
||||
return process.exit(1);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -25,8 +25,8 @@ exports.getModule = class GoldmineModule extends MenuModule {
|
||||
this.setConfigWithExtraArgs(options);
|
||||
|
||||
// http://goldminebbs.com/
|
||||
this.config.host = this.config.host || '165.232.153.209';
|
||||
this.config.rloginPort = this.config.rloginPort || 513;
|
||||
this.config.host = this.config.host || 'goldminedoors.com';
|
||||
this.config.rloginPort = this.config.rloginPort || 2513;
|
||||
}
|
||||
|
||||
initSequence() {
|
||||
|
||||
@@ -297,6 +297,14 @@ function catCurrentConfig() {
|
||||
keepWsc: false === argv.comments ? false : true,
|
||||
});
|
||||
|
||||
if (argv.meow) {
|
||||
console.info(
|
||||
` /\\_/\\
|
||||
( o.o )
|
||||
> ^ < ... mrow...`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(hjson.stringify(config, hjsonOpts));
|
||||
} catch (e) {
|
||||
if ('ENOENT' == e.code) {
|
||||
|
||||
@@ -20,6 +20,7 @@ Commands:
|
||||
config Configuration management
|
||||
fb File base management
|
||||
mb Message base management
|
||||
ssh SSH key management
|
||||
`,
|
||||
User: `usage: oputil.js user <action> [<arguments>]
|
||||
|
||||
@@ -101,7 +102,7 @@ Actions:
|
||||
cat Write current configuration to stdout
|
||||
|
||||
cat arguments:
|
||||
--no-color Disable color
|
||||
--no-colors Disable color
|
||||
--no-comments Strip any comments
|
||||
`,
|
||||
FileBase: `usage: oputil.js fb <action> [<arguments>]
|
||||
@@ -219,6 +220,11 @@ qwk-export arguments:
|
||||
TIMESTAMP.
|
||||
--no-qwke Disable QWKE extensions.
|
||||
--no-synchronet Disable Synchronet style extensions.
|
||||
`,
|
||||
SSH: `usage: oputil.js ssh <action>
|
||||
|
||||
Actions:
|
||||
create Create new SSH Keys
|
||||
`,
|
||||
});
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ const handleFileBaseCommand = require('./oputil_file_base.js').handleFileBaseCom
|
||||
const handleMessageBaseCommand =
|
||||
require('./oputil_message_base.js').handleMessageBaseCommand;
|
||||
const handleConfigCommand = require('./oputil_config.js').handleConfigCommand;
|
||||
const handleSSHKeyCommand = require('./oputil_ssh_key.js').handleSSHKeyCommand;
|
||||
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||
|
||||
module.exports = function () {
|
||||
@@ -32,6 +33,8 @@ module.exports = function () {
|
||||
return handleFileBaseCommand();
|
||||
case 'mb':
|
||||
return handleMessageBaseCommand();
|
||||
case 'ssh':
|
||||
return handleSSHKeyCommand();
|
||||
default:
|
||||
return printUsageAndSetExitCode(getHelpFor('General'), ExitCodes.BAD_COMMAND);
|
||||
}
|
||||
|
||||
137
core/oputil/oputil_ssh_key.js
Normal file
137
core/oputil/oputil_ssh_key.js
Normal file
@@ -0,0 +1,137 @@
|
||||
/* jslint node: true */
|
||||
/* eslint-disable no-console */
|
||||
'use strict';
|
||||
|
||||
// ENiGMA½
|
||||
const initConfigAndDatabases = require('./oputil_common.js').initConfigAndDatabases;
|
||||
|
||||
const {
|
||||
printUsageAndSetExitCode,
|
||||
argv,
|
||||
ExitCodes,
|
||||
getAnswers,
|
||||
} = require('./oputil_common.js');
|
||||
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||
|
||||
// deps
|
||||
const async = require('async');
|
||||
const fs = require('fs-extra');
|
||||
const exec = require('child_process').exec;
|
||||
const inq = require('inquirer');
|
||||
const _ = require('lodash');
|
||||
|
||||
|
||||
exports.handleSSHKeyCommand = handleSSHKeyCommand;
|
||||
|
||||
const ConfigIncludeKeys = [
|
||||
'loginServers.ssh',
|
||||
'loginServers.ssh.privateKeyPem',
|
||||
];
|
||||
|
||||
const MINIMUM_PASSWORD_LENGTH = 8;
|
||||
const QUESTIONS = {
|
||||
Create: [
|
||||
{
|
||||
name: 'createNew',
|
||||
message: 'Generate New SSH Keys?',
|
||||
type: 'confirm',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
name: 'password',
|
||||
message: 'SSL Password:',
|
||||
default: "",
|
||||
when: answers => answers.createNew,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function execute(ui, command) {
|
||||
exec(
|
||||
command,
|
||||
function (error, stdout, stderr) {
|
||||
ui.log.write(error);
|
||||
|
||||
if (error) {
|
||||
const reason = error ? error.message : 'OpenSSL Error';
|
||||
logDebug(
|
||||
{
|
||||
reason: reason,
|
||||
cmd: util.cmd,
|
||||
args: args
|
||||
},
|
||||
`openssl command failed`
|
||||
);
|
||||
}
|
||||
else {
|
||||
ui.log.write("SSH Keys Generated")
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function createNew(cb) {
|
||||
const ui = new inq.ui.BottomBar();
|
||||
|
||||
async.waterfall(
|
||||
[
|
||||
function init(callback) {
|
||||
return initConfigAndDatabases(callback);
|
||||
},
|
||||
function create(configuration, callback) {
|
||||
getAnswers(QUESTIONS.Create, answers => {
|
||||
if (!answers.createNew) {
|
||||
return callback('exit');
|
||||
}
|
||||
|
||||
// Get Answer Value
|
||||
const sslPassword = answers.password.trim();
|
||||
if (!sslPassword || sslPassword == "") {
|
||||
ui.log.write('Password must be set.');
|
||||
|
||||
return callback('exit');
|
||||
}
|
||||
if (sslPassword.length < MINIMUM_PASSWORD_LENGTH) {
|
||||
ui.log.write(`Password must be at least ${MINIMUM_PASSWORD_LENGTH} characters.`);
|
||||
|
||||
return callback('exit');
|
||||
}
|
||||
|
||||
// Check if Keyfiles Exist
|
||||
const sshKeyPath = "config/security/";
|
||||
const sshKeyFilename = "ssh_private_key.pem";
|
||||
const targetKeyFile = sshKeyPath + sshKeyFilename;
|
||||
|
||||
ui.log.write(`Creating SSH Key: ${targetKeyFile}`);
|
||||
|
||||
// Create Dir
|
||||
ui.log.write(`Creating Directory: ${sshKeyPath}`);
|
||||
fs.ensureDirSync(sshKeyPath);
|
||||
|
||||
// Create SSH Keys
|
||||
const command = `openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 | openssl rsa -out ./${targetKeyFile} -aes128 -traditional -passout pass:`;
|
||||
execute(ui, `${command}${sslPassword}`);
|
||||
});
|
||||
},
|
||||
],
|
||||
err => {
|
||||
return cb(err, configPath, config);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function handleSSHKeyCommand() {
|
||||
if (true === argv.help) {
|
||||
return printUsageAndSetExitCode(getHelpFor('SSH'), ExitCodes.ERROR);
|
||||
}
|
||||
|
||||
const action = argv._[1];
|
||||
|
||||
switch (action) {
|
||||
case 'create':
|
||||
return createNew();
|
||||
|
||||
default:
|
||||
return printUsageAndSetExitCode(getHelpFor('SSH'), ExitCodes.ERROR);
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,25 @@ class WebSocketClient extends TelnetClient {
|
||||
})(ws);
|
||||
|
||||
super(wsDuplex);
|
||||
|
||||
Log.trace({ headers: req.headers }, 'WebSocket connection headers');
|
||||
|
||||
//
|
||||
// If the config allows it, look for 'x-forwarded-proto' as "https"
|
||||
// to override |isSecure|
|
||||
//
|
||||
if (
|
||||
true === _.get(Config(), 'loginServers.webSocket.proxied') &&
|
||||
'https' === req.headers['x-forwarded-proto']
|
||||
) {
|
||||
Log.debug(
|
||||
`Assuming secure connection due to X-Forwarded-Proto of "${req.headers['x-forwarded-proto']}"`
|
||||
);
|
||||
this.proxied = true;
|
||||
} else {
|
||||
this.proxied = false;
|
||||
}
|
||||
|
||||
wsDuplex.setClient(this, req);
|
||||
|
||||
// fudge remoteAddress on socket, which is now TelnetSocket
|
||||
@@ -91,24 +110,6 @@ class WebSocketClient extends TelnetClient {
|
||||
ws.isConnectionAlive = true;
|
||||
});
|
||||
|
||||
Log.trace({ headers: req.headers }, 'WebSocket connection headers');
|
||||
|
||||
//
|
||||
// If the config allows it, look for 'x-forwarded-proto' as "https"
|
||||
// to override |isSecure|
|
||||
//
|
||||
if (
|
||||
true === _.get(Config(), 'loginServers.webSocket.proxied') &&
|
||||
'https' === req.headers['x-forwarded-proto']
|
||||
) {
|
||||
Log.debug(
|
||||
`Assuming secure connection due to X-Forwarded-Proto of "${req.headers['x-forwarded-proto']}"`
|
||||
);
|
||||
this.proxied = true;
|
||||
} else {
|
||||
this.proxied = false;
|
||||
}
|
||||
|
||||
// start handshake process
|
||||
this.banner();
|
||||
}
|
||||
|
||||
@@ -77,18 +77,18 @@ GEM
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
mercenary (0.4.0)
|
||||
minitest (5.19.0)
|
||||
nokogiri (1.15.4-aarch64-linux)
|
||||
nokogiri (1.16.5-aarch64-linux)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.15.4-x86_64-linux)
|
||||
nokogiri (1.16.5-x86_64-linux)
|
||||
racc (~> 1.4)
|
||||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (5.0.3)
|
||||
racc (1.7.1)
|
||||
racc (1.8.1)
|
||||
rb-fsevent (0.11.2)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rexml (3.2.6)
|
||||
rexml (3.3.9)
|
||||
rouge (3.30.0)
|
||||
safe_yaml (1.0.5)
|
||||
sassc (2.4.0)
|
||||
|
||||
@@ -78,22 +78,24 @@ webserver, and unpack it to a temporary directory.
|
||||
|
||||
````javascript
|
||||
var vtxdata = {
|
||||
sysName: "Your Awesome BBS",
|
||||
wsConnect: "wss://your-hostname.here:8811",
|
||||
term: "ansi-bbs",
|
||||
codePage: "CP437",
|
||||
fontName: "UVGA16",
|
||||
fontSize: "24px",
|
||||
crtCols: 80,
|
||||
crtRows: 25,
|
||||
crtHistory: 500,
|
||||
xScale: 1,
|
||||
initStr: "",
|
||||
defPageAttr: 0x1010,
|
||||
defCrsrAttr: ['thick', 'horizontal'],
|
||||
defCellAttr: 0x0007,
|
||||
telnet: 1,
|
||||
autoConnect: 0
|
||||
sysName: "Your Awesome BBS",
|
||||
wsConnect: "wss://your-hostname.here:8811",
|
||||
term: "ansi-bbs",
|
||||
codePage: "CP437",
|
||||
fontName: "UVGA16",
|
||||
fontSize: "24px",
|
||||
crtCols: 80,
|
||||
crtRows: 25,
|
||||
crtHistory: 500,
|
||||
xScale: 1,
|
||||
initStr: "",
|
||||
defPageAttr: 0x1010,
|
||||
defCrsrAttr: ['thick', 'horizontal'],
|
||||
defCellAttr: 0x0007,
|
||||
telnet: 1,
|
||||
autoConnect: 0,
|
||||
wsProtocol: 'telnet',
|
||||
wsDataType: 'arraybuffer',
|
||||
};
|
||||
````
|
||||
|
||||
|
||||
@@ -20,7 +20,25 @@ Several things can cause this:
|
||||
1. `ssh_private_key.pem` was installed to the wrong location. Make sure that it is in the `config/security` directory and has the name matching the error message. You can also change your `config.hjson` if you prefer to point to the location of the key file.
|
||||
2. `ssh_private_key.pem` has the wrong file permissions. Verify that the file will be readable by the user that the BBS is running as. Because it is a cryptographic key however, we do recommend that access is restricted only to that user.
|
||||
|
||||
## Error With Netrunner
|
||||
## Cannot parse privateKey: Unsupported key format
|
||||
|
||||
***Symptom:***
|
||||
BBS not starting with an error similar to the following:
|
||||
|
||||
```shell
|
||||
Error initializing: Error: Cannot parse privateKey: Unsupported key format
|
||||
```
|
||||
|
||||
***Solution:***
|
||||
Depending on the OpenSSL version, a specific key format is used to generate the private key. The error above is observed on Debian 12 (Bookworm), but might present itself on other Debian 12 derivatives.
|
||||
|
||||
Convert the unsupported key to a supported one:
|
||||
|
||||
```shell
|
||||
openssl rsa -in unsupported.key -out converted.key -traditional
|
||||
```
|
||||
|
||||
## Errors With Netrunner
|
||||
|
||||
***Symptom:***
|
||||
Some ssh clients connect, but Netrunner (and other older clients) get a connection failed message and the following is in the log:
|
||||
|
||||
@@ -157,7 +157,15 @@
|
||||
//
|
||||
// 3 - Finally, set 'enabled' to 'true'
|
||||
//
|
||||
// ! - If you receive the following error when starting ENiGMA:
|
||||
// "Error initializing: Error: Cannot parse privateKey: Unsupported key format"
|
||||
// You might need to convert your key. This can be done as follows:
|
||||
//
|
||||
// > openssl rsa -in unsupported.key -out converted.key -traditional
|
||||
//
|
||||
//
|
||||
// Additional reading:
|
||||
// - https://nuskooler.github.io/enigma-bbs/troubleshooting/ssh-troubleshooting.html
|
||||
// - https://blog.sleeplessbeastie.eu/2017/12/28/how-to-generate-private-key/
|
||||
// - https://gist.github.com/briansmith/2ee42439923d8e65a266994d0f70180b
|
||||
//
|
||||
|
||||
237
misc/install.sh
237
misc/install.sh
@@ -1,16 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
{ # this ensures the entire script is downloaded before execution
|
||||
|
||||
ENIGMA_NODE_VERSION=${ENIGMA_NODE_VERSION:=14}
|
||||
ENIGMA_BRANCH=${ENIGMA_BRANCH:=master}
|
||||
ENIGMA_INSTALL_DIR=${ENIGMA_INSTALL_DIR:=$HOME/enigma-bbs}
|
||||
ENIGMA_SOURCE=${ENIGMA_SOURCE:=https://github.com/NuSkooler/enigma-bbs.git}
|
||||
TIME_FORMAT=`date "+%Y-%m-%d %H:%M:%S"`
|
||||
WAIT_BEFORE_INSTALL=10
|
||||
|
||||
# ANSI Codes
|
||||
RESET="\e[0m"
|
||||
BOLD="\e[1m"
|
||||
UNDERLINE="\e[4m"
|
||||
INVERSE="\e7m"
|
||||
FOREGROUND_BLACK="\e[30m"
|
||||
FOREGROUND_RED="\e[31m"
|
||||
FOREGROUND_GREEN="\e[32m"
|
||||
FOREGROUND_YELLOW="\e[33m"
|
||||
FOREGROUND_BLUE="\e[34m"
|
||||
FOREGROUND_MAGENTA="\e[35m"
|
||||
FOREGROUND_CYAN="\e[36m"
|
||||
FOREGROUND_WHITE="\e[37m"
|
||||
BACKGROUND_BLACK="\e[40m"
|
||||
BACKGROUND_RED="\e[41m"
|
||||
BACKGROUND_GREEN="\e[42m"
|
||||
BACKGROUND_YELLOW="\e[43m"
|
||||
BACKGROUND_BLUE="\e[44m"
|
||||
BACKGROUND_MAGENTA="\e[45m"
|
||||
BACKGROUND_CYAN="\e[46m"
|
||||
BACKGROUND_WHITE="\e[47m"
|
||||
FOREGROUND_STRONG_WHITE="\e[90m"
|
||||
FOREGROUND_STRONG_RED="\e[91m"
|
||||
FOREGROUND_STRONG_GREEN="\e[92m"
|
||||
FOREGROUND_STRONG_YELLOW="\e[93m"
|
||||
FOREGROUND_STRONG_BLUE="\e[94m"
|
||||
FOREGROUND_STRONG_MAGENTA="\e[95m"
|
||||
FOREGROUND_STRONG_CYAN="\e[96m"
|
||||
FOREGROUND_STRONG_WHITE="\e[97m"
|
||||
BACKGROUND_STRONG_BLACK="\e[100m"
|
||||
BACKGROUND_STRONG_RED="\e[101m"
|
||||
BACKGROUND_STRONG_GREEN="\e[102m"
|
||||
BACKGROUND_STRONG_YELLOW="\e[103m"
|
||||
BACKGROUND_STRONG_BLUE="\w[104m"
|
||||
BACKGROUND_STRONG_MAGENTA="\e[105m"
|
||||
BACKGROUND_STRONG_CYAN="\e[106m"
|
||||
BACKGROUND_STRONG_WHITE="\e[107m"
|
||||
|
||||
enigma_header() {
|
||||
clear
|
||||
printf "$FOREGROUND_STRONG_WHITE"
|
||||
cat << EndOfMessage
|
||||
______
|
||||
_____________________ _____ ____________________ __________\\_ /
|
||||
@@ -22,27 +58,16 @@ _____________________ _____ ____________________ __________\\_ /
|
||||
<*> ENiGMA½ // https://github.com/NuSkooler/enigma-bbs <*> /__/
|
||||
|
||||
|
||||
Installing ENiGMA½:
|
||||
ENiGMA½:
|
||||
Source : ${ENIGMA_SOURCE} (${ENIGMA_BRANCH} branch)
|
||||
Destination: ${ENIGMA_INSTALL_DIR}
|
||||
Node.js : ${ENIGMA_NODE_VERSION}.x via NVM (If you have NVM it will be updated to the latest version)
|
||||
|
||||
>> If this isn't what you were expecting, hit CTRL-C now!
|
||||
>> Installation will continue in ${WAIT_BEFORE_INSTALL} seconds...
|
||||
|
||||
EndOfMessage
|
||||
|
||||
SECS=10
|
||||
while [ $SECS -gt 0 ]; do
|
||||
echo -ne "${SECS}... "
|
||||
sleep 1
|
||||
((SECS --))
|
||||
done
|
||||
echo ""
|
||||
printf "$RESET"
|
||||
}
|
||||
|
||||
fatal_error() {
|
||||
printf "${TIME_FORMAT} \e[41mERROR:\033[0m %b\n" "$*" >&2;
|
||||
log "${TIME_FORMAT} ERROR: %b\n $*" >&2;
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -51,52 +76,68 @@ check_exists() {
|
||||
}
|
||||
|
||||
enigma_install_needs_ex() {
|
||||
echo -ne "Checking for '$1'..."
|
||||
log "Checking for '$1'...${RESET}"
|
||||
if check_exists $1 ; then
|
||||
echo " Found!"
|
||||
log " Found!"
|
||||
else
|
||||
echo ""
|
||||
fatal_error "ENiGMA½ requires '$1' but it was not found. Please install it and/or make sure it is in your path then restart the installer.\n\n$2"
|
||||
fi
|
||||
}
|
||||
|
||||
enigma_install_needs_python() {
|
||||
echo -ne "Checking for a suitable Python installation..."
|
||||
if check_exists "python" || check_exists "python7" || check_exists "python3" ; then
|
||||
echo " Found!"
|
||||
else
|
||||
echo ""
|
||||
fatal_error "ENiGMA½ requires Python for node-gyp to build binaries. Please see https://www.npmjs.com/package/node-gyp for details."
|
||||
fi
|
||||
}
|
||||
|
||||
enigma_install_needs() {
|
||||
enigma_install_needs_ex $1 "Examples:\n sudo apt install $1 # Debian/Ubuntu\n sudo yum install $1 # CentOS"
|
||||
}
|
||||
|
||||
log() {
|
||||
printf "${TIME_FORMAT} %b\n" "$*";
|
||||
enigma_has_mise() {
|
||||
log "Checking for an installation of mise-en-place (https://mise.jdx.dev/)"
|
||||
if check_exists "mise"; then
|
||||
log "Found!"
|
||||
else
|
||||
log ""
|
||||
fatal_error "ENiGMA½ requires mise-enplace to install dependencies."
|
||||
fi
|
||||
}
|
||||
|
||||
log() {
|
||||
local LOG_CONTENT=$1
|
||||
|
||||
local COLOUR=""
|
||||
case $LOG_CONTENT in
|
||||
"ERROR")
|
||||
COLOUR="${FOREGROUND_STRONG_RED}"
|
||||
;;
|
||||
*)
|
||||
COLOUR="${FOREGROUND_GREEN}"
|
||||
;;
|
||||
esac
|
||||
|
||||
printf "${TIME_FORMAT} %b\n" "${COLOUR}${LOG_CONTENT}${RESET}";
|
||||
}
|
||||
|
||||
enigma_install_init() {
|
||||
enigma_install_needs git
|
||||
enigma_install_needs curl
|
||||
enigma_install_needs_python
|
||||
enigma_install_needs_ex make "Examples:\n sudo apt install build-essential # Debian/Ubuntu\n sudo yum groupinstall 'Development Tools' # CentOS"
|
||||
enigma_install_needs make
|
||||
enigma_install_needs gcc
|
||||
}
|
||||
|
||||
install_nvm() {
|
||||
log "Installing nvm"
|
||||
curl -o- https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
|
||||
install_mise_en_place() {
|
||||
curl https://mise.run | sh
|
||||
|
||||
# ~/.local/bin/mise activate bash >> bash
|
||||
eval "$(~/.local/bin/mise activate bash)"
|
||||
|
||||
cd $ENIGMA_INSTALL_DIR
|
||||
|
||||
mise install
|
||||
|
||||
export PATH="$HOME/.local/share/mise/shims:$PATH"
|
||||
}
|
||||
|
||||
configure_nvm() {
|
||||
log "Installing Node ${ENIGMA_NODE_VERSION} via nvm"
|
||||
. ~/.nvm/nvm.sh
|
||||
nvm install ${ENIGMA_NODE_VERSION}
|
||||
nvm use ${ENIGMA_NODE_VERSION}
|
||||
install_tools() {
|
||||
# Used to read toml files from bash scripts
|
||||
python -m pip install toml-cli
|
||||
}
|
||||
|
||||
download_enigma_source() {
|
||||
@@ -104,7 +145,7 @@ download_enigma_source() {
|
||||
INSTALL_DIR=${ENIGMA_INSTALL_DIR}
|
||||
|
||||
if [ -d "$INSTALL_DIR/.git" ]; then
|
||||
log "ENiGMA½ is already installed in $INSTALL_DIR, trying to update using git"
|
||||
log "ENiGMA½ is already installed in $INSTALL_DIR, trying to update using git..."
|
||||
command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" fetch 2> /dev/null ||
|
||||
fatal_error "Failed to update ENiGMA½, run 'git fetch' in $INSTALL_DIR yourself."
|
||||
else
|
||||
@@ -126,19 +167,21 @@ is_arch_arm() {
|
||||
|
||||
extra_npm_install_args() {
|
||||
if is_arch_arm ; then
|
||||
echo "--build-from-source"
|
||||
printf "--build-from-source"
|
||||
else
|
||||
echo ""
|
||||
printf ""
|
||||
fi
|
||||
}
|
||||
|
||||
install_node_packages() {
|
||||
log "Installing required Node packages..."
|
||||
log "Note that on some systems such as RPi, this can take a VERY long time. Be patient!"
|
||||
printf "Note that on some systems such as RPi, this can take a VERY long time. Be patient!"
|
||||
|
||||
cd ${ENIGMA_INSTALL_DIR}
|
||||
local EXTRA_NPM_ARGS=$(extra_npm_install_args)
|
||||
git checkout ${ENIGMA_BRANCH} && npm install ${EXTRA_NPM_ARGS}
|
||||
git checkout ${ENIGMA_BRANCH}
|
||||
|
||||
npm install ${EXTRA_NPM_ARGS}
|
||||
if [ $? -eq 0 ]; then
|
||||
log "npm package installation complete"
|
||||
else
|
||||
@@ -147,21 +190,22 @@ install_node_packages() {
|
||||
}
|
||||
|
||||
copy_template_files() {
|
||||
if [[ ! -f "./gopher/gophermap" ]]; then
|
||||
cp "./misc/gophermap" "./gopher/gophermap"
|
||||
log "Copying Template Files to ${ENIGMA_INSTALL_DIR}/misc/gophermap"
|
||||
if [[ ! -f "$ENIGMA_INSTALL_DIR/gopher/gophermap" ]]; then
|
||||
cp "$ENIGMA_INSTALL_DIR/misc/gophermap" "$ENIGMA_INSTALL_DIR/gopher/gophermap"
|
||||
fi
|
||||
}
|
||||
|
||||
enigma_footer() {
|
||||
log "ENiGMA½ installation complete!"
|
||||
echo -e "\e[1;33m"
|
||||
printf "${FOREGROUND_YELLOW}"
|
||||
cat << EndOfMessage
|
||||
|
||||
ADDITIONAL ACTIONS ARE REQUIRED!
|
||||
--------------------------------
|
||||
|
||||
1 - If you did not have Node.js and/or NVM installed previous to this please open a new shell/terminal now!
|
||||
(!) Not doing so will prevent 'nvm' or 'node' commands from functioning!
|
||||
1 - If you did not have Node.js and/or mise installed previous to this please open a new shell/terminal now!
|
||||
(!) Not doing so will prevent 'nvm', 'node', or 'python' commands from functioning!
|
||||
|
||||
2 - If this is the first time you've installed ENiGMA½, you now need to generate a minimal configuration:
|
||||
|
||||
@@ -185,17 +229,94 @@ ADDITIONAL ACTIONS ARE REQUIRED!
|
||||
|
||||
See docs for more information including other useful binaries!
|
||||
|
||||
4 - Start ENiGMA½ BBS!
|
||||
|
||||
./autoexec.sh
|
||||
|
||||
5 - Enable Automated Startup on Boot (optional)
|
||||
|
||||
Create a file in /etc/systemd/system/bbs.service with the following contents:
|
||||
[Unit]
|
||||
Description=Enigma½ BBS
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/home/<YOUR_USERNAME>/enigma-bbs/autoexec.sh
|
||||
Type=simple
|
||||
User=<YOUR_USERNAME>
|
||||
Group=<YOUR_USERNAME>
|
||||
WorkingDirectory=/home/<YOUR_USERNAME>/enigma-bbs/
|
||||
Restart=on-failure
|
||||
|
||||
Run 'sudo systemctl enable bbs.service'
|
||||
|
||||
EndOfMessage
|
||||
echo -e "\e[39m"
|
||||
printf "${RESET}"
|
||||
}
|
||||
|
||||
post_install() {
|
||||
MISE_SHIM_PATH_COMMAND='export PATH="$HOME/.local/share/mise/shims:$PATH"'
|
||||
if grep -Fxq "$MISE_SHIM_PATH_COMMAND" ~/.bashrc
|
||||
then
|
||||
log "Mise Shims found in your ~/.bashrc"
|
||||
else
|
||||
echo $MISE_SHIM_PATH_COMMAND >> ~/.bashrc
|
||||
log "Installed Mise Shims into your ~/.bashrc"
|
||||
fi
|
||||
}
|
||||
|
||||
install_dependencies() {
|
||||
log "Installing Dependencies..."
|
||||
|
||||
enigma_install_init
|
||||
install_mise_en_place
|
||||
install_tools
|
||||
install_node_packages
|
||||
post_install
|
||||
}
|
||||
|
||||
install_bbs() {
|
||||
log "Installing ENiGMA½..."
|
||||
|
||||
download_enigma_source
|
||||
copy_template_files
|
||||
}
|
||||
|
||||
install_everything() {
|
||||
log "Installing Everything..."
|
||||
download_enigma_source
|
||||
install_dependencies
|
||||
copy_template_files
|
||||
}
|
||||
|
||||
menu() {
|
||||
title="Installation Options"
|
||||
prompt="Select>"
|
||||
options=(
|
||||
"Install Dependencies"
|
||||
"Install ENiGMA½"
|
||||
"Install Everything"
|
||||
)
|
||||
|
||||
echo "$title"
|
||||
PS3="$prompt "
|
||||
select opt in "${options[@]}" "Quit"; do
|
||||
case "$REPLY" in
|
||||
1) enigma_install_init; install_dependencies; break;;
|
||||
2) install_bbs; break;;
|
||||
3) enigma_install_init; install_everything; break;;
|
||||
$((${#options[@]}+1))) printf "Goodbye!"; exit 0;;
|
||||
*) printf "${FOREGROUND_STRONG_RED}Invalid option.${RESET}\n";continue;;
|
||||
esac
|
||||
done < /dev/tty
|
||||
|
||||
unset PS3
|
||||
}
|
||||
|
||||
enigma_header
|
||||
enigma_install_init
|
||||
install_nvm
|
||||
configure_nvm
|
||||
download_enigma_source
|
||||
install_node_packages
|
||||
copy_template_files
|
||||
menu
|
||||
enigma_footer
|
||||
|
||||
} # this ensures the entire script is downloaded before execution
|
||||
|
||||
7
mise.toml
Normal file
7
mise.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[tools]
|
||||
node = '18'
|
||||
python = '3.10'
|
||||
|
||||
[env]
|
||||
NODE_ENV = 'production'
|
||||
_.python.venv = { path = ".venv", create = true }
|
||||
@@ -65,7 +65,7 @@
|
||||
"temptmp": "^1.1.0",
|
||||
"uuid": "8.3.2",
|
||||
"uuid-parse": "1.1.0",
|
||||
"ws": "7.5.10",
|
||||
"ws": "8.18.0",
|
||||
"yazl": "^2.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
8
watch.sh
Normal file
8
watch.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
LOGFILE_PATH=~/enigma-bbs/logs/enigma-bbs.log
|
||||
BUNYAN_BINARY_PATH=~/enigma-bbs/node_modules/bunyan/bin/bunyan
|
||||
|
||||
PATH="$HOME/.local/share/mise/shims:$PATH"
|
||||
|
||||
tail -F $LOGFILE_PATH | $BUNYAN_BINARY_PATH
|
||||
Reference in New Issue
Block a user