Compare commits
2 Commits
e9f29dc569
...
6312eaec48
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6312eaec48 | ||
|
|
3e8ea1ef76 |
@@ -1,5 +1,6 @@
|
|||||||
import { execSync } from 'node:child_process';
|
import { execSync } from 'node:child_process';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
|
import os from 'node:os';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
@@ -12,10 +13,50 @@ const certPath = path.join(certsDir, 'cert.pem');
|
|||||||
|
|
||||||
fs.mkdirSync(certsDir, { recursive: true });
|
fs.mkdirSync(certsDir, { recursive: true });
|
||||||
|
|
||||||
// ECDSA P-256 keeps pairing QR codes much smaller than RSA-2048
|
function isIp(value) {
|
||||||
const subject = '/CN=localhost/O=JTL POS Sync/C=DE';
|
return /^(?:\d{1,3}\.){3}\d{1,3}$/.test(value) || value.includes(':');
|
||||||
const san = 'subjectAltName=DNS:localhost,IP:127.0.0.1,IP:0.0.0.0';
|
}
|
||||||
|
|
||||||
|
function localIpv4s() {
|
||||||
|
const ips = [];
|
||||||
|
for (const entries of Object.values(os.networkInterfaces())) {
|
||||||
|
for (const entry of entries || []) {
|
||||||
|
if (entry.family !== 'IPv4' || entry.internal) continue;
|
||||||
|
// Skip link-local / docker / libvirt bridge noise by default — keep LAN + extras via args
|
||||||
|
if (entry.address.startsWith('169.254.')) continue;
|
||||||
|
if (entry.address.startsWith('172.17.')) continue;
|
||||||
|
if (entry.address.startsWith('192.168.122.')) continue;
|
||||||
|
ips.push(entry.address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ips;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dnsNames = new Set(['localhost']);
|
||||||
|
const ipAddrs = new Set(['127.0.0.1', '0.0.0.0']);
|
||||||
|
|
||||||
|
for (const ip of localIpv4s()) {
|
||||||
|
ipAddrs.add(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
const extras = [
|
||||||
|
...(process.env.CERT_SAN || '').split(/[,\s]+/).filter(Boolean),
|
||||||
|
...process.argv.slice(2),
|
||||||
|
];
|
||||||
|
for (const value of extras) {
|
||||||
|
if (isIp(value)) ipAddrs.add(value);
|
||||||
|
else dnsNames.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sanParts = [
|
||||||
|
...[...dnsNames].map((name) => `DNS:${name}`),
|
||||||
|
...[...ipAddrs].map((ip) => `IP:${ip}`),
|
||||||
|
];
|
||||||
|
const san = `subjectAltName=${sanParts.join(',')}`;
|
||||||
|
const cn = [...dnsNames][0] || 'localhost';
|
||||||
|
const subject = `/CN=${cn}/O=JTL POS Sync/C=DE`;
|
||||||
|
|
||||||
|
// ECDSA P-256 keeps pairing QR codes much smaller than RSA-2048
|
||||||
execSync(
|
execSync(
|
||||||
`openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -keyout "${keyPath}" -out "${certPath}" -days 3650 -subj "${subject}" -addext "${san}"`,
|
`openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -keyout "${keyPath}" -out "${certPath}" -days 3650 -subj "${subject}" -addext "${san}"`,
|
||||||
{ stdio: 'inherit' }
|
{ stdio: 'inherit' }
|
||||||
@@ -34,5 +75,6 @@ const serial = execSync(`openssl x509 -in "${certPath}" -noout -serial`, {
|
|||||||
|
|
||||||
logger.success(`Wrote ${keyPath}`);
|
logger.success(`Wrote ${keyPath}`);
|
||||||
logger.success(`Wrote ${certPath}`);
|
logger.success(`Wrote ${certPath}`);
|
||||||
|
logger.info(`SAN: ${sanParts.join(', ')}`);
|
||||||
logger.info(`Fingerprint: ${sha1.replace(/:/g, '')}`);
|
logger.info(`Fingerprint: ${sha1.replace(/:/g, '')}`);
|
||||||
logger.info(`Serial: ${serial}`);
|
logger.info(`Serial: ${serial}`);
|
||||||
|
|||||||
@@ -79,17 +79,25 @@ The server starts without MSSQL if `MSSQL_USER` is unset or the connection fails
|
|||||||
|
|
||||||
## TLS certificates
|
## TLS certificates
|
||||||
|
|
||||||
Place a certificate and key at `certs/cert.pem` and `certs/key.pem` (relative to the working directory when you run the binary).
|
From the repo root (preferred — picks up LAN IPs automatically):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run cert
|
||||||
|
# optional extras:
|
||||||
|
npm run cert -- 192.168.188.22 sync.quixpos.com
|
||||||
|
```
|
||||||
|
|
||||||
|
Or manually:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p certs
|
mkdir -p certs
|
||||||
openssl req -x509 -newkey rsa:2048 -nodes \
|
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes \
|
||||||
-keyout certs/key.pem -out certs/cert.pem -days 3650 \
|
-keyout certs/key.pem -out certs/cert.pem -days 3650 \
|
||||||
-subj '/CN=localhost/O=JTL POS Sync/C=DE' \
|
-subj '/CN=localhost/O=JTL POS Sync/C=DE' \
|
||||||
-addext 'subjectAltName=DNS:localhost,IP:127.0.0.1,IP:0.0.0.0'
|
-addext 'subjectAltName=DNS:localhost,DNS:sync.quixpos.com,IP:127.0.0.1,IP:0.0.0.0,IP:192.168.188.22'
|
||||||
```
|
```
|
||||||
|
|
||||||
On startup the server prints the pairing code and whether MSSQL connected.
|
Place `certs/cert.pem` and `certs/key.pem` relative to the working directory when you run the binary. The browser hostname check requires the address you open (`192.168.x.x` or a DNS name) to appear in the certificate SAN — trusting a CA alone is not enough.
|
||||||
|
|
||||||
## API endpoints
|
## API endpoints
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ export function handle(req, res, { url, pairingStore, config }) {
|
|||||||
|
|
||||||
if (authCode.length === 6) {
|
if (authCode.length === 6) {
|
||||||
if (pairingStore.hasPairingCode(authCode)) {
|
if (pairingStore.hasPairingCode(authCode)) {
|
||||||
pairingStore.revokePairingCode(authCode);
|
|
||||||
pairingStore.registerDevice(config.authToken, name);
|
pairingStore.registerDevice(config.authToken, name);
|
||||||
return sendJson(res, 200, buildClientStep2(authCode, config));
|
return sendJson(res, 200, buildClientStep2(authCode, config));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user