Jump to content

NNCP

From NixOS Wiki

NNCP (Node to Node copy) is a collection of utilities simplifying secure store-and-forward files, mail and command exchanging.

These utilities are intended to help build up small size (dozens of nodes) ad-hoc friend-to-friend (F2F) statically routed darknet delay-tolerant networks for fire-and-forget secure reliable files, file requests, Internet mail and commands transmission. All packets are integrity checked, end-to-end encrypted, explicitly authenticated by known participants public keys. Onion encryption is applied to relayed packets. Each node acts both as a client and server, can use push and poll behaviour model. Also there is multicasting areas support.

Out-of-box offline sneakernet/floppynet, dead drops, sequential and append-only CD-ROM/tape storages, air-gapped computers support. But online TCP daemon with full-duplex resumable data transmission exists.

http://www.nncpgo.org/

Configuration

NNCP can be installed and configured manually or via NixOS configuration.

In any case the first step is to generate a configuration file.

$ nncp-cfgnew -nocomments > /etc/secrets/nncp.hjson

This generated file should be stripped down to include only the self and neigh sections:

{
  self: {
    # DO NOT show anyone your private keys!!!
    id: HFTEI…SITTA
    exchpub: RG2SF…7JEYA
    exchprv: 4YAON…LWCMA
    signpub: ASKTA…EFVSQ
    signprv: Z6Q4R…SC2ZI
    noiseprv: ACJVW…7G7NA
    noisepub: J2W5C…SZM6Q
  }
  neigh: {
    self: {
      id: HFTEI…SITTA
      exchpub: RG2SF…7JEYA
      signpub: ASKTA…EFVSQ
      noisepub: J2W5C…SZM6Q
    }
  }
}

The location of this file should be defined in your NixOS configuration at programs.nncp.secrets:

{
  programs.nncp = {
    enable = true;
    secrets = [ "/etc/secrets/nncp.hjson" ];
  };
}

In this example the secret keys are stored outside the Nix store an we will add public keys for neighboring nodes in the NixOS configuration.

{
  programs.nncp = {
    enable = true;
    secrets = [ "/etc/secrets/nncp.hjson" ];
    neigh = {
      carol = {
        # information that Carol has given us about her "self".
        id = "D6BOO…YTYWQ";
        exchpub = "V4WJ6…4VA3Q";
        signpub = "NZLTN…HCGOA";
        noisepub = "UNL2J…7FRDA";
        # We can connect directly to Carol over network.
        addr = {
          lan = "[fe80::1234%igb0]:5400";
          internet = "carol.example.com:3389";
          proxied = "|ssh remote.host nncp-daemon -ucspi";
        };
      };
      bob = {
        # information that Bob has given us about his "self".
        id = "3I3HC…F4P4Q";
        exchpub = "7VJN7…BWUTQ";
        signpub = "E6XSC…5VYRA";
        noisepub = "TAKXG…Z6MZQ";
        # We cannot connect to Bob but we can relay packets to him thru Carol.
        via = [ "carol" ];
      };
    };
  };
}

Callers and Daemons

The NNCP caller and daemon can be enabled for NixOS using the options services.nncp.caller and services.nncp.daemon.

{
  services.nncp = let
    attrs = {
      enable = true;
      extraArgs = [ "-autotoss" ];
    };
  in {
    caller = attrs;
    daemon = attrs;
  };
}

Copying Nix store paths

NNCP can be use to transport the closures of Nix store paths between machines.

NNCP config:

{
  programs.nncp.settings.neigh.${NODE}.exec.nix-store-import = "nix-store --import";
}

Export command:

$ nix-store --export ./result | nncp-exec "$NODE" nix-store-import

Email

NNCP is an ideal transport for secure email.

Receiving email

# NixOS module for Alice that allows reception of mail from Bob and Carol as well as mail relayed thru her mailserver.
{
  config,
  lib,
  pkgs,
  ...
}:

{
  programs.nncp.settings.neigh =
    let
      mailer.exec.sendmail = [
        "/run/wrappers/bin/sendmail" # Pipe mail into the system sendmail.
        "alice"                      # Redirect messages to the "alice" user.
      ];
    in
    {
      bob = mailer;
      carol = mailer;
      mailserver = mailer; # This is Alice's mailserver, described later. 
    };

  # Use opensmtpd for the system sendmail command.
  services.opensmtpd = {
    enable = true;
    setSendmail = true;
    serverConfiguration = ''
      listen on lo

      # Deliver mail into Alice's home directory.
      action "inbox" maildir "%{user.directory}/mail"

      match for local action "inbox"
    '';
  };

}

Sending mail

To send mail alice configures her client to relay mail to her mailserver by using nncp-exec as if it were sendmail.

nncp-exec -noprogress mailserver sendmail -f alice@example.org -t

Relaying email

To send mail to domains via STMP a relay is required that implements the SPF standard. Configuring SPF and other DNS based standards is not described here.

# NixOS module for Alice's STMP relay server.
{ config, lib, ... }:

let
  domain = "example.org";
  fqdn = "example.org";
  certCfg = config.security.acme.certs.${fqdn};
  certDir = certCfg.directory;
  smtpdCertDir = "/var/lib/smtp";
in
{
  # Allow incoming SMTP connections.
  networking.firewall.allowedTCPPorts = [
    25
    465
  ];

  # Receive mail from Alice's NNCP node and pipe it into sendmail unaltered.
  programs.nncp.settings.neigh.alice.exec.sendmail = [ 
    "/run/wrappers/bin/sendmail"
  ];

  # Get a certificate for SMTP from ACME.
  security.acme = {
    acceptTerms = true;
    certs.${fqdn} = {
      email = "admin@${domain}";
      reloadServices = [ "opensmtpd.service" ];
      postRun = ''
        mkdir -p ${smtpdCertDir}
        cp ${certDir}/cert.pem ${smtpdCertDir}/cert
        cp ${certDir}/key.pem ${smtpdCertDir}/key
        chown 0:0 ${smtpdCertDir}/*
      '';
    };
  };

  # Wrap nncp-exec so that the unpriviledged
  # smtpd can produce outgoing NNCP packets.
  security.wrappers.nncp-exec = {
    setuid = true;
    owner = "root";
    group = "uucp";
    source = "${config.programs.nncp.package}/bin/nncp-exec";
  };  

  # Configure an smtpd.
  services.opensmtpd = {
    enable = true;
    setSendmail = true; # Create the sendmail command for incoming NNCP mails.
    serverConfiguration = ''
      # Use the ACME certificate.
      pki ${fqdn} cert "${smtpdCertDir}/cert"
      pki ${fqdn} key "${smtpdCertDir}/key"

      # Configure SMTP listeners.
      # Authentication is by domain only, there are no logins here.
      listen on lo
      listen on eth0 smtps pki ${fqdn} # Classical SMTP.
      listen on eth0 tls pki ${fqdn}   # Listen with TLS.
      listen on tun0 mask-src          # Listen on a tunnel interface but
                                       # omit the details from headers.

      # Configure a NNCP Mail Delivery Agent (MDA) for local users.
      action "nncp" mda "/run/wrappers/bin/nncp-exec -quiet %{dest.user:strip} sendmail"

      # Configure SMTP relaying to external domains.
      action "relay" relay tls helo ${domain}

      # Rules for mail received at this smtpd.
      match from any for domain "${domain}" action "nncp"
      match from local for any action "relay"
    '';
  };
}