Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ lexopt = "0.3"
regex-lite = "0.1"
listenfd = "1.0.2"
tungstenite = "0.29"
rustix = { version = "1.0", features = ["event", "process"] }
rustix = { version = "1.0", features = ["event", "net", "process"] }
# TODO: switch back to crates.io once tokio-vsock releases a version with
# From<OwnedFd> support (https://github.com/rust-vsock/tokio-vsock/pull/72)
tokio-vsock = { git = "https://github.com/rust-vsock/tokio-vsock.git", rev = "c6225086", features = ["axum08"] }
vsock = "0.5"
signal-hook = "0.4"
socket2 = "0.6"
# using opeenssl here to keep size small, rusttls adds ~1.8MB binary size
Expand Down
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,68 @@ $ varlink --bridge "websocat --binary ws://localhost:1031/ws/sockets/io.systemd.

```

## vsock transport

The bridge supports AF_VSOCK as an alternative to TCP, allowing
host-to-guest communication without a network. vsock traffic cannot
be sniffed on the network, but any process on the host can connect
to a guest's vsock port, so authentication is still required (mTLS
or SSH key auth).

### SSH key auth over vsock

vsock with SSH key auth works without TLS - the transport is not
sniffable so the lack of encryption is acceptable:

```console
# Server (inside the guest):
$ varlink-httpd --bind=vsock --authorized-keys=~/.ssh/authorized_keys

# Client (on the host):
$ varlinkctl call vsock://3/ws/sockets/io.systemd.Hostname \
io.systemd.Hostname.Describe '{}'
```

### mTLS over vsock

Server (inside the guest):

```console
$ varlink-httpd --bind=vsock \
--cert=server.pem --key=server-key.pem --trust=ca.pem
```

Client (on the host), using `vsock+tls://`:

```console
$ varlinkctl call vsock+tls://3/ws/sockets/io.systemd.Hostname \
io.systemd.Hostname.Describe '{}'
```

The client looks for its certificate and key in the same config
directories as for TCP (see [Client (varlinkctl-http)](#client-varlinkctl-http)
below). CID 3+ are guests; CID 2 is the host.

### systemd socket activation

Two socket units are shipped, both backed by the same
`varlink-httpd.service`:

- `varlink-httpd.socket` listens on TCP `0.0.0.0:1031`.
- `varlink-httpd-vsock.socket` listens on `vsock::1031` and has
`ConditionVirtualization=vm`, so it only activates inside a VM. On
bare metal or the host it is silently skipped, which makes it safe
to enable unconditionally.

Enable whichever sockets you want; if both are enabled, systemd passes
both listening fds to the service on activation, so the daemon listens
on both transports regardless of which connection arrives first. The
daemon starts on demand when the first connection arrives.

```console
# systemctl enable --now varlink-httpd.socket varlink-httpd-vsock.socket
```

## TLS / mTLS

TLS flag names follow the systemd convention.
Expand Down
14 changes: 14 additions & 0 deletions data/varlink-httpd-vsock.socket
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of varlink-http-bridge.

[Unit]
Description=Varlink HTTP Bridge (vsock)
ConditionVirtualization=vm

[Socket]
ListenStream=vsock::1031
Service=varlink-httpd.service

[Install]
WantedBy=sockets.target
18 changes: 17 additions & 1 deletion data/varlink-httpd.service.in
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,26 @@ Conflicts=shutdown.target

[Service]
Type=simple
ExecStart=@bindir@/varlink-httpd --bind 0.0.0.0:1031
ExecStart=@bindir@/varlink-httpd
Restart=on-failure
RestartSec=5

# Hardening
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_VSOCK
RestrictNamespaces=yes
RestrictSUIDSGID=yes
MemoryDenyWriteExecute=yes
LockPersonality=yes
NoNewPrivileges=yes

# TLS credentials: well-known names from the credstore, renamed to the
# short names the service expects in $CREDENTIALS_DIRECTORY.
# Place certs in /etc/credstore/ and keys in /etc/credstore.encrypted/.
Expand Down
13 changes: 13 additions & 0 deletions data/varlink-httpd.socket
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of varlink-http-bridge.

[Unit]
Description=Varlink HTTP Bridge (TCP)

[Socket]
ListenStream=0.0.0.0:1031
Service=varlink-httpd.service

[Install]
WantedBy=sockets.target
4 changes: 4 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ install_server: (build "release")
install -Dm755 {{srv_binary}} {{destdir}}{{bindir}}/varlink-httpd
install -dm755 {{destdir}}{{unitdir}}
sed 's|@bindir@|{{bindir}}|g' data/varlink-httpd.service.in > {{destdir}}{{unitdir}}/varlink-httpd.service
install -m644 data/varlink-httpd.socket {{destdir}}{{unitdir}}/varlink-httpd.socket
install -m644 data/varlink-httpd-vsock.socket {{destdir}}{{unitdir}}/varlink-httpd-vsock.socket

install_client: (build "release")
install -Dm755 {{helper_binary}} {{destdir}}{{bridgedir}}/http
ln -sf http {{destdir}}{{bridgedir}}/https
ln -sf http {{destdir}}{{bridgedir}}/ws
ln -sf http {{destdir}}{{bridgedir}}/wss
ln -sf http {{destdir}}{{bridgedir}}/vsock
ln -sf http {{destdir}}{{bridgedir}}/vsock+tls

install_config:
install -dm755 {{destdir}}{{sysconfdir}}/varlink-httpd
Expand Down
Loading