Bridge: Use selfsigned TLS certificate on Mac OS (#145)

This commit is contained in:
Michał Leszczyński
2023-04-09 12:42:56 +02:00
committed by GitHub
parent 2ba84b515d
commit 0e78214454
6 changed files with 119 additions and 6 deletions

View File

@@ -111,6 +111,7 @@ jobs:
node_modules/.bin/pkg --compress GZip -t node16-macos-x64 -c package.json -o dist/halocli entry_cli.js
node_modules/.bin/pkg --compress GZip -t node16-macos-x64 -c package.json -o dist/halo-bridge entry_bridge.js
mv "macos_bridge_app" "dist/HaLo CLI Bridge Server.app"
mv "macos_pkgbuild_scripts" "dist/macos_pkgbuild_scripts"
mv "Entitlements.plist" "dist/Entitlements.plist"
- name: Compress dist files
shell: bash
@@ -223,7 +224,7 @@ jobs:
chmod +x ./root/usr/local/bin/halocli
chmod +x ./root/usr/local/bin/halo-bridge
chmod +x "./root/Applications/HaLo CLI Bridge Server.app/Contents/MacOS/halocli_bridge_launcher"
pkgbuild --root ./root --identifier "org.arx.halo.halocli" --version "1.0.$(date +%s)" --install-location "/" --sign "${{ secrets.MACOS_SIGN_IDENTITY_INSTALLER }}" ./halocli-macos-x64.pkg
pkgbuild --root ./root --identifier "org.arx.halo.halocli" --version "1.0.$(date +%s)" --scripts "macos_pkgbuild_scripts/" --install-location "/" --sign "${{ secrets.MACOS_SIGN_IDENTITY_INSTALLER }}" ./halocli-macos-x64.pkg
- name: Notarize application for Mac OS
if: matrix.os == 'macos-latest'
run: |

View File

@@ -16,11 +16,17 @@ parser.add_argument("-l", "--listen-host", {
dest: "listenHost"
});
parser.add_argument("-p", "--listen-port", {
help: "Port where the server should bind",
help: "Port where the server should bind (HTTP/WS)",
type: "int",
default: 32868,
dest: "listenPort"
});
parser.add_argument("-P", "--listen-port-tls", {
help: "Port where the server should bind (HTTPS/WSS)",
type: "int",
default: 32869,
dest: "listenPortTLS"
});
parser.add_argument("-a", "--allow-origins", {
help: "List of origins that are allowed to connect (semicolon-separated)",
type: "str",

View File

@@ -21,7 +21,26 @@
document.getElementById('log').innerText += '\n' + data;
}
let wsp = createWs('ws://localhost:32868');
let wsAddress;
let wsPort;
if (!window.location.host.includes(':')) {
if (window.location.protocol === 'https:') {
wsPort = 443;
} else {
wsPort = 80;
}
} else {
wsPort = parseInt(window.location.host.split(':')[1]);
}
if (window.location.protocol === 'https:') {
wsAddress = 'wss://halo-bridge.local:' + wsPort;
} else {
wsAddress = 'ws://127.0.0.1:' + wsPort;
}
let wsp = createWs(wsAddress);
async function execHaloCommand(command) {
log('Executing command: ' + JSON.stringify(command));

View File

@@ -149,11 +149,16 @@ function runHalo(entryMode, args) {
if (entryMode === "server") {
console.log('Launching Web Socket Server...');
wsCreateServer(args, () => Object.keys(nfc.readers).map(r => nfc.readers[r].name));
let serverInfo = wsCreateServer(
args, () => Object.keys(nfc.readers).map(r => nfc.readers[r].name));
console.log('Web Socket Server is listening...');
if (!args.nonInteractive) {
open('http://127.0.0.1:' + args.listenPort);
if (serverInfo.hasTLS) {
open('https://halo-bridge.local:' + args.listenPortTLS);
} else {
open('http://127.0.0.1:' + args.listenPort);
}
}
} else {
stopPCSCTimeout = setTimeout(stopPCSC, 4000, "timeout", args.output);

View File

@@ -0,0 +1,36 @@
#!/bin/bash
set -e
# check if user requested us not to override the certificate
if [ -f /usr/local/etc/halo-bridge/DONT_OVERRIDE ]
then
exit 0
fi
# create a directory for our certificate
mkdir -p /usr/local/etc/halo-bridge
# remove stale files
rm -f /usr/local/etc/halo-bridge/private_key.pem
rm -f /usr/local/etc/halo-bridge/server.csr
rm -f /usr/local/etc/halo-bridge/server.csr
# generate new local certificate
openssl genrsa -out /usr/local/etc/halo-bridge/private_key.pem 2048
openssl req -new -sha256 -key /usr/local/etc/halo-bridge/private_key.pem -out /usr/local/etc/halo-bridge/server.csr -subj '/CN=halo-bridge.local/'
openssl req -x509 -sha256 -days 3650 -extensions SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName='DNS:halo-bridge.local'")) -key /usr/local/etc/halo-bridge/private_key.pem -in /usr/local/etc/halo-bridge/server.csr -out /usr/local/etc/halo-bridge/server.pem
# notify user that we are going to modify the trust list
osascript -e 'display alert "HaLo CLI Installer" message "We will generate a self-signed certificate for halo-bridge'\''s local domain and mark it as trusted in the system.\n\nThis step is required in order to support Safari web browser."'
# add certificate to the trust list
security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /usr/local/etc/halo-bridge/server.pem
# add halo-bridge.local domain to /etc/hosts if it doesn't exist yet
if ! grep -q "halo-bridge.local" /etc/hosts
then
echo "127.0.0.1 halo-bridge.local" >> /etc/hosts
fi
exit 0

View File

@@ -5,6 +5,10 @@ const crypto = require('crypto').webcrypto;
const {execHaloCmdPCSC} = require('../index.js');
const {dirname, randomBuffer} = require("./util");
const jwt = require('jsonwebtoken');
const https = require("https");
const fs = require("fs");
const path = require("path");
const os = require("os");
let wss = null;
@@ -115,10 +119,42 @@ function wsEventReaderDisconnected(reader) {
});
}
function readTLSData() {
let privateKeyPath;
let certificatePath;
if (process.platform === "win32") {
privateKeyPath = path.join(os.homedir(), ".halo-bridge\\private_key.pem");
certificatePath = path.join(os.homedir(), ".halo-bridge\\server.pem");
} else {
privateKeyPath = '/usr/local/etc/halo-bridge/private_key.pem';
certificatePath = '/usr/local/etc/halo-bridge/server.pem';
}
if (fs.existsSync(privateKeyPath) && fs.existsSync(certificatePath)) {
const privateKey = fs.readFileSync(privateKeyPath);
const certificate = fs.readFileSync(certificatePath);
return {privateKey, certificate};
}
return null;
}
function wsCreateServer(args, getReaderNames) {
const tlsData = readTLSData();
let serverTLS = null;
const app = express();
const server = app.listen(args.listenPort, args.listenHost);
if (tlsData) {
serverTLS = https.createServer({
key: tlsData.privateKey,
cert: tlsData.certificate
}, app).listen(args.listenPortTLS, args.listenHost);
}
wss = new WebSocketServer({noServer: true});
app.use(express.urlencoded({extended: false}));
@@ -174,6 +210,14 @@ function wsCreateServer(args, getReaderNames) {
});
});
if (serverTLS) {
serverTLS.on('upgrade', (request, socket, head) => {
wss.handleUpgrade(request, socket, head, socket => {
wss.emit('connection', socket, request);
});
});
}
wss.on('connection', (ws, req) => {
let permitted = false;
let originHostname = new URL(req.headers.origin).hostname;
@@ -190,7 +234,7 @@ function wsCreateServer(args, getReaderNames) {
permitted = true;
}
if (originHostname === "127.0.0.1" || originHostname === "localhost") {
if (originHostname === "127.0.0.1" || originHostname === "localhost" || originHostname === "halo-bridge.local") {
permitted = true;
}
@@ -273,6 +317,8 @@ function wsCreateServer(args, getReaderNames) {
});
}
});
return {hasTLS: !!serverTLS};
}
module.exports = {