mirror.py event plug-in that generates rsyncd.conf and rsyncd.secrets from the package list.
Project description
mirror.py rsync-server plugin
An event plugin for mirror.py that generates
rsyncd.conf and rsyncd.secrets from the live mirror package list plus a sidecar
rsync.json configuration file. The plugin regenerates both files automatically when the
mirror daemon starts (MASTER.INIT.POST) and whenever the package set changes via
mirror config reload (MASTER.CONFIG_RELOAD.POST).
How it works
The plugin listens on two mirror.py events:
MASTER.INIT.POST— fires once when the master daemon finishes starting up.MASTER.CONFIG_RELOAD.POST— fires aftermirror config reloadhas loaded a new configuration. Until the mirror.py core ships this event, regeneration happens at daemon start only; the listener signature accepts(*args, **kwargs)so it is forward-compatible.
File generation only. The plugin writes rsyncd.conf and rsyncd.secrets; it does
not start, stop, or signal the rsyncd process. This is intentional: rsyncd re-reads
rsyncd.conf and the secrets file on every incoming client connection, so adding or
removing mirror modules takes effect immediately with no reload signal needed.
Writes are atomic (via a temporary file in the same directory followed by os.replace).
If the generated content is identical to what is already on disk, the file is not
rewritten. Regeneration runs under a module-level lock so concurrent event firings do not
race.
Packages with settings.hidden: true are completely excluded — they appear in neither
the public nor the private module list.
Installation
Requires mirror.py >= 1.3.0 (uses the 1.3.0 plugin API: API version gate and the per-plugin config-file mechanism).
Install the plugin into the same Python environment as mirror.py:
pip install -e .
Enable the plugin in mirror.py's config.json. As of mirror.py 1.3.0 the plugins
block is enable-only:
"plugins": { "rsync-server": { "enabled": true } }
Any config sub-block here is ignored — the plugin's own settings live in rsync.json
(see the Configuration section below).
Scaffold rsync.json
The plugin supports mirror plugin config create:
mirror plugin config create rsync-server
This writes an example rsync.json (mode 0600, since it holds plaintext passwords).
Caveat: during mirror plugin config create the mirror config path is not loaded, so the
file is written to the fixed fallback location /etc/mirror/rsync.json regardless of
any --config flag. --config only points the command at a valid config.json so the
plugin registry can load; it does not change where the scaffold is written. If your
mirror config lives elsewhere, move the generated file next to your config.json, or copy
rsync.json.example manually. (At daemon runtime the config path
is known, so generation and reads use rsync.json next to your config.json.)
Configuration
rsync.json must live in the same directory as mirror.py's config.json (default path:
/etc/mirror/rsync.json).
See rsync.json.example for a complete, realistic example.
All keys are optional and fall back to the defaults listed below.
| Key | Type | Default | Description |
|---|---|---|---|
rsyncd_conf |
string (absolute path) | /etc/rsyncd.conf |
Destination path for the generated rsyncd.conf. |
secrets_file |
string (absolute path) | /etc/rsyncd.secrets |
Destination path for the generated secrets file. Also emitted as secrets file in the global section of rsyncd.conf. |
users |
object | {} |
Map of username to password. Written to the secrets file in insertion order. |
global |
object | {} |
Raw rsyncd parameter passthrough. Keys and values are emitted in insertion order as key = value lines in the global section. Values may be strings, integers, or booleans (booleans render as true/false). The key secrets file is managed by the plugin and is rejected if placed here. |
private_modules.enabled |
boolean | true |
When false, no [.Name] private module sections are generated. |
private_modules.auth_users |
string | * |
Value written to auth users in every private module section. |
private_modules.list |
boolean | false |
Value written to list in every private module section. |
private_modules.lock_file |
string (absolute path) | /var/run/rsyncd-private.lock |
Value written to lock file in every private module section. The private modules share this lock file, giving them a separate max-connections accounting pool independent of the public modules. |
Unknown top-level keys and unknown private_modules keys are rejected with a ValueError
at load time (typo protection). Any validation failure leaves previously generated files
untouched.
SECURITY NOTE: rsync.json contains plaintext passwords. Restrict its permissions:
chmod 600 /etc/mirror/rsync.json
The plugin logs a warning if rsync.json is group- or other-readable. The generated
rsyncd.secrets file is always written with mode 0600, as required by rsyncd's strict
modes.
Generated output
Given a package with id archlinux (display name ArchLinux), src = rsync://rsync.archlinux.org/ftp_tier1 and dst = /mirror/ftp/ArchLinux, and the global
settings from rsync.json.example, the plugin produces:
# WARNING: DO NOT EDIT!! Generated by the mirror.py rsync-server plugin.
uid = rsync
gid = nogroup
use chroot = no
max connections = 20
motd file = /mirror/etc/motd
log file = /var/log/geoul/rsyncd/all.log
transfer logging = yes
log format = %o %a - %u [%t] "%P/%f" - %l
pid file = /var/run/rsyncd.pid
exclude = .~tmp~
secrets file = /mirror/etc/rsyncd.secrets
[archlinux]
path = /mirror/ftp/ArchLinux
comment = from rsync://rsync.archlinux.org/ftp_tier1
[.archlinux]
path = /mirror/ftp/ArchLinux
comment = private module for ArchLinux without connection limits for authorized mirrors
auth users = *
list = false
lock file = /var/run/rsyncd-private.lock
Each package produces a public/private module pair:
[pkgid]is the public module. The module name is the package id, not the display name, so it is a valid rsyncd module token (no spaces or special characters). It is visible inrsync host::listings and carries acomment = from <upstream src>line. The comment line is omitted when a package has no upstreamsrc(for example, locally synced packages).[.pkgid]is the authenticated, unlisted private twin. The leading dot hides it fromrsync host::listings. Access is gated byauth usersand the secrets file. This module has its own lock file, so its connection accounting is independent of the public module'smax connectionslimit.
Modules are sorted by package id. The static header contains no timestamp so that identical content is detected and the file is not rewritten unnecessarily.
The secrets file (rsyncd.secrets) contains one username:password line per entry in
users, written in insertion order, with no header line.
Running rsyncd
Point rsyncd at the generated configuration file:
rsync --daemon --config=/etc/rsyncd.conf
Operational notes
Changing users or rsync settings. Edit rsync.json, then run:
mirror config reload
No daemon restart is needed for module or authentication changes. rsyncd picks up the
new rsyncd.conf and rsyncd.secrets on the next incoming client connection.
Parameters that require an rsyncd restart. Daemon-socket parameters such as port,
address, and pid file are read by rsyncd only at startup. Changing them in rsync.json
and running mirror config reload will update the generated file, but rsyncd itself must
be restarted for these to take effect. Module-level parameters (path, auth, comment, etc.)
apply per connection and do not require a restart.
Private module connection accounting. The private modules share the lock file
specified by private_modules.lock_file. This gives them a separate max-connections pool
rather than unlimited connections — the pool size is controlled by rsyncd's global
max connections parameter applied against that lock file independently from the public
modules' lock file.
Development
Set up a virtual environment and install the plugin alongside mirror.py:
uv venv
uv pip install -e /path/to/mirror.py -e . pytest
Run the test suite:
uv run pytest
License
Apache-2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mirror_py_rsync_server-1.1.0.tar.gz.
File metadata
- Download URL: mirror_py_rsync_server-1.1.0.tar.gz
- Upload date:
- Size: 22.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5168de43e5e34e9b2c7595eaa84ed3b56338de78fdaac4d909463cd813e4d6a8
|
|
| MD5 |
56402ae0707f4a7b5c9274b3a2859749
|
|
| BLAKE2b-256 |
78e5cbff3eeb9a5ea0455415fb1a02ebbcc1f04811c48273fcd32fa52e5e0562
|
Provenance
The following attestation bundles were made for mirror_py_rsync_server-1.1.0.tar.gz:
Publisher:
pypi.yaml on sparcs-kaist/mirror.py-rsync-server
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mirror_py_rsync_server-1.1.0.tar.gz -
Subject digest:
5168de43e5e34e9b2c7595eaa84ed3b56338de78fdaac4d909463cd813e4d6a8 - Sigstore transparency entry: 2083455309
- Sigstore integration time:
-
Permalink:
sparcs-kaist/mirror.py-rsync-server@6b31071c188e2941c7249743622039cec54eaea4 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/sparcs-kaist
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yaml@6b31071c188e2941c7249743622039cec54eaea4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mirror_py_rsync_server-1.1.0-py3-none-any.whl.
File metadata
- Download URL: mirror_py_rsync_server-1.1.0-py3-none-any.whl
- Upload date:
- Size: 17.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0bf768d866d7bcbcd5fe015940e565821a8859246dbc64a614cc7df5febaf6da
|
|
| MD5 |
fcae75e600f105a68f36858a8523e349
|
|
| BLAKE2b-256 |
f97fab71d1c5fdcc698f17575b80703834b49e7f1a90f0803cda81dff996a944
|
Provenance
The following attestation bundles were made for mirror_py_rsync_server-1.1.0-py3-none-any.whl:
Publisher:
pypi.yaml on sparcs-kaist/mirror.py-rsync-server
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mirror_py_rsync_server-1.1.0-py3-none-any.whl -
Subject digest:
0bf768d866d7bcbcd5fe015940e565821a8859246dbc64a614cc7df5febaf6da - Sigstore transparency entry: 2083455343
- Sigstore integration time:
-
Permalink:
sparcs-kaist/mirror.py-rsync-server@6b31071c188e2941c7249743622039cec54eaea4 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/sparcs-kaist
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yaml@6b31071c188e2941c7249743622039cec54eaea4 -
Trigger Event:
push
-
Statement type: