NNCP: Difference between revisions
 Add configuration example for receiving email.  | 
				 Add configurations examples for relaying email.  | 
				||
| Line 59: | Line 59: | ||
     secrets = [ "/etc/secrets/nncp.hjson" ];  |      secrets = [ "/etc/secrets/nncp.hjson" ];  | ||
     neigh = {  |      neigh = {  | ||
       carol = {  | |||
         # information that   |          # information that Carol has given us about her "self".  | ||
         id = "D6BOO…YTYWQ";  |          id = "D6BOO…YTYWQ";  | ||
         exchpub = "V4WJ6…4VA3Q";  |          exchpub = "V4WJ6…4VA3Q";  | ||
         signpub = "NZLTN…HCGOA";  |          signpub = "NZLTN…HCGOA";  | ||
         noisepub = "UNL2J…7FRDA";  |          noisepub = "UNL2J…7FRDA";  | ||
         # We can connect directly to   |          # We can connect directly to Carol over network.  | ||
         addr = {  |          addr = {  | ||
           lan = "[fe80::1234%igb0]:5400";  |            lan = "[fe80::1234%igb0]:5400";  | ||
           internet = "  |            internet = "carol.example.com:3389";  | ||
           proxied = "|ssh remote.host nncp-daemon -ucspi";  |            proxied = "|ssh remote.host nncp-daemon -ucspi";  | ||
         };  |          };  | ||
| Line 78: | Line 78: | ||
         signpub = "E6XSC…5VYRA";  |          signpub = "E6XSC…5VYRA";  | ||
         noisepub = "TAKXG…Z6MZQ";  |          noisepub = "TAKXG…Z6MZQ";  | ||
         # We   |          # We cannot connect to Bob but we can relay packets to him thru Carol.  | ||
         via = [ "  |          via = [ "carol" ];  | ||
       };  |        };  | ||
     };  |      };  | ||
| Line 104: | Line 104: | ||
</syntaxhighlight>  | </syntaxhighlight>  | ||
==   | == Copying Nix store paths ==  | ||
NNCP can be use to transport the closures of Nix store paths between machines.  | |||
NNCP config:  | NNCP config:  | ||
| Line 120: | Line 120: | ||
</syntaxhighlight>  | </syntaxhighlight>  | ||
=== Receiving   | == Email ==  | ||
NNCP is an ideal transport for secure email.  | |||
=== Receiving email ===  | |||
<syntaxhighlight lang="nix">  | |||
# 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"  | |||
    '';  | |||
  };  | |||
}  | |||
</syntaxhighlight>  | |||
=== Sending mail ===  | |||
To send mail alice configures her client send mail to her mailserver by using nncp-exec as if it were <code>sendmail</code>.  | |||
<code>  | |||
nncp-exec -noprogress mailserver sendmail -f alice@example.org -t  | |||
</code>  | |||
=== Relaying email ===  | |||
To send mail to domains via STMP a relay is required that implements the  [[wikipedia:Sender Policy Framework|SPF]] standard. Configuring SPF and other DNS based standards is not described here.  | |||
<syntaxhighlight lang="nix">  | <syntaxhighlight lang="nix">  | ||
# NixOS module   | # NixOS module for Alice's STMP relay server.  | ||
{ config, lib, ... }:  | |||
let  | let  | ||
   domain = "example.org";  | |||
  fqdn = "example.org";  | |||
  certCfg = config.security.acme.certs.${fqdn};  | |||
  certDir = certCfg.directory;  | |||
  smtpdCertDir = "/var/lib/smtp";  | |||
in  | in  | ||
{  | {  | ||
   programs.nncp.settings.neigh = {  |   # 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 %{rcpt.user} 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"  | |||
    '';  | |||
   };  |    };  | ||
}  | }  | ||
</syntaxhighlight>  | </syntaxhighlight>  | ||
Revision as of 09:52, 29 April 2025
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.
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
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 send 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 %{rcpt.user} 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"
    '';
  };
}