Just to add to the other solutions already posted, you could just use certificates on both ends, that way you could do the authentication at the TLS layer instead of the application layer if that helps at all.
Assuming you're using node on both servers, you could achieve this like so:
- Create one custom CA, and then one certificate and one private key for each server.
Each server might have code like:
const tls = require('tls');
function onMessage(socket, msg) {
// `msg` is a parsed message received from `socket`
}
// Receive incoming messages
tls.createServer({
rejectUnauthorized: true,
requestCert: true,
ca: MY_CUSTOM_CA,
cert: THIS_SERVER_CERT,
key: THIS_SERVER_PRIVATE_KEY
}, (socket) => {
if (!socket.authorized)
return socket.end(); // Certificate did not check out
console.log('Connection accepted');
// example protocol: newline-delimited JSON
var jsonBuffer = '';
socket.setEncoding('utf8');
socket.on('data', (chunk) => {
var chunks = chunk.split('\n');
var numComplete = chunks.length - 1;
// Last line does not have trailing newline
var incompleteChunk = chunks[numComplete];
if (numComplete === 0) {
jsonBuffer += incompleteChunk;
return;
}
chunks[0] = jsonBuffer + chunks[0];
for (var i = 0; i < numComplete; ++i) {
try {
onMessage(socket, JSON.parse(chunks[i]));
} catch (ex) {}
}
jsonBuffer = incompleteChunk;
});
socket.on('end', () => {
console.log('Connection ended');
});
}).listen(MY_PORT);
// Send outgoing messages
function sendMessages(host, port, msgs, cb) {
if (!Array.isArray(msgs))
msgs = [msgs];
var req = tls.connect({
host,
port,
rejectUnauthorized: true,
ca: MY_CUSTOM_CA,
cert: THIS_SERVER_CERT,
key: THIS_SERVER_PRIVATE_KEY
}, () => {
if (!this.authorized)
return this.end(); // Certificate did not check out
for (var i = 0; i < msgs.length; ++i)
this.write(JSON.stringify(msgs[i]) + '\n');
this.end();
}).once('error', onError).once('close', onClose);
function onError(err) {
req.removeListener('close', onClose);
cb(err);
}
function onClose() {
cb();
}
}
Add incoming message handling in onMessage()
and send outgoing messages with sendMessages()
.
You could also just keep a single socket open all the time instead of using a new connection per set of outgoing messages, but that would be a little more involved because you'd need to add an application-level keepalive mechanism and such, but it's certainly doable.