rsync

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 24, 2026 License: BSD-3-Clause Imports: 4 Imported by: 0

README

gokrazy rsync

Go Reference tests Sourcegraph

gokrazy/rsync is an Rsync program and reusable library implemented in the Go programming language, originally as part of the gokrazy Go appliance platform project. gokrazy/rsync is (wire-)compatible with the original "tridge" Rsync.

Various use-cases are supported:

  • The gokr-rsync command implements an rsync client and server, which can send or receive files (all directions supported).
  • You can set up a public or private rsync server, in daemon mode or via SSH (anonymous or authorized).
  • You can embed rsync in your program with the rsynccmd package.
  • You can implement your own rsync server with the rsyncd package.

Aside from being implemented in Go, a memory-safe programming language, gokrazy/rsync makes use of Go’s traversal-resistant file API (os.Root) and the Linux Landlock Unprivileged Sandboxing to steer clear of vulnerabilities. In server settings, gokrazy/rsync supports mount namespaces and can be further locked down with systemd’s various hardening options.

This project accepts contributions as time permits to merge them (best effort).

Notable users / use-cases

How do I know this project won’t eat my data?

This rsync implementation is not as well-tested as the original “tridge” implementation from the Samba project. gokrazy/rsync was started in 2021 and doesn’t have many users yet.

With that warning out of the way, the rsync protocol uses MD4 checksums over file contents, so at least your file contents should never be able to be corrupted.

There is enough other functionality (delta transfers, file metadata, special files like symlinks or devices, directory structures, etc.) in the rsync protocol that provides opportunities for bugs to hide.

I recommend you carefully check that your transfers work, and please do report any issues you run into!

Existing rsync implementation survey

Language URL Note Max Protocol Server mode?
C RsyncProject/rsync (formerly WayneD/rsync) original “tridge” implementation; I found older versions easier to study 32 ✔ yes
C kristapsdz/openrsync OpenBSD, good docs 27 ✔ yes
Go gokrazy/rsync → you are here ← 27 ✔ yes 🎉
Go jbreiding/rsync-go rsync algorithm ❌ no
Go kaiakz/rsync-os only client/receiver 27 ❌ no
Go knight42 proxy ❌ no
Go c4milo/gsync ❌ no
Java APNIC-net/repositoryd archived ✔ yes
Java JohannesBuchner/Jarsync archived, internet draft RFC “The rsync Network Protocol” ✔ yes
Java perlundq/yajsync ✔ yes
C++ gilbertchen/acrosync-library commercial ❌ no
Rust sourcefrog/rsyn archived, client, “rsyn is rsync with no c” 27 ❌ no

Getting started

To serve the /usr/share/man directory via rsync on localhost:8730, use:

go install github.com/gokrazy/rsync/cmd/gokr-rsync@latest
gokr-rsync --daemon --gokr.listen=localhost:8730 --gokr.modulemap=man=/usr/share/man

You can then copy the contents of the current directory with clients such as rsync(1):

% rsync -v --archive --port 8730 rsync://localhost/man/ man
receiving file list ... done
created directory man
./
ar/
ar/man1/
[…]
zh_TW/man8/userdel.8.gz
zh_TW/man8/usermod.8.gz

sent 658.973 bytes  received 88.012.067 bytes  3.940.935,11 bytes/sec
total size is 84.504.170  speedup is 0,95

…or openrsync(1), shown doing a differential update:

% openrsync -v --archive --port 8730 rsync://localhost/man/ man
openrsync: warning: connect refused: ::1, localhost
Transfer starting: 40202 files
[…]
zh_TW/man8/userdel.8.gz (732 B, 100.0% downloaded)
zh_TW/man8/usermod.8.gz (1.8 KB, 100.0% downloaded)
Transfer complete: 83.93 MB sent, 643.5 KB read, 80.59 MB file size

Usage / Setup

setup encrypted authenticated private files? privileges protocol version config required
1. rsync daemon protocol (TCP port 873) ❌ no ⚠ rsync (insecure) ❌ only world-readable ✔ dropped + namespace ✔ negotiated config required
2. anon SSH (daemon) ✔ yes ✔ rsync ❌ only world-readable ✔ dropped + namespace ✔ negotiated config required
3. SSH (command) ✔ yes ✔ SSH ✔ yes ⚠ full user ⚠ assumed no config
4. SSH (daemon) ✔ yes ✔ SSH (+ rsync) ✔ yes ⚠ full user ✔ negotiated ~/.config/gokr-rsyncd.toml required

Regarding protocol version “assumed”: the flags to send over the network are computed before starting SSH and hence the remote rsync process. You might need to specify --protocol=27 explicitly on the client. Once the connection is established, both sides do negotiate the protocol, though.

Setup 1: rsync daemon protocol (TCP port 873)

Serving rsync daemon protocol on TCP port 873 is only safe where the network layer ensures trusted communication, e.g. in a local network (LAN), or when using Tailscale or similar. In untrusted networks, attackers can eavesdrop on file transfers and possibly even modify file contents.

Prefer setup 2 instead.

Example:

  • Server: gokr-rsync --daemon --gokr.modulemap=module=/srv/rsync-module
  • Client: rsync rsync://webserver/module/path
Setup 2: anon SSH (daemon)

This setup is well suited for serving world-readable files without authentication.

Example:

  • Server: gokr-rsync --daemon --gokr.modulemap=module=/srv/rsync-module --gokr.anonssh_listen=:22873
  • Client: rsync -e ssh rsync://webserver/module/path
Setup 3: SSH (command)

This setup is well suited for interactive one-off transfers or regular backups, and uses SSH for both encryption and authentication.

Note that because gokr-rsync is invoked with user privileges (not root privileges), it cannot do namespacing and hence retains more privileges. When serving public data, it is generally preferable to use setup 2 instead.

Note that rsync(1) assumes the server process understands all flags that it sends, i.e. is running the same version on client and server, or at least a compatible-enough version. You can either specify --protocol=27 on the client, or use setup 4, which negotiates the protocol version, side-stepping possible compatibility gaps between rsync clients and gokr-rsync.

Example:

  • Server will be started via SSH
  • Client: rsync --rsync-path=gokr-rsync webserver:path
Setup 4: SSH (daemon)

This setup is more reliable than setup 3 because the rsync protocol version will be negotiated between client and server. This setup is slightly inconvenient because it requires a config file to be present on the server in ~/.config/gokr-rsyncd.toml.

Note that this mode of operation is only implemented by the original “trigde” rsync, not in openrsync. Apple started shipping openrsync with macOS 15 Sequoia. For a while, /usr/libexec/rsync/rsync.samba was still available, but on more recent macOS versions you need to use homebrew or Nix to get tridge rsync.

Example:

  • Server will be started via SSH
  • Client: rsync -e ssh --rsync-path=gokr-rsync rsync://webserver/module/path

Limitations

Bandwidth

In my tests, gokr-rsync can easily transfer data at > 6 Gbit/s. The current bottleneck is the MD4 algorithm itself (not sure whether in the “tridge” rsync client, or in gokr-rsync). Implementing support for more recent protocol versions would help here, as these include hash algorithm negotiation with more recent choices.

  • xattrs (including acls) was introduced in rsync protocol 30, so is currently not supported.

Supported environments and privilege dropping

Supported environments:

  1. systemd (Linux)
  2. privileged Linux (running as root, or in a user namespace)
  3. privileged non-Linux
  4. unprivileged Linux, Mac or Windows

In all environments, the default instructions will take care that:

  • gokr-rsync uses Go’s Traversal-resistant os.Root file APIs to restrict all file access to the specified paths.
  • (On privileged Linux only) Only configured rsync modules from the host file system are mounted (read-only, unless the module is writable) into a Linux mount namespace for gokr-rsync, to guard against data modification and data exfiltration.
  • (On Linux only) File system access is restricted using the Landlock Linux kernel security module, which works similar to OpenBSD’s unveil(2) API.
  • (On privileged environments) gokr-rsync drops privileges to user nobody, to limit the scope of what an attacker can do when exploiting a vulnerability.

Known gaps:

  • gokr-rsync does not guard against denial of service attacks, i.e. consuming too many resources (connections, bandwidth, CPU, …).
systemd (unprivileged)

We provide a gokr-rsyncd.socket and gokr-rsyncd.service file for systemd. These files enables most of systemd’s security features. You can check by running systemd-analyze security gokr-rsyncd.service, which should result in an exposure level of “0.2 SAFE” as of systemd 249 (September 2021).

First, configure your server flags by creating a systemd service override file:

systemctl edit gokr-rsyncd.service

In the opened editor, change the file to:

[Service]
ExecStart=
ExecStart=/usr/bin/gokr-rsync --daemon --gokr.modulemap=pwd=/etc/tmpfiles.d

Close the editor and install the service using:

systemctl enable --now gokr-rsyncd.socket

Additional hardening recommendations:

privileged Linux (including gokrazy.org)

When started as root on Linux, gokr-rsync will create a Linux mount namespace, mount all configured rsync modules read-only into the namespace, then change into the namespace using chroot(2) and drop privileges using setuid(2).

Tip: you can verify which file system objects the daemon process can see by using ls -l /proc/$(pidof gokr-rsync)/root/.

Additional hardening recommendations:

privileged non-Linux (e.g. Mac)

When started as root on non-Linux (e.g. Mac), gokr-rsync will drop privileges using setuid(2).

unprivileged with write permission (e.g. from a shell)

To prevent accidental misconfiguration, gokr-rsync refuses to start when it detects that it has write permission in any configured rsync module.

Documentation

Overview

Package rsync (gokrazy/rsync) contains a native Go rsync implementation that supports sending and receiving files as client or server, compatible with the original tridge rsync (from the samba project) or openrsync (used on OpenBSD and macOS 15+).

The only component currently is gokr-rsyncd, a read-only rsync daemon sender-only Go implementation of rsyncd. rsync daemon is a custom (un-standardized) network protocol, running on port 873 by default.

Index

Constants

View Source
const (
	XMIT_TOP_DIR             = (1 << 0)
	XMIT_SAME_MODE           = (1 << 1)
	XMIT_EXTENDED_FLAGS      = (1 << 2)
	XMIT_SAME_RDEV_pre28     = XMIT_EXTENDED_FLAGS /* Only in protocols < 28 */
	XMIT_SAME_UID            = (1 << 3)
	XMIT_SAME_GID            = (1 << 4)
	XMIT_SAME_NAME           = (1 << 5)
	XMIT_LONG_NAME           = (1 << 6)
	XMIT_SAME_TIME           = (1 << 7)
	XMIT_SAME_RDEV_MAJOR     = (1 << 8)
	XMIT_HAS_IDEV_DATA       = (1 << 9)
	XMIT_SAME_DEV            = (1 << 10)
	XMIT_RDEV_MINOR_IS_SMALL = (1 << 11)
)

rsync.h

View Source
const (
	S_IFMT   = 0o0170000 // bits determining the file type
	S_IFDIR  = 0o0040000 // Directory
	S_IFCHR  = 0o0020000 // Character device
	S_IFBLK  = 0o0060000 // Block device
	S_IFREG  = 0o0100000 // Regular file
	S_IFIFO  = 0o0010000 // FIFO
	S_IFLNK  = 0o0120000 // Symbolic link
	S_IFSOCK = 0o0140000 // Socket
)

as per /usr/include/bits/stat.h:

View Source
const FileSystemRoot = "/"
View Source
const ProtocolVersion = 27

ProtocolVersion defines the currently implemented rsync protocol version. Protocol version 27 seems to be the safest bet for wide compatibility: version 27 was introduced by rsync 2.6.0 (released 2004), and is supported by openrsync and rsyn.

Variables

This section is empty.

Functions

This section is empty.

Types

type BothCloser

type BothCloser struct {
	io.ReadCloser
	io.WriteCloser
}

BothCloser is a wrapper around a ReadCloser and WriteCloser that provides its own Close() function which calls Close() on both, the WriteCloser and ReadCloser, in order, and returns the first non-nil error.

func (*BothCloser) Close

func (b *BothCloser) Close() error

Close implements io.Closer.

type Logger

type Logger = log.Logger

Logger is an interface that allows specifying your own logger. By default, the Go log package is used, which prints to stderr.

type SumBuf

type SumBuf struct {
	Offset int64
	Len    int64
	Index  int32
	Sum1   uint32
	Sum2   [16]byte
}

rsync/rsync.h:struct sum_buf

type SumHead

type SumHead struct {
	// “number of blocks” (openrsync)
	// “how many chunks” (rsync)
	ChecksumCount int32

	// “block length in the file” (openrsync)
	// maximum (1 << 29) for older rsync, (1 << 17) for newer
	BlockLength int32

	// “long checksum length” (openrsync)
	ChecksumLength int32

	// “terminal (remainder) block length” (openrsync)
	// RemainderLength is flength % BlockLength
	RemainderLength int32

	Sums []SumBuf
}

TODO: remove connection.go:sumHead in favor of this type

func (*SumHead) ReadFrom

func (sh *SumHead) ReadFrom(c *rsyncwire.Conn) error

func (*SumHead) WriteTo

func (sh *SumHead) WriteTo(c *rsyncwire.Conn) error

Directories

Path Synopsis
cmd
gokr-rsync command
Tool gokr-rsync is an rsync Go implementation.
Tool gokr-rsync is an rsync Go implementation.
gokr-rsyncd command
Tool gokr-rsyncd is an old name for gokr-rsync.
Tool gokr-rsyncd is an old name for gokr-rsync.
internal
log
Package log defines the logger interface used in rsync library.
Package log defines the logger interface used in rsync library.
maincmd
Package maincmd implements a subset of the '$ rsync' CLI surface, namely that it can:
Package maincmd implements a subset of the '$ rsync' CLI surface, namely that it can:
restrict
Package restrict can be used to restrict further file system access of the process if the operating system provides an API for that.
Package restrict can be used to restrict further file system access of the process if the operating system provides an API for that.
rsynccommon
Package rsynccommon contains functionality that both the sender and the receiver implementation need.
Package rsynccommon contains functionality that both the sender and the receiver implementation need.
rsyncopts
Package rsyncopts implements a parser for command-line options that implements a subset of popt(3) semantics; just enough to parse typical rsync(1) invocations without the advanced popt features like aliases or option prefix matching (not --del, only --delete).
Package rsyncopts implements a parser for command-line options that implements a subset of popt(3) semantics; just enough to parse typical rsync(1) invocations without the advanced popt features like aliases or option prefix matching (not --del, only --delete).
testlogger
Package testlogger contains a helper to put a stdout/stderr output stream of a subprocess onto the testing package's t.Log().
Package testlogger contains a helper to put a stdout/stderr output stream of a subprocess onto the testing package's t.Log().
Package rsyncclient implements an rsync client (only), but note that gokrazy/rsync contains a native Go rsync implementation that supports sending and receiving files as client or server, compatible with the original tridge rsync (from the samba project) or openrsync (used on OpenBSD and macOS 15+).
Package rsyncclient implements an rsync client (only), but note that gokrazy/rsync contains a native Go rsync implementation that supports sending and receiving files as client or server, compatible with the original tridge rsync (from the samba project) or openrsync (used on OpenBSD and macOS 15+).
Package rsynccmd provides a command-like interface to gokrazy/rsync, which contains a native Go rsync implementation that supports sending and receiving files as client or server, compatible with the original tridge rsync (from the samba project) or openrsync (used on OpenBSD and macOS 15+).
Package rsynccmd provides a command-like interface to gokrazy/rsync, which contains a native Go rsync implementation that supports sending and receiving files as client or server, compatible with the original tridge rsync (from the samba project) or openrsync (used on OpenBSD and macOS 15+).
Package rsyncd implements an rsync server (only), but note that gokrazy/rsync contains a native Go rsync implementation that supports sending and receiving files as client or server, compatible with the original tridge rsync (from the samba project) or openrsync (used on OpenBSD and macOS 15+).
Package rsyncd implements an rsync server (only), but note that gokrazy/rsync contains a native Go rsync implementation that supports sending and receiving files as client or server, compatible with the original tridge rsync (from the samba project) or openrsync (used on OpenBSD and macOS 15+).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL