| Title: Asynchronous secure file transfer with nncp | |
| Author: Solène | |
| Date: 04 October 2024 | |
| Tags: privacy security network unix | |
| Description: In this blog post, you will learn about the software nncp | |
| and how to use it to exchange encrypted data with peers | |
| # Introduction | |
| nncp (node to node copy) is a software to securely exchange data | |
| between peers. Is it command line only, it is written in Go and | |
| compiles on Linux and BSD systems (although it is only packaged for | |
| FreeBSD in BSDs). | |
| The website will do a better job than me to talk about the numerous | |
| features, but I will do my best to explain what you can do with it and | |
| how to use it. | |
| nncp official project website | |
| # Explanations | |
| nncp is a suite of tools to asynchronously exchange data between peers, | |
| using zero knowledge encryption. Once peers have exchanged their | |
| public keys, they are able to encrypt data to send to this peer, this | |
| is nothing really new to be honest, but there is a twist. | |
| * a peer can directly connect to another using TCP, you can even | |
| configure different addresses like a tor onion or I2P host and use the | |
| one you want | |
| * a peer can connect to another using ssh | |
| * a peer can generate plain files that will be carried over USB, | |
| network storage, synchronization software, whatever, to be consumed by | |
| a peer. Files can be split in chunks of arbitrary size in order to | |
| prevent anyone snooping from figuring how many files are exchanged or | |
| their name (hence zero knowledge). | |
| * a peer can generate data to burn on a CD or tape (it is working as a | |
| stream of data instead of plain files) | |
| * a peer can be reachable through another relay peer | |
| * when a peer receives files, nncp generates ACK files | |
| (acknowledgement) that will tell you they correctly received it | |
| * a peer can request files and/or trigger pre-configured commands you | |
| expose to this peer | |
| * a peer can send emails with nncp (requires a specific setup on the | |
| email server) | |
| * data transfer can be interrupted and resumed | |
| What is cool with nncp is that files you receive are unpacked in a | |
| given directory and their integrity is verified. This is sometimes | |
| more practical than a network share in which you are never sure when | |
| you can move / rename / modify / delete the file that was transferred | |
| to you. | |
| I identified a few "realistic" use cases with nncp: | |
| * exchange files between air gap environments (I tried to exchange | |
| files over sound or QR codes, I found no reliable open source solution) | |
| * secure file exchange over physical medium with delivery notification | |
| (the medium needs to do a round-trip for the notification) | |
| * start a torrent download remotely, prepare the file to send back once | |
| downloaded, retrieve the file at your own pace | |
| * reliable data transfer over poor connections (although I am not sure | |
| if it beats kermit at this task :D ) | |
| * "simple" file exchange between computers / people over network | |
| This let a lot of room for other imaginative use cases. | |
| # Real world example: Syncthing gateway | |
| My preferred workflow with nncp that I am currently using is a group of | |
| three syncthing servers. | |
| Each syncthing server is running on a different computer, the location | |
| does not really matter. There is a single share between these | |
| syncthing instances. | |
| The servers where syncthing are running have incoming and outgoing | |
| directories exposed over a NFS / SMB share, with a directory named | |
| after each peer in both directories. Deposing a file in the "outgoing" | |
| directory of a peer will make nncp to prepare the file for this peer, | |
| put it into the syncthing share and let it share, the file is consumed | |
| in the process. | |
| In the same vein, in the incoming directory, new files are unpacked in | |
| the incoming directory of emitting peer on the receiver server running | |
| syncthing. | |
| Why is it cool? You can just drop a file in the peer you want to send | |
| to, it disappears locally and magically appears on the remote side. If | |
| something wrong happens, due to ACK, you can verify if the file was | |
| delivered and unpacked. With three shares, you can almost have two | |
| connected at the same time. | |
| It is a pretty good file deposit that requires no knowledge to use. | |
| This could be implemented with pure syncthing, however you would have | |
| to: | |
| * for each peer, configure a one-way directory share in syncthing for | |
| each other peer to upload data to | |
| * for each peer, configure a one-way directory share in syncthing for | |
| each other peer to receive data from | |
| * for each peer, configure an encrypted share to relay all one way | |
| share from other peers | |
| This does not scale well. | |
| Side note, I am using syncthing because it is fun and requires no | |
| infrastructure. But actually, a webdav filesystem, a Nextcloud drive | |
| or anything to share data over the network would work just fine. | |
| # Setup | |
| ## Configuration file and private keys | |
| On each peer, you have to generate a configuration file with its | |
| private keys. The default path for the configuration file is | |
| `/etc/nncp.hjson` but nothing prevents you from storing this file | |
| anywhere, you will have to use the parameter `-cfg /path/to/config` | |
| file in that case. | |
| Generate the file like this: | |
| ``` | |
| nncp-cfgnew > /etc/nncp.hjson | |
| ``` | |
| The file contains comments, this is helpful if you want to see how the | |
| file is structured and existing options. Never share the private keys | |
| of this file! | |
| I recommend checking the spool and log paths, and decide which user | |
| should use nncp. For instance, you can use `/var/spool/nncp` to store | |
| nncp data (waiting to be delivered or unpacked) and the log file, and | |
| make your user the owner of this directory. | |
| ## Public keys | |
| Now, generate the public keys (they are just derived from the private | |
| keys generated earlier) to share with your peers, there is a command | |
| for this that will read the private keys and output the public keys in | |
| a format ready to put in the nncp.hjson file of recipients. | |
| ``` | |
| nncp-cfgmin > my-peer-name.pub | |
| ``` | |
| You can share the generated file with anyone, this will allow them to | |
| send you files. The peer name of your system is "self", you can rename | |
| it, it is just an identifier. | |
| ## Import public keys | |
| When import public keys, you just need to add the content generated by | |
| the command `nncp-cfgmin` of a peer in your nncp configuration file. | |
| Just copy / paste the content in the `neigh` structure within the | |
| configuration file, just make sure to rename "self" by the identifier | |
| you want to give to this peer. | |
| If you want to receive data from this peer, make sure to add an | |
| attribute line `incoming: "/path/to/incoming/data"` for that peer, | |
| otherwise you will not be able to unpack received file. | |
| # Usage | |
| Now you have peers who exchanged keys, they are able to send data to | |
| each other. nncp is a collection of tools, let's see the most common | |
| and what they do: | |
| * nncp-file: add a file in the spool to deliver to a peer | |
| * nncp-toss: unpack incoming data (files, commands, file request, | |
| emails) and generate ack | |
| * nncp-reass: reassemble files that were split in smaller parts | |
| * nncp-exec: trigger a pre-configured command on the remote peer, stdin | |
| data will be passed as the command parameters. Let's say a peer offers | |
| a "wget" service, you can use `echo "https://some-domain/uri/" | | |
| nncp-exec peername wget` to trigger a remote wget. | |
| If you use the client / server model over TCP, you will also use: | |
| * nncp-daemon: the daemon waiting for connections | |
| * nncp-caller: a daemon occasionally triggering client connections (it | |
| works like a crontab) | |
| * nncp-call: trigger a client connection to a peer | |
| If you use asynchronous file transfers, you will use: | |
| * nncp-xfer: generates to / consumes files from a directory for async | |
| transfer | |
| # Workflow (how to use) | |
| ## Sending files | |
| For sending files, just use `nncp-file file-path peername:`, the file | |
| name will be used when unpacked, but you can also give the filename you | |
| want to give once unpacked. | |
| A directory could be used as a parameter instead of a file, it will be | |
| stored automatically in a .tar file for delivery. | |
| Finally, you can send a stream of data using nncp-file stdin, but you | |
| have to give a name to the resulting file. | |
| ## Sync and file unpacking | |
| This was not really clear from the documentation, so here it is how to | |
| best use nncp when exchanging files using plain files, the destination | |
| is `/mnt/nncp` in my examples (it can be an external drive, a syncthing | |
| share, a NFS mount...): | |
| When you want to sync, always use this scheme: | |
| 1. `nncp-xfer -rx /mnt/nncp` | |
| 2. `nncp-toss -gen-ack` | |
| 3. `nncp-xfer -keep -tx -mkdir /mnt/nncp` | |
| 4. `nncp-rm -all -ack` | |
| This receives files using `nncp-xfer -rx`, the files are stored in nncp | |
| spool directory. Then, with `nncp-toss -gen-ack`, the files are | |
| unpacked to the "incoming" directory of each peer who sent files, and | |
| ACK are generated (older versions of `nncp-toss` does not handle ack, | |
| you need to generate ack befores and remove them after tx, with | |
| `nncp-ack -all 4>acks` and `nncp-rm -all -pkt < acks`). | |
| `nncp-xfer -tx` will put in the directory the data you want to send to | |
| peers, and also the ack files generated by the rx which happened | |
| before. The `-keep` flag is crucial here if you want to make use of | |
| ACK, with `-keep`, the sent data are kept in the pool until you receive | |
| the ACK for them, otherwise the data are removed from the spool and | |
| will not be retransmited if the files were not received. Finally, | |
| `nncp-rm` will delete all ACK files so you will not transmit them | |
| again. | |
| # Explanations about ACK | |
| From my experience and documentation reading, there are three cases | |
| with the spool and ACK: | |
| * the shared drive is missing the files you sent (that are still in | |
| pool), and you received no ACK, the next time you run `nncp-xfer`, the | |
| files will be transmitted again | |
| * when you receive ACK files for files in spool, they are deleted from | |
| the spool | |
| * when you do not use `-keep` when sending files with `nncp-xfer`, the | |
| files will not be stored in the spool so you will not be able to know | |
| what to retransmit if ACK are missing | |
| ACKs do not clean up themselves, you need to use `nncp-rm`. It took me | |
| a while to figure this, my nodes were sending ACKs to each other | |
| repeatedly. | |
| # Conclusion | |
| I really like nncp as it allows me to securely transfer files between | |
| my computers without having to care if they are online. Rsync is not | |
| always possible because both the sender and receiver need to be up at | |
| the same time (and reachable correctly). | |
| The way files are delivered is also practical for me, as I already | |
| shared above, files are unpacked in a defined directory by peer, | |
| instead of remembering I moved something in a shared drive. This | |
| removes the doubt about files being in a shared drive: why is it there? | |
| Why did I put it there? What was its destination?? | |
| I played with various S3 storage to exchange nncp data, but this is for | |
| another blog post :-) | |
| # Going further | |
| There are more features in nncp, I did not play with all of them. | |
| You can define "areas" in parallel of using peers, you can use emails | |
| notifications when a remote receives data from you to have a | |
| confirmation, requesting remote files etc... It is all in the | |
| documentation. | |
| I have the idea to use nncp on a SMTP server to store encrypted | |
| incoming emails until I retrieve them (I am still working at improving | |
| the security of email storage), stay tuned :) |