Merge pull request 'Fix sshd docs' (#5492) from Qubasa/clan-core:fix_sshd_docs into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/5492
This commit is contained in:
Luis Hebendanz
2025-10-14 08:56:32 +00:00
2 changed files with 79 additions and 25 deletions

View File

@@ -1,39 +1,92 @@
The `sshd` Clan service manages SSH to make it easy to securely access your
machines over the internet. The service uses `vars` to store the SSH host keys
for each machine to ensure they remain stable across deployments.
`sshd` also generates SSH certificates for both servers and clients allowing for
certificate-based authentication for SSH.
## What it does
- Generates and persists SSH host keys via `vars`.
- Optionally issues CA-signed host certificates for servers.
- Installs the `server` CA public key into `clients` `known_hosts` for TOFU-less verification.
The service also disables password-based authentication over SSH, to access your
machines you'll need to use public key authentication or certificate-based
authentication.
## Usage
## When to use it
- Zero-TOFU SSH for dynamic fleets: admins/CI can connect to frequently rebuilt hosts (e.g., server-1.example.com) without prompts or per-host `known_hosts` churn.
### Roles
- Server: runs sshd, presents a CA-signed host certificate for `<machine>.<domain>`.
- Client: trusts the CA for the given domains to verify servers' certificates.
Tip: assign both roles to a machine if it should both present a cert and verify others.
Quick start (with host certificates)
Useful if you never want to get a prompt about trusting the ssh fingerprint.
```nix
{
inventory.instances = {
sshd-with-certs = {
module = { name = "sshd"; input = "clan-core"; };
# Servers present certificates for <machine>.example.com
roles.server.tags.all = { };
roles.server.settings = {
certificate.searchDomains = [ "example.com" ];
# Optional: also add RSA host keys
# hostKeys.rsa.enable = true;
};
# Clients trust the CA for *.example.com
roles.client.tags.all = { };
roles.client.settings = {
certificate.searchDomains = [ "example.com" ];
};
};
};
}
```
Basic: only add persistent host keys (ed25519), no certificates
Useful if you want to get an ssh "trust this server" prompt once and then never again.
```nix
{
inventory.instances = {
# By default this service only generates ed25519 host keys
sshd-basic = {
module = {
name = "sshd";
input = "clan-core";
};
roles.server.tags.all = { };
roles.client.tags.all = { };
};
# Also generate RSA host keys for all servers
sshd-with-rsa = {
module = {
name = "sshd";
input = "clan-core";
};
roles.server.tags.all = { };
roles.server.settings = {
hostKeys.rsa.enable = true;
};
roles.client.tags.all = { };
};
};
}
```
Example: selective trust per environment
Admins should trust only production; CI should trust prod and staging. Servers are reachable under both domains.
```nix
{
inventory.instances = {
sshd-env-scoped = {
module = { name = "sshd"; input = "clan-core"; };
# Servers present certs for both prod and staging FQDNs
roles.server.tags.all = { };
roles.server.settings = {
certificate.searchDomains = [ "prod.example.com" "staging.example.com" ];
};
# Admin laptop: trust prod only
roles.client.machines."admin-laptop".settings = {
certificate.searchDomains = [ "prod.example.com" ];
};
# CI runner: trust prod and staging
roles.client.machines."ci-runner-1".settings = {
certificate.searchDomains = [ "prod.example.com" "staging.example.com" ];
};
};
};
}
```
### Explanation
- Admin -> server1.prod.example.com: zero-TOFU (verified via cert).
- Admin -> server1.staging.example.com: falls back to TOFU (or is blocked by policy).
- CI -> either prod or staging: zero-TOFU for both.
Note: server and client searchDomains don't have to be identical; they only need to overlap for the hostnames you actually use.
### Notes
- Connect using a name that matches a cert principal (e.g., `server1.example.com`); wildcards are not allowed inside the certificate.
- CA private key stays in `vars` (not deployed); only the CA public key is distributed.
- Logins still require your user SSH keys on the server (passwords are disabled).

View File

@@ -2,7 +2,7 @@
{
_class = "clan.service";
manifest.name = "clan-core/sshd";
manifest.description = "Enables secure remote access to the machine over SSH";
manifest.description = "Enables secure remote access to the machine over SSH with automatic host key management and optional CA-signed host certificates.";
manifest.categories = [
"System"
"Network"
@@ -180,7 +180,8 @@
settings.PasswordAuthentication = false;
settings.HostCertificate = lib.mkIf (
settings.certificate.searchDomains != [ ]
config.clan.core.vars.generators.openssh-cert.files."ssh.id_ed25519-cert.pub".exists
&& settings.certificate.searchDomains != [ ]
) config.clan.core.vars.generators.openssh-cert.files."ssh.id_ed25519-cert.pub".path;
hostKeys = [