Haraka icon indicating copy to clipboard operation
Haraka copied to clipboard

Feature request: Expose SNICallback as a plugin hook

Open andris9 opened this issue 3 years ago • 4 comments

This would enable plugin-managed vanity MX domain names. There could be a plugin hook that is run on SNICallback. If the hook returns a secure context object then use it, otherwise go with the default.

I use such a system with WildDuck IMAP and ZoneMta MSA servers - there is a central certificate system that manages and renews Let’s Encrypt certificates. Whenever a SNI connection is established the certificate is pulled from that central system instead of using fixed certificates stored on disk.

andris9 avatar Sep 14 '21 03:09 andris9

a PR is welcome. I've looked at your TLS work and you did a nicer job than we have.

msimerson avatar Sep 14 '21 04:09 msimerson

I tried to do something like this but ran into an obstacle. In general what I'd like the SNICallback to look like would be something like this:

function SNICallback (servername, sniDone) {
    plugins.run_hooks('sni', pluginObject, ( ctx )=>{
        if (ctx){
            return sniDone(null, ctx);
        }

        if (ctxByHost[servername] === undefined) servername = '*';
        sniDone(null, ctxByHost[servername]);
    });
}

The problem is I can't figure out what to use as the plugin object? SNICallback method has no bound context.

andris9 avatar Sep 16 '21 11:09 andris9

SNICallback method has no bound context.

I recall running into this issue when I added SNI support and the ability to support more than one TLS certificate. IIRC, the reason there's no bound context is because plugins are run in node's vm.runInNewContext. That early design choice has proven to be a thorn in our side for ages. I worked around it by moving nearly all the TLS bits out of the plugin and into tls_socket.js. I figured it was just a matter of time before plugins/tls disappeared entirely.

msimerson avatar Sep 29 '21 14:09 msimerson

+1 on this. We are using a modified version of tls_socket.js to be able to dynamically load certs from a http/redis backend based on SNI but having that on a plugin would be awesome.

joelchornik avatar Oct 13 '21 19:10 joelchornik

A currently open PR exports SNICallback from tls_socket. (It also cleans up that file significantly, but that's a different issue). certsByHost was already exported. That same open PR turns certsByHost into a haraka-note but that's mostly just cleaning up JS object get/set safeguards.

Plugins have access to haraka_require, so they can do crazy like const tls_socket = haraka_require('tls_socket').

Now, this isn't an endorsement or anything, but that should make it possible to:

exports.register = function () {
  const tls_socket = haraka_require('tls_socket')
  tls_socket.SNICallback = (servername, sniDone) => {
    // do some stuff
    sniDone(null, context)
  }
}

msimerson avatar May 03 '24 17:05 msimerson

moved to wiki/TODO

msimerson avatar May 07 '24 16:05 msimerson