Sync up with master

This commit is contained in:
Bryan Ashby 2023-10-11 19:52:21 -06:00
commit 2bf4540707
No known key found for this signature in database
GPG Key ID: C2C1B501E4EFD994
41 changed files with 801 additions and 770 deletions

View File

@ -7,7 +7,7 @@
"features": { "features": {
"ghcr.io/devcontainers/features/python:1": { "ghcr.io/devcontainers/features/python:1": {
"installTools": true, "installTools": true,
"version": "latest" "version": "3.11"
}, },
"ghcr.io/devcontainers-contrib/features/curl-apt-get:1": {}, "ghcr.io/devcontainers-contrib/features/curl-apt-get:1": {},
"ghcr.io/jungaretti/features/ripgrep:1": {}, "ghcr.io/jungaretti/features/ripgrep:1": {},

View File

@ -11,23 +11,23 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx - name: Set up Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v2
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v1 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v2 uses: docker/build-push-action@v3
with: with:
tags: enigmabbs/enigma-bbs:latest tags: enigmabbs/enigma-bbs:latest
file: docker/Dockerfile file: docker/Dockerfile

4
.gitignore vendored
View File

@ -10,4 +10,6 @@ logs/
mail/ mail/
node_modules/ node_modules/
docs/_site/ docs/_site/
docs/.sass-cache/ docs/.sass-cache/
docs/.jekyll-cache/

1
.vscode/launch.json vendored
View File

@ -6,6 +6,7 @@
"configurations": [ "configurations": [
{ {
"type": "node", "type": "node",
"runtimeExecutable": "/home/nuskooler/.local/share/rtx/installs/nodejs/16.20.2/bin/node",
"request": "launch", "request": "launch",
"name": "Launch Program", "name": "Launch Program",
"skipFiles": ["<node_internals>/**"], "skipFiles": ["<node_internals>/**"],

View File

@ -45,6 +45,10 @@ fileBaseListEntriesNoResults: {
See also: [Menu Modules](./docs/_docs/modding/menu-module.md). See also: [Menu Modules](./docs/_docs/modding/menu-module.md).
* Due to changes to supported algorithms in newer versions of openssl, the default list of supported algorithms for the ssh login server has changed. There are both removed ciphers as well as optional new kex algorithms available now. ***NOTE:*** Changes to supported algorithms are only needed to support keys generated with new versions of openssl, if you already have a ssl key in use you should not have to make any changes to your config.
* Removed ciphers: 'blowfish-cbc', 'arcfour256', 'arcfour128', and 'cast128-cbc'
* Added kex: 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'ecdh-sha2-nistp256', 'ecdh-sha2-nistp384', 'ecdh-sha2-nistp521'
## 0.0.12-beta to 0.0.13-beta ## 0.0.12-beta to 0.0.13-beta
* To enable the new Waiting for Caller (WFC) support, please see [WFC](docs/modding/wfc.md). * To enable the new Waiting for Caller (WFC) support, please see [WFC](docs/modding/wfc.md).
* :exclamation: The SSH server's `ssh2` module has gone through a major upgrade. Existing users will need to comment out two SSH KEX algorithms from their `config.hjson` if present else clients such as NetRunner will not be able to connect over SSH. Comment out `diffie-hellman-group-exchange-sha256` and `diffie-hellman-group-exchange-sha1` * :exclamation: The SSH server's `ssh2` module has gone through a major upgrade. Existing users will need to comment out two SSH KEX algorithms from their `config.hjson` if present else clients such as NetRunner will not be able to connect over SSH. Comment out `diffie-hellman-group-exchange-sha256` and `diffie-hellman-group-exchange-sha1`

View File

@ -187,7 +187,7 @@
} }
mci: { mci: {
VM1: { VM1: {
height: 15, height: 14
width: 50 width: 50
itemFormat: "|00|11{userName:<17.17}|03{affils:<21.21}|11{location:<19.19}|03{lastLoginTs}" itemFormat: "|00|11{userName:<17.17}|03{affils:<21.21}|11{location:<19.19}|03{lastLoginTs}"
focusItemFormat: "|00|19|15{userName:<17.17}{affils:<21.21}{location:<19.19}{lastLoginTs}" focusItemFormat: "|00|19|15{userName:<17.17}{affils:<21.21}{location:<19.19}{lastLoginTs}"
@ -344,7 +344,7 @@
} }
mci: { mci: {
VM1: { VM1: {
height: 14 height: 13
width: 70 width: 70
itemFormat: "|00|15{msgNum:>4} |03{subject:<28.27} |11{fromUserName:<20.20} |03{ts:<15.16} |15{newIndicator}" itemFormat: "|00|15{msgNum:>4} |03{subject:<28.27} |11{fromUserName:<20.20} |03{ts:<15.16} |15{newIndicator}"
focusItemFormat: "|00|19|15{msgNum:>4} {subject:<28.27} {fromUserName:<20.20} {ts:<15.16} {newIndicator}" focusItemFormat: "|00|19|15{msgNum:>4} {subject:<28.27} {fromUserName:<20.20} {ts:<15.16} {newIndicator}"
@ -418,7 +418,7 @@
} }
mci: { mci: {
VM1: { VM1: {
height: 14 height: 12
width: 70 width: 70
itemFormat: "|00|15{msgNum:>4} |03{subject:<28.27} |11{fromUserName:<20.20} |03{ts} |15{newIndicator}" itemFormat: "|00|15{msgNum:>4} |03{subject:<28.27} |11{fromUserName:<20.20} |03{ts} |15{newIndicator}"
focusItemFormat: "|00|19|15{msgNum:>4} {subject:<28.27} {fromUserName:<20.20} {ts} {newIndicator}" focusItemFormat: "|00|19|15{msgNum:>4} {subject:<28.27} {fromUserName:<20.20} {ts} {newIndicator}"
@ -620,7 +620,7 @@
} }
mci: { mci: {
VM1: { VM1: {
height: 16 height: 12
width: 71 width: 71
itemFormat: "|00|15 {msgNum:<4.4} |03{subject:<34.33} {fromUserName:<19.18} |03{ts:<12.12}" itemFormat: "|00|15 {msgNum:<4.4} |03{subject:<34.33} {fromUserName:<19.18} |03{ts:<12.12}"
focusItemFormat: "|00|19> |15{msgNum:<4.4} {subject:<34.33} {fromUserName:<19.18} {ts:<12.12}" focusItemFormat: "|00|19> |15{msgNum:<4.4} {subject:<34.33} {fromUserName:<19.18} {ts:<12.12}"
@ -786,7 +786,7 @@
} }
mci: { mci: {
VM1: { VM1: {
height: 14 height: 12
width: 70 width: 70
itemFormat: "|00|15 {msgNum:<5.5}|03{subject:<28.27} |15{fromUserName:<20.20} {ts}" itemFormat: "|00|15 {msgNum:<5.5}|03{subject:<28.27} |15{fromUserName:<20.20} {ts}"
focusItemFormat: "|00|19> |15{msgNum:<5.5}{subject:<28.27} {fromUserName:<20.20} {ts}" focusItemFormat: "|00|19> |15{msgNum:<5.5}{subject:<28.27} {fromUserName:<20.20} {ts}"
@ -1217,7 +1217,7 @@
2: { 2: {
mci: { mci: {
MT1: { MT1: {
height: 14 height: 13
width: 45 width: 45
} }

View File

@ -204,22 +204,15 @@ module.exports = () => {
// //
// 1 - Generate a Private Key (PK): // 1 - Generate a Private Key (PK):
// Currently ENiGMA 1/2 requires a PKCS#1 PEM formatted PK. // Currently ENiGMA 1/2 requires a PKCS#1 PEM formatted PK.
// To generate a secure PK, issue the following command: // For information on generating a key, see:
// // https://nuskooler.github.io/enigma-bbs/servers/loginservers/ssh.html#generate-a-ssh-private-key
// > openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
// -pkeyopt rsa_keygen_pubexp:65537 | openssl rsa \
// -out ./config/security/ssh_private_key.pem -aes128
//
// (The above is a more modern equivalent of the following):
// > openssl genrsa -aes128 -out ./config/security/ssh_private_key.pem 2048
// //
// 2 - Set 'privateKeyPass' to the password you used in step #1 // 2 - Set 'privateKeyPass' to the password you used in step #1
// //
// 3 - Finally, set 'enabled' to 'true' // 3 - Finally, set 'enabled' to 'true'
// //
// Additional reading: // Additional reading:
// - https://blog.sleeplessbeastie.eu/2017/12/28/how-to-generate-private-key/ // - https://nuskooler.github.io/enigma-bbs/servers/loginservers/ssh.html
// - https://gist.github.com/briansmith/2ee42439923d8e65a266994d0f70180b
// //
privateKeyPem: paths.join( privateKeyPem: paths.join(
__dirname, __dirname,
@ -238,14 +231,18 @@ module.exports = () => {
// //
algorithms: { algorithms: {
kex: [ kex: [
'curve25519-sha256',
'curve25519-sha256@libssh.org',
'ecdh-sha2-nistp256', 'ecdh-sha2-nistp256',
'ecdh-sha2-nistp384', 'ecdh-sha2-nistp384',
'ecdh-sha2-nistp521', 'ecdh-sha2-nistp521',
'diffie-hellman-group14-sha1', 'diffie-hellman-group14-sha1',
'diffie-hellman-group1-sha1', 'diffie-hellman-group1-sha1',
// Group exchange not currnetly supported 'curve25519-sha256',
// 'diffie-hellman-group-exchange-sha256', 'curve25519-sha256@libssh.org',
// 'diffie-hellman-group-exchange-sha1', 'ecdh-sha2-nistp256',
'ecdh-sha2-nistp384',
'ecdh-sha2-nistp521',
], ],
cipher: [ cipher: [
'aes128-ctr', 'aes128-ctr',
@ -258,12 +255,7 @@ module.exports = () => {
'aes256-cbc', 'aes256-cbc',
'aes192-cbc', 'aes192-cbc',
'aes128-cbc', 'aes128-cbc',
'blowfish-cbc',
'3des-cbc', '3des-cbc',
'arcfour256',
'arcfour128',
'cast128-cbc',
'arcfour',
], ],
hmac: [ hmac: [
'hmac-sha2-256', 'hmac-sha2-256',

View File

@ -57,8 +57,10 @@ module.exports = class Door {
run(exeInfo, cb) { run(exeInfo, cb) {
this.encoding = (exeInfo.encoding || 'cp437').toLowerCase(); this.encoding = (exeInfo.encoding || 'cp437').toLowerCase();
if ('socket' === this.io && !this.sockServer) { if ('socket' === this.io) {
return cb(Errors.UnexpectedState('Socket server is not running')); if (!this.sockServer) {
return cb(Errors.UnexpectedState('Socket server is not running'));
}
} else if ('stdio' !== this.io) { } else if ('stdio' !== this.io) {
return cb(Errors.Invalid(`"${this.io}" is not a valid io type!`)); return cb(Errors.Invalid(`"${this.io}" is not a valid io type!`));
} }

View File

@ -195,7 +195,12 @@ exports.FullScreenEditorModule =
); );
if (errMsgView) { if (errMsgView) {
if (err) { if (err) {
errMsgView.setText(err.friendlyText); errMsgView.clearText();
errMsgView.setText(err.friendlyText || err.message);
if (MciViewIds.header.subject === err.view.getId()) {
// :TODO: for "area" mode, should probably just bail if this is emtpy (e.g. cancel)
}
} else { } else {
errMsgView.clearText(); errMsgView.clearText();
} }
@ -208,6 +213,13 @@ exports.FullScreenEditorModule =
return cb(null); return cb(null);
}, },
editModeEscPressed: function (formData, extraArgs, cb) { editModeEscPressed: function (formData, extraArgs, cb) {
const errMsgView = self.viewControllers.header.getView(
MciViewIds.header.errorMsg
);
if (errMsgView) {
errMsgView.clearText();
}
self.footerMode = self.footerMode =
'editor' === self.footerMode ? 'editorMenu' : 'editor'; 'editor' === self.footerMode ? 'editorMenu' : 'editor';
@ -1085,7 +1097,7 @@ exports.FullScreenEditorModule =
posView.setText( posView.setText(
_.padStart(String(pos.row + 1), 2, '0') + _.padStart(String(pos.row + 1), 2, '0') +
',' + ',' +
_.padEnd(String(pos.col + 1), 2, '0') _.padStart(String(pos.col + 1), 2, '0')
); );
this.client.term.rawWrite(ansi.restorePos()); this.client.term.rawWrite(ansi.restorePos());
} }

View File

@ -135,7 +135,7 @@ module.exports = class Address {
static fromString(addrStr) { static fromString(addrStr) {
const m = FTN_ADDRESS_REGEXP.exec(addrStr); const m = FTN_ADDRESS_REGEXP.exec(addrStr);
if (m) { if (m && m[2] && m[3]) {
// start with a 2D // start with a 2D
let addr = { let addr = {
net: parseInt(m[2]), net: parseInt(m[2]),

View File

@ -778,6 +778,11 @@ module.exports = class Message {
} }
persist(cb) { persist(cb) {
const containsNonWhitespaceCharacterRegEx = /\S/;
if (!containsNonWhitespaceCharacterRegEx.test(this.message)) {
return cb(Errors.Invalid('Empty message'));
}
if (!this.isValid()) { if (!this.isValid()) {
return cb(Errors.Invalid('Cannot persist invalid message!')); return cb(Errors.Invalid('Cannot persist invalid message!'));
} }

View File

@ -14,6 +14,39 @@ exports.moduleInfo = {
author: 'NuSkooler', author: 'NuSkooler',
}; };
const MciViewIds = {
header: {
from: 1,
to: 2,
subject: 3,
errorMsg: 4,
modTimestamp: 5,
msgNum: 6,
msgTotal: 7,
customRangeStart: 10, // 10+ = customs
},
body: {
message: 1,
},
// :TODO: quote builder MCIs - remove all magic #'s
// :TODO: consolidate all footer MCI's - remove all magic #'s
ViewModeFooter: {
MsgNum: 6,
MsgTotal: 7,
// :TODO: Just use custom ranges
},
quoteBuilder: {
quotedMsg: 1,
// 2 NYI
quoteLines: 3,
},
};
exports.getModule = class AreaPostFSEModule extends FullScreenEditorModule { exports.getModule = class AreaPostFSEModule extends FullScreenEditorModule {
constructor(options) { constructor(options) {
super(options); super(options);
@ -42,19 +75,25 @@ exports.getModule = class AreaPostFSEModule extends FullScreenEditorModule {
], ],
function complete(err) { function complete(err) {
if (err) { if (err) {
// :TODO:... sooooo now what? const errMsgView = self.viewControllers.header.getView(
} else { MciViewIds.header.errorMsg
// note: not logging 'from' here as it's part of client.log.xxxx()
self.client.log.info(
{
to: msg.toUserName,
subject: msg.subject,
uuid: msg.messageUuid,
},
`User "${self.client.user.username}" posted message to "${msg.toUserName}" (${msg.areaTag})`
); );
if (errMsgView) {
errMsgView.setText(err.message);
}
return cb(err);
} }
// note: not logging 'from' here as it's part of client.log.xxxx()
self.client.log.info(
{
to: msg.toUserName,
subject: msg.subject,
uuid: msg.messageUuid,
},
`User "${self.client.user.username}" posted message to "${msg.toUserName}" (${msg.areaTag})`
);
return self.nextMenu(cb); return self.nextMenu(cb);
} }
); );

View File

@ -22,6 +22,8 @@ exports.validateEmailAvail = validateEmailAvail;
exports.validateBirthdate = validateBirthdate; exports.validateBirthdate = validateBirthdate;
exports.validatePasswordSpec = validatePasswordSpec; exports.validatePasswordSpec = validatePasswordSpec;
const emptyFieldError = () => new Error('Field cannot be empty');
function validateNonEmpty(data, cb) { function validateNonEmpty(data, cb) {
return cb( return cb(
data && data.length > 0 data && data.length > 0
@ -136,6 +138,11 @@ function validateGeneralMailAddressedTo(data, cb) {
// - Supported remote flavors such as FTN, email, ... // - Supported remote flavors such as FTN, email, ...
// //
const addressedToInfo = getAddressedToInfo(data); const addressedToInfo = getAddressedToInfo(data);
if (addressedToInfo.name.length === 0) {
return cb(emptyFieldError());
}
if (Message.AddressFlavor.Local !== addressedToInfo.flavor) { if (Message.AddressFlavor.Local !== addressedToInfo.flavor) {
return cb(null); return cb(null);
} }

View File

@ -199,6 +199,10 @@ TextView.prototype.setText = function (text, redraw) {
}; };
TextView.prototype.clearText = function () { TextView.prototype.clearText = function () {
if (this.text) {
this.setText(this.fillChar.repeat(this.text.length));
}
this.setText(''); this.setText('');
}; };

View File

@ -77,6 +77,8 @@ GEM
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0) mercenary (0.4.0)
minitest (5.19.0) minitest (5.19.0)
nokogiri (1.15.4-aarch64-linux)
racc (~> 1.4)
nokogiri (1.15.4-x86_64-linux) nokogiri (1.15.4-x86_64-linux)
racc (~> 1.4) racc (~> 1.4)
pathutil (0.16.2) pathutil (0.16.2)
@ -101,6 +103,7 @@ GEM
webrick (1.8.1) webrick (1.8.1)
PLATFORMS PLATFORMS
aarch64-linux
x86_64-linux x86_64-linux
DEPENDENCIES DEPENDENCIES

View File

@ -131,4 +131,5 @@ collections:
- admin/oputil.md - admin/oputil.md
- admin/updating.md - admin/updating.md
- troubleshooting/monitoring-logs.md - troubleshooting/monitoring-logs.md
- troubleshooting/ssh-troubleshooting.md

View File

@ -3,7 +3,7 @@ layout: page
title: TIC Support title: TIC Support
--- ---
## TIC Support ## TIC Support
ENiGMA½ supports FidoNet-Style TIC file attachments by mapping TIC areas to local file areas. ENiGMA½ supports FidoNet-Style TIC file attachments by mapping external TIC area tags to local file areas.
Under a given node defined in the `ftn_bso` config section in `config.hjson` (see Under a given node defined in the `ftn_bso` config section in `config.hjson` (see
[BSO Import/Export](../messageareas/bso-import-export.md)), TIC configuration may be supplied: [BSO Import/Export](../messageareas/bso-import-export.md)), TIC configuration may be supplied:
@ -17,9 +17,9 @@ Under a given node defined in the `ftn_bso` config section in `config.hjson` (se
packetPassword: mypass packetPassword: mypass
encoding: cp437 encoding: cp437
archiveType: zip archiveType: zip
tic: { tic: { // <--- General TIC config for 46:*
password: TESTY-TEST password: TESTY-TEST
uploadBy: Agoranet TIC uploadBy: AgoraNet TIC
allowReplace: true allowReplace: true
} }
} }
@ -29,7 +29,15 @@ Under a given node defined in the `ftn_bso` config section in `config.hjson` (se
} }
``` ```
You then need to configure the mapping between TIC areas you want to carry, and the file base area and storage tag for them to be tossed to. Optionally you can also add hashtags to the tossed files to assist users in searching for files: Valid `tic` members:
| Item | Required | Description |
|--------|---------------|------------------|
| `password` | :-1: | TIC packet password, if required |
| `uploadedBy` | :-1: | Sets the "uploaded by" field for TIC attachments, for example "AgoraNet TIC" |
| `allowReplace` | :-1: | Set to `true` to allow TIC attachments to replace each other. This is especially handy for things like weekly node list attachments |
Next, we need to configure the mapping between TIC areas you want to carry, and the file base area (and, optionally, specific storage tag) for them to be tossed to. You can also add hashtags to the tossed files to assist users in searching for files:
```hjson ```hjson
ticAreas: { ticAreas: {
@ -41,10 +49,22 @@ ticAreas: {
} }
``` ```
Multiple TIC areas can be mapped to a single file base area.
> :information_source: Note that in the example above `agn_node` represents the **external** network area tag, usually represented in all caps. In this case, `AGN_NODE`.
Valid `ticAreas` members under a given node mapping are as follows:
| Item | Required | Description |
|--------|---------------|------------------|
| `areaTag` | :+1: | Specifies the local areaTag in which to place TIC attachments |
| `storageTag` | :-1: | Optionally, set a specific storageTag. If not set, the default for this area will be used. |
| `hashTags` | :-1: | One or more optional hash tags to assign TIC attachments in this area. |
💡 Multiple TIC areas can be mapped to a single file base area.
### Example Configuration ### Example Configuration
An example configuration linking file base areas, FTN BSO node configuration and TIC area configuration. Example configuration fragments mapping file base areas, FTN BSO node configuration and TIC area configuration.
```hjson ```hjson
fileBase: { fileBase: {
@ -79,22 +99,24 @@ scannerTossers: {
} }
} }
} }
ticAreas: {
// here we map AgoraNet AGN_NODE -> local msgNetworks file area
agn_node: {
areaTag: msgNetworks
storageTag: msg_network
hashTags: agoranet,nodelist
}
agn_info: {
areaTag: msgNetworks
storageTag: msg_network
hashTags: agoranet,infopack
}
}
} }
} }
ticAreas: {
agn_node: {
areaTag: msgNetworks
storageTag: msg_network
hashTags: agoranet,nodelist
}
agn_info: {
areaTag: msgNetworks
storageTag: msg_network
hashTags: agoranet,infopack
}
}
``` ```
## See Also ## See Also

View File

@ -3,9 +3,13 @@ layout: page
title: SSH Server title: SSH Server
--- ---
## SSH Login Server ## SSH Login Server
The ENiGMA½ SSH *login server* allows secure user logins over SSH (ssh://). The ENiGMA½ SSH *login server* allows secure user logins over SSH (ssh://).
*Note:* If you run into any troubles during SSH setup, please see [Troubleshooting SSH](../../troubleshooting/ssh-troubleshooting.md)
## Configuration ## Configuration
Entries available under `config.loginServers.ssh`: Entries available under `config.loginServers.ssh`:
| Item | Required | Description | | Item | Required | Description |
@ -20,10 +24,8 @@ Entries available under `config.loginServers.ssh`:
| `algorithms` | :-1: | Configuration block for SSH algorithms. Includes keys of `kex`, `cipher`, `hmac`, and `compress`. See the algorithms section in the [ssh2-streams](https://github.com/mscdex/ssh2-streams#ssh2stream-methods) documentation for details. For defaults set by ENiGMA½, see `core/config_default.js`. | `algorithms` | :-1: | Configuration block for SSH algorithms. Includes keys of `kex`, `cipher`, `hmac`, and `compress`. See the algorithms section in the [ssh2-streams](https://github.com/mscdex/ssh2-streams#ssh2stream-methods) documentation for details. For defaults set by ENiGMA½, see `core/config_default.js`.
| `traceConnections` | :-1: | Set to `true` to enable full trace-level information on SSH connections. | `traceConnections` | :-1: | Set to `true` to enable full trace-level information on SSH connections.
* *IMPORTANT* With the `privateKeyPass` option set, make sure that you verify that the config file is not readable by other users! * *IMPORTANT* With the `privateKeyPass` option set, make sure that you verify that the config file is not readable by other users!
### Example Configuration ### Example Configuration
```hjson ```hjson
@ -40,43 +42,94 @@ Entries available under `config.loginServers.ssh`:
``` ```
## Generate a SSH Private Key ## Generate a SSH Private Key
To utilize the SSH server, an SSH Private Key (PK) will need generated. OpenSSH or (with some versions) OpenSSL can be used for this task: To utilize the SSH server, an SSH Private Key (PK) will need generated. OpenSSH or (with some versions) OpenSSL can be used for this task:
### OpenSSH ### OpenSSH (Preferred)
```bash #### OpenSSH Install - Linux / Mac
ssh-keygen -m PEM -h -f config/ssh_private_key.pem
If it is not already available, install OpenSSH using the package manager of your choice (should be pre-installed on most distributions.)
#### Running OpenSSH - Linux / Mac
From the root directory of the Enigma BBS, run the following:
```shell
mkdir -p config/security
ssh-keygen -t rsa -m PEM -h -f config/security/ssh_private_key.pem
``` ```
#### Windows Install - OpenSSH
OpenSSH may already be installed, try running `ssh-keygen.exe`. If not, see this page: [Install OpenSSH for Windows](https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse?tabs=gui)
#### Running OpenSSH - Windows
After installation, go to the root directory of your enigma project and run:
```powershell
mkdir .\config\security -ErrorAction SilentlyContinue
ssh-keygen.exe -t rsa -m PEM -h -f .\config\security\ssh_private_key.pem
```
#### ssh-keygen options
Option descriptions: Option descriptions:
| Option | Description | | Option | Description |
|------|-------------| |------|-------------|
| `-t rsa` | Use the RSA algorithm needed for the `ssh2` library |
| `-m PEM` | Set the output format to `PEM`, compatible with the `ssh2` library | | `-m PEM` | Set the output format to `PEM`, compatible with the `ssh2` library |
| `-h` | Generate a host key | | `-h` | Generate a host key |
| `-f config/ssh_private_key.pem` | Filename for the private key. Used in the `privateKeyPem` option in the configuration | | `-f config/ssh_private_key.pem` | Filename for the private key. Used in the `privateKeyPem` option in the configuration |
When you execute the `ssh-keygen` command it will ask for a passphrase (and a confirmation.) This should then be used as the value for `privateKeyPass` in the configuration. When you execute the `ssh-keygen` command it will ask for a passphrase (and a confirmation.) This should then be used as the value for `privateKeyPass` in the configuration.
### OpenSSL ### OpenSSL
If you do not have OpenSSH installed or if you have trouble with the above OpenSSH commands, using some versions for OpenSSL (before version 3) the following commands may work as well: #### Open SSL Install - Linux / Mac
If not already installed, install via the `openssl` package on most package managers.
```bash #### Open SSL Install - Windows
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 | openssl rsa -out ./config/ssh_private_key.pem -aes128
```powershell
winget install -e --id ShiningLight.OpenSSL
``` ```
Or for even older OpenSSL versions: #### Running OpenSSL
```bash *Note:* Using `ssh-keygen` from OpenSSL is recommended where possible. If you have trouble with the above OpenSSH commands, using some versions for OpenSSL (before version 3) the following commands may work as well:
#### Running OpenSSL - Linux / Mac
Run the following from the root directory of Enigma
```shell
mkdir -p config/security
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 | openssl rsa -out ./config/security/ssh_private_key.pem -aes128
```
#### Running OpenSSL - Windows
Run the following from the root directory of Enigma (note: you may need to specify the full path to openssl.exe if it isn't in your system path, on my system it was `C:\Program Files\OpenSSL-Win64\bin\openssl.exe`):
```powershell
mkdir .\config\security -ErrorAction SilentlyContinue
openssl.exe genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 | openssl.exe rsa -out ./config/security/ssh_private_key.pem -aes128
```
#### Running Older OpenSSL
For older OpenSSL versions, the following command has been known to work:
```shell
openssl genrsa -aes128 -out ./config/ssh_private_key.pem 2048 openssl genrsa -aes128 -out ./config/ssh_private_key.pem 2048
``` ```
Note that you may need `-3des` for very old implementations or SSH clients! *Note:* that you may need `-3des` for very old implementations or SSH clients!
## Prompt ## Prompt
The keyboard interactive prompt can be customized using a `SSHPMPT.ASC` art file. See [art](../../art/general.md) for more information on configuring. This prompt includes a `newUserNames` variable to show the list of allowed new user names (see `firstMenuNewUser` above.) See [mci](../../art/mci.md) for information about formatting this string. Note: Regardless of the content of the `SSHPMPT.ASC` file, the prompt is surrounded by "Access denied", a newline, the prompt, another newline, and then the string "\[username]'s password: ". This normally occurs after the first password prompt (no art is shown before the first password attempt is made.) The keyboard interactive prompt can be customized using a `SSHPMPT.ASC` art file. See [art](../../art/general.md) for more information on configuring. This prompt includes a `newUserNames` variable to show the list of allowed new user names (see `firstMenuNewUser` above.) See [mci](../../art/mci.md) for information about formatting this string. Note: Regardless of the content of the `SSHPMPT.ASC` file, the prompt is surrounded by "Access denied", a newline, the prompt, another newline, and then the string "\[username]'s password: ". This normally occurs after the first password prompt (no art is shown before the first password attempt is made.)

View File

@ -0,0 +1,45 @@
---
layout: page
title: Troubleshooting SSH
---
Stuck with errors trying to get your SSH setup configured? See below for some common problems. Or as always, reach out to us by creating an [Issue](https://github.com/NuSkooler/enigma-bbs/issues) or start a [Discussion](https://github.com/NuSkooler/enigma-bbs/discussions)
## No Such File or Directory
***Symptom:***
BBS not starting with an error similar to the following:
```shell
Error initializing: Error: ENOENT: no such file or directory, open '<path>/config/security/ssh_private_key.pem'
```
***Solution:***
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
***Symptom:***
Some ssh clients connect, but Netrunner (and other older clients) get a connection failed message and the following is in the log:
```shell
"level":40,"error":"Handshake failed","code":2,"msg":"SSH connection error"
```
***Solution:***
The key was most likely not generated with the `-t rsa` option, and is using a newer algorithm that is not supported by Netrunner and similar clients. Regenerate the certificate with the `-t rsa` option.
***Symptom:***
Some ssh clients connect, but Netrunner (and other older clients) get a connection failed message and the following is in the log:
```shell
"level":40,"error":"Group exchange not implemented for server","msg":"SSH connection error"
```
***Solution:***
Remove the following encryption protocols from your `config.hjson`: `diffie-hellman-group-exchange-sha256` and `diffie-hellman-group-exchange-sha1`

View File

@ -65,9 +65,9 @@
"sanitize-filename": "^1.6.3", "sanitize-filename": "^1.6.3",
"sqlite3": "5.1.6", "sqlite3": "5.1.6",
"sqlite3-trans": "1.3.0", "sqlite3-trans": "1.3.0",
"ssh2": "1.11.0",
"string-strip-html": "8.4.0", "string-strip-html": "8.4.0",
"systeminformation": "5.12.3", "ssh2": "1.14.0",
"systeminformation": "5.21.7",
"telnet-socket": "0.2.4", "telnet-socket": "0.2.4",
"temptmp": "^1.1.0", "temptmp": "^1.1.0",
"uuid": "8.3.2", "uuid": "8.3.2",

1219
yarn.lock

File diff suppressed because it is too large Load Diff