remote-systemd-toggle is intentionally not a remote shell. It exposes exactly one predefined systemd action over a tiny mTLS-protected request/response protocol.
remote-systemd-toggle is a small Go client/server tool for toggling a
configured systemd service remotely.
It uses TLS 1.3 with mutual TLS, an additional password check, and Argon2id for
password storage. The server is intended to run as root because it calls
systemctl directly.
remote-systemd-toggled: TLS serverremote-systemd-toggle: TLS clientcommon: shared config and wire protocol code
The server is designed to be reachable over an untrusted network, but only with strict authentication:
- TLS 1.3 only
- mutual TLS is required
- the server verifies the client certificate against
TLS.client-ca-cert - the server can additionally verify the client certificate CN with
TLS.client-cn(recommended for production) - the client verifies the server certificate using system CAs plus optional
TLS.server-ca-cert - toggle and status requests both require mTLS and the password
- every parsed request returns one status response after mTLS; wrong passwords return
unauthorized - passwords are read through a hidden prompt unless
--passwordis used for scripts - passwords are never logged
- password bytes are wiped after use where practical
- the password hash is stored as Argon2id parameters plus salt/hash in YAML
- the
secrets.ymlfile is written with0600 - the server config directory is corrected to
0700 - the server config and
secrets.ymlfile are corrected to0600
After wrong passwords, the server waits increasingly longer:
delay = wrong_attempts * wrong_attempts * 3 minutes // '3 minutes' can be changed in configOn the tenth (can be changed in config) wrong password, the server disables and stops itself with systemctl. (In --dev mode it only logs what it would do, does not wait after wrong passwords, and exits at the limit.)
- Get a binary from a release or from the Debian repository, or build one from source.
- Create certificates, optionally using
cert-generation-examples/(for production, you only need to generate client certificates). - Create the configuration file, optionally using
config-examples/as a template. - May be install fail2ban rules (server only)
Detailed instructions for each step are provided in the sections below.
- Enable the systemd unit to run the server (see below).
- Set a password using
remote-systemd-toggled --passwd(see below).
remote-systemd-toggle toggleconnects to the configured server, toggles the configured service, and prints the new status. Without a command,toggleis used as the default.remote-systemd-toggle statusconnects to the configured server and prints the current status:active,inactive,failed, orunknown.- The client prompts for a password and sends one authenticated request to the server.
For scripts, the client also accepts--password <password>and skips the prompt. This exposes the password to shell history and process listings; use it only in controlled environments. - If authentication fails, the client prints
unauthorized. - The server accepts one connection at a time, reads one request, verifies the password, and then executes the requested command.
If the password is wrong, the server waits increasingly longer and eventually disables and stops itself.
GitHub releases provide Debian packages, Red Hat compatible RPM packages, and a Windows client binary.
- The Linux packages are built for
amd64andarm64. - The Windows artifact contains the client only.
If you need support for other architectures, just open an issue.
You can use the Debian repository provided by thk-systems.net to receive automatic updates (currently only amd64):
curl -fsSL https://debian.thk-systems.net/repo-install.sh | sudo sh
sudo apt install remote-systemd-toggle-server (or/and)
sudo apt install remote-systemd-toggle-clientFirst get a source code tarball from a release, or clone the repository.
go build -o remote-systemd-toggle ./remote-systemd-togglego build -o remote-systemd-toggled ./remote-systemd-toggledGOOS=windows GOARCH=amd64 go build -o remote-systemd-toggle.exe ./remote-systemd-toggleIt is recommended practice to store self-generated certificates in the configuration directory or in a subdirectory below it.
OpenSSL helper scripts are provided in cert-generation-examples/.
./cert-generation-examples/create-client-cert.sh client-certs remote-systemd-toggle-client./cert-generation-examples/create-server-cert.sh server-certs server.example.orgFor production servers, a public CA certificate such as a certbot certificate is usually preferable for the server certificate. The client certificate should still be issued by your private client CA.
Keep CA private keys offline and do not deploy them to clients or servers. The server only needs its server certificate/key and the client CA certificate. The client only needs its client certificate/key and, if configured, the server CA certificate.
The client searches:
~/.config/remote-systemd-toggle/config-client.yml
~/.remote-systemd-toggle/config-client.yml
/etc/remote-systemd-toggle/config-client.yml
The server searches:
~/.config/remote-systemd-toggle/config-server.yml
~/.remote-systemd-toggle/config-server.yml
/etc/remote-systemd-toggle/config-server.yml
If you are using Windows, create a .config or .remote-systemd-toggle directory in your user's home directory.
Example configs are in config-examples/.
Server:
address: server.example.org
port: 47112 # optional, default 47112
timeout: 5 # optional, default 5 seconds
TLS:
cert: /home/<user>/.config/remote-systemd-toggle/client.crt
key: /home/<user>/.config/remote-systemd-toggle/client.key
server-ca-cert: /home/<user>/.config/remote-systemd-toggle/server-ca.crt # optional, extends system CAs(see above how to create certificates)
Server:
listen: 0.0.0.0 # optional, default 0.0.0.0
port: 47112 # optional, default 47112
timeout: 5 # optional, default 5 seconds
wrong-password-limit: 10 # optional, default 10
wrong-password-delay-minutes: 3 # optional, default 3
TLS:
cert: /etc/letsencrypt/live/vpn.example.org/fullchain.pem
key: /etc/letsencrypt/live/vpn.example.org/privkey.pem
client-ca-cert: /etc/remote-systemd-toggle/client-ca.crt
client-cn: remote-systemd-toggle-client # optional, verifies the client certificate CN when set, recommended for production
Service:
name: example.service
Secrets:
path: secrets.yml # optional, default secrets.yml next to config; relative paths are relative to config
argon2-time: 5 # optional, default 5
argon2-memory: 65536 # optional, default 65536 KiB
argon2-threads: 1 # optional, default 1
argon2-key-len: 32 # optional, default 32 bytesCreate or replace the server-side password hash:
remote-systemd-toggled --passwdThis command prompts for a password, reads the server config, writes secrets.yml, and exits.
Changing Secrets.argon2-* in configuration only affects newly generated password hashes.
Run remote-systemd-toggled --passwd again after changing these values.
An example unit file is provided:
remote-systemd-toggled.service
The prebuilt Debian and RPM packages install this systemd unit file automatically, but they do not enable or start the service.
If you install from source or use the release tarball, install the unit file according to your distribution's systemd conventions and adjust paths if needed.
After the server has been configured, enable and start the service:
systemctl enable remote-systemd-toggled.service
systemctl start remote-systemd-toggled.serviceExample fail2ban configuration is provided in fail2ban-examples/:
fail2ban-examples/remote-systemd-toggled.conf
fail2ban-examples/remote-systemd-toggled.local
To enable fail2ban manually, install the filter and create or copy the jail:
/etc/fail2ban/filter.d/remote-systemd-toggled.conf
/etc/fail2ban/jail.d/remote-systemd-toggled.local
Example jail:
[remote-systemd-toggled]
enabled = true
filter = remote-systemd-toggled
logpath = /var/log/syslog
backend = auto
port = 47112
protocol = tcp
maxretry = 3
findtime = 10m
bantime = 1hAdjust maxretry, findtime, and bantime to your environment, then reload fail2ban.
The generated server packages already installs the filter to:
/etc/fail2ban/filter.d/remote-systemd-toggled.conf
It also includes the example files in:
/usr/share/doc/remote-systemd-toggle-server/fail2ban-examples/
Both binaries support --version:
remote-systemd-toggle --version
remote-systemd-toggled --versionThe tool intentionally fails fast on configuration errors or similar unexpected conditions.
This is a deliberate design choice: a misconfigured remote service toggle should stop immediately and loudly instead of continuing in an undefined or potentially unsafe state.
In other words: panic is not a bug here β it is part of the safety model. π
This tool is not a replacement for SSH.
SSH is a powerful, feature-rich, and highly complex protocol for general remote access. remote-systemd-toggle deliberately goes in the opposite direction: one mTLS-protected TCP connection, one password-authenticated command, one response, and nothing else.
The Windows client binary is currently unsigned. Because this is a new FOSS project without an established publisher reputation, Microsoft Defender SmartScreen or antivirus products may warn about the executable or classify it as suspicious.
The source code is public, release artifacts are built by GitHub Actions, and checksums are provided by GitHub releases. If you do not trust the prebuilt binary, please build the client from source.
If you trust the source, the release artifact, and the checksum, you may unblock the binary locally at your own risk.
If necessary, run this command in an elevated PowerShell:
Unblock-File .\remote-systemd-toggle.exe
Add-MpPreference -ExclusionPath "C:\<...>\remote-systemd-toggle.exe"Do not disable Microsoft Defender globally.
Microsoft has been informed about this Defender false positive detection.
Case ID: 254eb93e-f17d-4c6a-8c4b-4b9699f0435b
In the meantime, Microsoft has classified the binary as harmless. With up-to-date Microsoft Defender security intelligence, it should now run without any issues.
Run the server in development mode:
remote-systemd-toggled --devDevelopment mode is completely non-destructive:
- Logs are written to stdout.
- The configured service is never started or stopped. The server only logs what it would do.
- Toggle and status requests return
unknown; the server only logs what it would do. - No delay is applied after a wrong password. The calculated delay is logged, but execution continues immediately.
- No
systemctlactions are executed after a last wrong password. The server only logs whether it would stop and exits. remote-systemd-toggle toggleandremote-systemd-toggle statusreturnunknownand do not check the service status.- Stacktraces are printed.
The client has a --dev flag too, just to enable stacktraces.
This project is dedicated to Jessie, my best friend ever.
He never left my side. Even when he was old and sick, he would fight his way up the stairs just to find me and be near me. We played together in the snow like two children, chasing sticks and sharing moments of pure joy.
Through good days and hard days, he was always there β loyal, gentle, and steadfast. His companionship, trust, and unconditional friendship shaped my life in ways words can hardly express.
Though he is gone, his paw prints remain on my heart, and the memories of our time together continue to bring both a smile and a tear.
You were not just a dog. You were family, my companion, and my friend. You will never be forgotten. πΎ
MIT License
Copyright (c) 2026 Thomas Kuhlmann
