Deployment guide

spiped

Overview

spiped (pronounced "ess pipe dee") is a secure pipe daemon. It creates a symmetrically encrypted and authenticated pipe between two socket addresses, so you can connect to one address and transparently reach another across an untrusted network. It is similar in spirit to an SSH port forward, but it is a purpose-built daemon rather than a side effect of a shell session. It authenticates with a pre-shared symmetric key instead of SSH credentials.

This SecureAnyCloud image runs spiped on Ubuntu 24.04 on Azure. It builds spiped 1.6.4 from the official signed upstream release tarball (www.tarsnap.com/spiped/spiped-1.6.4.tgz) and verifies the tarball against the upstream published SHA-256 during the build. The exact version is therefore reproducible, and the running VM uses no third-party package repository. Both daemons run under systemd as the unprivileged spiped user, and unattended security upgrades keep the base OS patched.

A pipe has two ends, and this image ships both. You can verify the pipe the moment the VM boots, with no second machine to set up first.

  • spiped.service is the server side. It accepts encrypted connections on TCP 8022 and forwards the decrypted stream to the service you are protecting.
  • spiped-client.service is the client side. It accepts plaintext connections on 127.0.0.1:9022 and forwards them encrypted to the server side.

As shipped, the client side loops back to the server side on the same VM. Connecting to 127.0.0.1:9022 therefore exercises the complete encrypt-then-decrypt path and reaches the protected service. In a real deployment, you keep the server side on this VM and run the client side on your own machine. The shipped client is both a working end-to-end proof and the template you copy for that remote endpoint.

No key material ships in the image. spiped uses a symmetric key, so whoever holds the key can both open and impersonate the pipe. A key shipped inside the image would be a shared secret on every customer VM. On the first boot of every VM, a fresh 256 bit key is generated from the kernel random source. The key is unique to that instance and is recorded in /root/spiped-credentials.txt (mode 0600, root only). Both daemons are held behind a bootstrap gate until that has happened, so neither can start with missing or shared key material.

The key acts as a credential gate, not just as encryption. spiped authenticates during its handshake. A peer that cannot prove it holds this VM's key is dropped before any byte reaches the protected service, so a plaintext client or a client with the wrong key never gets through.

What you get:

  • spiped 1.6.4 built from the pinned, checksum-verified upstream source, with both the daemon and the spipe one-shot client, run under systemd as the unprivileged spiped user
  • An encrypted pipe endpoint on TCP 8022 and a local plaintext endpoint on 127.0.0.1:9022, wired together and to this VM's SSH service as a working demonstration that you repoint at your own service
  • A per instance 256 bit key generated on first boot, with a fingerprint and a transportable copy in /root/spiped-credentials.txt (0600)
  • A hardened baseline: the plaintext endpoint bound to loopback only, an unprivileged runtime user, and CAP_NET_BIND_SERVICE granted so you can move the encrypted endpoint to a privileged port such as 443 without editing the unit
  • One short, commented file at /etc/spiped/spiped.conf that drives everything, so you retarget the pipe without touching systemd units

Key facts

Item Value

Platform Ubuntu 24.04 LTS on Azure

Default administrative user azureuser

Encrypted pipe endpoint TCP 8022 (all interfaces)

Local plaintext endpoint TCP 9022 (loopback only)

Key file /etc/spiped/spiped.key (0640 root:spiped)

Credentials file /root/spiped-credentials.txt (0600 root:root)

Configuration /etc/spiped/spiped.conf

Prerequisites

Before you begin, make sure you have the following:

  • An active Azure subscription, an SSH public key, and a VNet and subnet in the target region
  • A subscription to the spiped listing on Azure Marketplace
  • Network Security Group rules that allow TCP 22 for administration and TCP 8022 from the clients that will use the pipe
  • A way to copy a small secret, over a channel you already trust, to the machine that will run the client side

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM). Encryption throughput scales with CPU, so busy pipes should use Standard_D2s_v5 or larger.

1

Deploy from the Azure Portal

Search Azure Marketplace for spiped, select the SecureAnyCloud publisher, and select Create.

Configure the Network Security Group to allow TCP 8022 from your client networks and TCP 22 for administration. Leave the plaintext port 9022 closed. It is bound to loopback and is not meant to be reachable from the network.

2

Deploy from the Azure CLI

RG="pipe-prod"; LOCATION="eastus"; VM_NAME="pipe1" IMAGE_URN="sac1647283583153:spiped-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION"
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:spiped-ubuntu-24-04:default:latest
az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$IMAGE_URN" \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values "$SSH_KEY" \
  --public-ip-sku Standard
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 8022 --priority 1001
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 22 --priority 1002
3

First boot

On first boot, the image generates this VM's 256 bit key and writes it to /etc/spiped/spiped.key. It records the key's fingerprint and a transportable copy in /root/spiped-credentials.txt, and only then releases the two spiped units to start. This completes within a few seconds.

SSH in as azureuser and read the details:

sudo cat /root/spiped-credentials.txt

The file lists the key file, its SHA-256 fingerprint, a base64 copy you can carry to the client machine, and the two endpoints. Keep the file readable by root only. It is the one place the key exists in a form you can copy.

4

Confirm both ends of the pipe are running

Both units run under systemd as the unprivileged spiped user. The ss output in the following commands confirms that the encrypted endpoint listens on all interfaces and the plaintext endpoint is bound to loopback only.

systemctl is-active spiped.service
systemctl is-active spiped-client.service ps -o user= -C spiped | sort -u | tr -d ' ' ss -tlnp | grep -E ':8022 |:9022 '

Expected output:

active active spiped LISTEN 0 10 127.0.0.1:9022 0.0.0.0:* users:(("spiped",pid=3406,fd=3)) LISTEN 0 10 0.0.0.0:8022 0.0.0.0:* users:(("spiped",pid=3405,fd=3))

Confirm the build version:

timeout 10 spiped -v </dev/null 2>&1
spiped 1.6.4

Note the </dev/null redirect. Both spiped and spipe read standard input as part of normal operation, so redirect it whenever you only probe the binary.

5

Prove the encrypted round trip

This check runs entirely on this one VM. The client side encrypts a connection to the local plaintext port and carries it to the server side, which decrypts it and hands it to the protected service. In the shipped configuration, the protected service is this VM's own SSH daemon, because it is always present. Its greeting arriving back through the pipe proves that the whole path works.

echo -n 'through the pipe : '; timeout 8 bash -c 'exec 3<>/dev/tcp/127.0.0.1/9022; head -1 <&3' 2>/dev/null || echo 'no response'

Expected output:

through the pipe : SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.18

The service at the far end of the pipe produced that greeting. A real byte stream passed through encryption and decryption, which shows the pipe is carrying traffic rather than merely holding a port open.

6

The pipe requires this VM's key

The encrypted endpoint on 8022 authenticates its peer during the spiped handshake. A client that cannot prove it holds this VM's key is dropped before it reaches the protected service.

Run the following commands to test three cases. They run on the VM against loopback. From a remote client, replace 127.0.0.1 with the VM address.

WRONGKEY=$(mktemp); dd if=/dev/urandom bs=32 count=1 of="$WRONGKEY" status=none echo -n 'plaintext client : '; timeout 8 bash -c 'exec 3<>/dev/tcp/127.0.0.1/8022; head -c 64 <&3' 2>/dev/null | grep -aq 'SSH-2.0' && echo 'reached the protected service' || echo 'never reached the protected service' echo -n 'wrong key : '; { sleep 3; } | timeout 10 spipe -t '[127.0.0.1]:8022' -k "$WRONGKEY" 2>/dev/null | head -1 | grep . || echo 'never reached the protected service' echo -n 'this VM key : '; { sleep 3; } | timeout 10
sudo spipe -t '[127.0.0.1]:8022' -k /etc/spiped/spiped.key 2>/dev/null | head -1 | grep . || echo 'never reached the protected service' rm -f "$WRONGKEY"

Expected output:

plaintext client : never reached the protected service wrong key : never reached the protected service this VM key : SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.18

A plaintext client does receive a few bytes, because the spiped server opens its handshake by sending a random value. The client gets no further. Without the key, it cannot compute the authentication code the server expects, so the connection is dropped and the protected service is never touched.

spipe is the one-shot client shipped alongside the daemon. It speaks the same protocol but pipes standard input and output instead of listening on a port, which makes it convenient for checks like these.

7

Wire up the second endpoint on your own machine

In production, the server side stays on this VM and the client side runs wherever your traffic originates. Install spiped on that machine, recreate the key file from the base64 copy in the credentials file, and run the client side pointing at this VM.

On the VM, print the two values you need:

sudo grep -E '^KEY_SHA256=|^KEY_B64=' /root/spiped-credentials.txt

On your own machine, recreate the key and confirm that it matches. Replace <vm-ip> with this VM's address, and paste the base64 value from the previous command.

echo '<paste the KEY_B64 value here>' | base64 -d > spiped.key chmod 0600 spiped.key sha256sum spiped.key spiped -e -s '[127.0.0.1]:8022' -t '[<vm-ip>]:8022' -k spiped.key & ssh -p 8022 azureuser@127.0.0.1

The fingerprint printed by sha256sum must match the KEY_SHA256 value from the VM. The last two commands start the client side and then connect to 127.0.0.1:8022 on your own machine. This reaches the protected service on the VM, with everything between the two hosts encrypted and authenticated.

Transfer the key over a channel you already trust, such as an existing SSH session or a secrets manager. Anyone who obtains the key can both use and impersonate the pipe.

8

Review the configuration

spiped takes all of its settings as command-line arguments. This image supplies them from one short file, so you never have to edit a systemd unit. Print the active settings:

grep -vE '^\s*#|^\s*$' /etc/spiped/spiped.conf

Expected output:

SPIPED_SERVER_SOURCE=[0.0.0.0]:8022 SPIPED_SERVER_TARGET=[127.0.0.1]:22 SPIPED_CLIENT_SOURCE=[127.0.0.1]:9022 SPIPED_CLIENT_TARGET=[127.0.0.1]:8022 SPIPED_KEY=/etc/spiped/spiped.key SPIPED_MAX_CONNS=100 SPIPED_TIMEOUT=5

SPIPED_SERVER_TARGET is the service you are protecting. SPIPED_CLIENT_TARGET is where the client side sends encrypted traffic. Addresses use spiped's own format: [ip.v4.ad.dr]:port, host.name:port, or an absolute path to a UNIX socket.

9

Point the pipe at your own service

To protect a service other than SSH, change SPIPED_SERVER_TARGET to its address and restart the server side. The backend can be on this VM or anywhere else your VM can reach.

For example, to protect a PostgreSQL server elsewhere in your virtual network, replace <private-ip> with that server's address:

sudo sed -i 's|^SPIPED_SERVER_TARGET=.*|SPIPED_SERVER_TARGET=[<private-ip>]:5432|' /etc/spiped/spiped.conf
sudo systemctl restart spiped

Clients then run the client side locally and connect to their own loopback port as if PostgreSQL were running there. Neither side needs to know it is talking through an encrypted pipe. The database keeps speaking its own plaintext protocol, and the untrusted network in between only carries authenticated ciphertext.

To run several pipes on one VM, copy /etc/systemd/system/spiped.service to a second unit with its own source port and target. Give the new unit its own EnvironmentFile.

10

Confirm no key material ships in the image

No default, shared or built-in key exists. The credentials file is 0600 root:root. The key file is 0640 root:spiped, so only root and the service account can read it. The directory itself is 0750.

sudo stat -c 'file: %n perms: %a owner: %U:%G' /root/spiped-credentials.txt /etc/spiped/spiped.key /etc/spiped
sudo stat -c '%s bytes' /etc/spiped/spiped.key
sudo sed -E 's/^(KEY_SHA256=.{12}).*/\1********************/; s/^(KEY_B64=.{10}).*/\1**************************/' /root/spiped-credentials.txt | grep -E '^KEY_FILE=|^KEY_SHA256=|^KEY_B64='

Expected output:

file: /root/spiped-credentials.txt perms: 600 owner: root:root file: /etc/spiped/spiped.key perms: 640 owner: root:spiped file: /etc/spiped perms: 750 owner: root:spiped 32 bytes KEY_FILE=/etc/spiped/spiped.key KEY_SHA256=a79dbcb3c8c5******************** KEY_B64=HQZAO+M2uo**************************

The key is exactly 32 bytes, which is the 256 bits of entropy upstream requires. It was read from the kernel random source on this instance's first boot. The fingerprint above differs on your VM, because every VM has its own key.

Server Components

Component Version Purpose

spiped 1.6.4 Secure pipe daemon, both server and client sides

spipe 1.6.4 One shot spiped protocol client for scripts and checks

OpenSSL 3.0.13 Cryptographic primitives spiped links against

systemd 255 Service supervision for both pipe ends and first boot

Ubuntu 24.04 LTS Base operating system

Filesystem Layout

Path Size Purpose

/ 29 GB Root filesystem

/boot 881 MB Operating system kernel files

/boot/efi 105 MB UEFI boot partition (Gen2 Hyper V)

/mnt varies Azure temporary resource disk

Key directories

Path Purpose

/usr/local/bin/spiped The spiped daemon binary

/usr/local/bin/spipe The spipe one shot client binary

/etc/spiped/ Configuration and key material (0750 root:spiped)

/etc/spiped/spiped.conf Endpoint and tuning settings for both units

/etc/spiped/spiped.key This instance 256 bit key (0640 root:spiped)

/root/spiped-credentials.txt Key fingerprint and transportable copy (0600 root:root)

/usr/local/share/doc/spiped/ Upstream COPYRIGHT and README shipped with the binaries

/usr/local/share/man/man1/ spiped.1 and spipe.1 manual pages

Managing the service

systemctl status spiped.service --no-pager
systemctl status spiped-client.service --no-pager

Restart the server side after you change SPIPED_SERVER_SOURCE, SPIPED_SERVER_TARGET or the key. Restart the client side after you change SPIPED_CLIENT_SOURCE or SPIPED_CLIENT_TARGET.

spiped handles SIGTERM gracefully. It stops accepting new connections and exits once existing ones have finished, so a restart does not cut live sessions short.

Once your own client endpoint is in place, you can drop the loopback demonstration. Run sudo systemctl disable --now spiped-client.service to stop the client side and keep it stopped across reboots. The server side, which protects your service, keeps running untouched. To re-enable the client side later, run sudo systemctl enable --now spiped-client.service.

Scripts and Log Files

Path Purpose

/usr/local/sbin/spiped-firstboot.sh Generates this instance key on first boot

/var/log/spiped-firstboot.log First boot key generation log

journalctl -u spiped.service Server side daemon log

journalctl -u spiped-client.service Client side daemon log

journalctl -u spiped-firstboot.service First boot service log

On Startup

spiped-firstboot.service runs once, on the first boot of a new VM. It generates the per-instance key, writes the credentials file, and then creates the marker /var/lib/sac/data/spiped-bootstrap-ready. Both spiped units carry ConditionPathExists on that marker, so they cannot start until the key exists.

After the first boot, the service marks itself complete with a sentinel at /var/lib/sac/data/spiped-firstboot.done and does not run again. The two daemons start normally on every subsequent boot.

systemctl is-active spiped-firstboot.service
sudo journalctl -u spiped-firstboot.service --no-pager -n 15

Troubleshooting

The pipe returns nothing at all. Check that both units are active and that the backend named in SPIPED_SERVER_TARGET is listening. A pipe to a dead backend completes its handshake and then has nothing to forward.

systemctl is-active spiped.service spiped-client.service
sudo ss -tlnp | grep -E ':8022 |:9022 |:22 '
sudo journalctl -u spiped.service --no-pager -n 20

A remote client cannot connect. Confirm that TCP 8022 is open in the Network Security Group for the client's network, and that the client uses this VM's address rather than loopback. Port 9022 is bound to loopback deliberately and never answers from off the VM.

A remote client connects but is dropped. The two ends do not hold the same key. Compare fingerprints: sha256sum of the client's key file must equal KEY_SHA256 in /root/spiped-credentials.txt on the VM. A key copied through an editor or a chat window is often corrupted by trailing whitespace or newline translation. For that reason, the credentials file carries a base64 copy and a fingerprint to check it against.

A unit refuses to start after a first boot problem. Both units are gated on the bootstrap marker. If key generation failed, the marker is absent and the daemons stay stopped by design rather than start without a key.

ls -l /var/lib/sac/data/spiped-bootstrap-ready /var/lib/sac/data/spiped-firstboot.done
sudo cat /var/log/spiped-firstboot.log

Connections use more bandwidth than expected. spiped sends data in fixed 1024 byte packets and pads anything smaller, so interactive traffic uses more bandwidth than the raw byte count suggests. This is a deliberate design choice that limits what an observer can infer from packet sizes. It does not hide timing, so spiped protects the content of an interactive session but not its typing rhythm.

Security Recommendations

Restrict the Network Security Group. Allow TCP 8022 only from the client networks that need the pipe, and allow TCP 22 for administration only.

Treat the key as a credential. The key is symmetric, so anyone who holds it can both open the pipe and impersonate the server. Transfer it only over a channel you already trust, and never commit it to source control.

Rotate the key by replacing the file on both ends at the same time. Write 32 fresh bytes from the kernel random source, distribute the new file, and restart the daemons at both ends. There is no in-band rotation, so both ends must change together.

Keep the plaintext endpoint on loopback. Exposing port 9022 to the network would publish the very traffic the pipe exists to protect.

Do not reuse one key across unrelated pipes. Upstream also asks that a single key file is not used for more than 2^64 connections. This is far beyond normal use but worth knowing.

Prefer perfect forward secrecy. The shipped configuration uses spiped's default full handshake, which provides it. Do not add the fast handshake option unless you have measured a genuine CPU problem and accept the loss.

Keep the OS patched. Unattended security upgrades remain enabled on the running VM.

Support and Licensing

spiped is developed by Colin Percival and Tarsnap Backup Inc. and distributed under the BSD 2 Clause License. The one external file it carries, libcperciva/external/queue/queue.h, is the BSD-licensed queue implementation from the University of California Regents.

This SecureAnyCloud image builds the unmodified upstream spiped 1.6.4 source, and the upstream COPYRIGHT file ships in the image at /usr/local/share/doc/spiped/COPYRIGHT. SecureAnyCloud provides the packaging, the secure default configuration, the per-instance key automation, security patching, and 24/7 support with a guaranteed 24 hour response SLA.

SecureAnyCloud is not affiliated with or endorsed by the spiped project or Tarsnap Backup Inc. spiped and Tarsnap are marks of their respective owners and are used here only to identify the software.

Deploy on Azure

Find spiped Secure Pipe Daemon on Ubuntu 24.04 LTS on the Azure Marketplace, published by SecureAnyCloud. Deploy from the Portal or the Azure CLI as shown above.

Need Help?

Email sales@secureanycloud.com for deployment help, configuration questions or licensing enquiries.

Deploy on Azure

Launch spiped 1.6.4 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure