Compare commits
158
Commits
forgejo
...
39504cd856
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39504cd856 | ||
|
|
ca75da7c37 | ||
|
|
9f1fed071d | ||
|
|
bfbdbae99b | ||
|
|
9f29907ec3 | ||
|
|
87821ed109 | ||
|
|
b37ee5777d | ||
|
|
68f40feffb | ||
|
|
a60d2bcd28 | ||
|
|
dc89592eb1 | ||
|
|
fa29ce93f4 | ||
|
|
73f5df1832 | ||
|
|
225020eb8d | ||
|
|
c762c24d86 | ||
|
|
a9b831a6b0 | ||
|
|
b1f4b6ad41 | ||
|
|
f2ba5d914c | ||
|
|
deae5d223a | ||
|
|
fae21e2962 | ||
|
|
69f492c7cc | ||
|
|
cddc369687 | ||
|
|
3f767dfc43 | ||
|
|
dccdc15851 | ||
|
|
a3a1cd7066 | ||
|
|
35fec024e0 | ||
|
|
6499ad7612 | ||
|
|
cf418ca7a0 | ||
|
|
8ae74a9bc9 | ||
|
|
b74133985c | ||
|
|
2de7650f3d | ||
|
|
41f33653a7 | ||
|
|
460d8908bb | ||
|
|
621a61fb9e | ||
|
|
3f3d847134 | ||
|
|
4e5f1230ab | ||
|
|
c3eeeb16f8 | ||
|
|
7556b13eb9 | ||
|
|
a72f34ab51 | ||
|
|
4e465b1846 | ||
|
|
d2737a42e8 | ||
|
|
a9ae07d5c1 | ||
|
|
f505100bb4 | ||
|
|
aff22e3a0f | ||
|
|
b13b774738 | ||
|
|
43fe37d4e2 | ||
|
|
a98e637ce2 | ||
|
|
0e194e5d44 | ||
|
|
68737a513f | ||
|
|
e76fc00a98 | ||
|
|
f22e8cb1d5 | ||
|
|
bb06b848be | ||
|
|
021ca1ba83 | ||
|
|
4bd11e1047 | ||
|
|
ff80098418 | ||
|
|
3265669503 | ||
|
|
7294e04ae2 | ||
|
|
3e1d438453 | ||
|
|
2f653b95ce | ||
|
|
1c1515678a | ||
|
|
ee4839877c | ||
|
|
fc2325e70b | ||
|
|
2ae5eb9547 | ||
|
|
ba305e29fd | ||
|
|
deda2c9bbc | ||
|
|
10c8c887db | ||
|
|
7b2dffbccf | ||
|
|
95f382107d | ||
|
|
3cd3099987 | ||
|
|
775543224e | ||
|
|
aff608c1ce | ||
|
|
ecb640fa45 | ||
|
|
ceeba1c786 | ||
|
|
b8153f0ebc | ||
|
|
e9ec34f15c | ||
|
|
6c8ca29751 | ||
|
|
bcd07532db | ||
|
|
2861c460c2 | ||
|
|
6e27e92f89 | ||
|
|
1002e6e0da | ||
|
|
24eeb5967c | ||
|
|
b7f5474893 | ||
|
|
1667e362aa | ||
|
|
cd5a49c4a6 | ||
|
|
244c60d9cd | ||
|
|
f2254e5dc7 | ||
|
|
a337ce6f2c | ||
|
|
278796f47c | ||
|
|
1ab00f286e | ||
|
|
771813a064 | ||
|
|
722cb78737 | ||
|
|
6bb73959c6 | ||
|
|
fcff43adb1 | ||
|
|
65751a14c5 | ||
|
|
05f92d1f59 | ||
|
|
7c4ce8a637 | ||
|
|
bb4fa7b82b | ||
|
|
dc4df3af29 | ||
|
|
6b8727b29d | ||
|
|
d64ce644c3 | ||
|
|
4746468a94 | ||
|
|
12e7d290a1 | ||
|
|
24f43872ce | ||
|
|
35bd80424d | ||
|
|
3447b28af1 | ||
|
|
2fea8238d1 | ||
|
|
cf1174d36b | ||
|
|
bae2b3027e | ||
|
|
68483c0231 | ||
|
|
f0eba76e49 | ||
|
|
8357372b39 | ||
|
|
3c4aa74b0f | ||
|
|
ed473ddfae | ||
|
|
133bad5aef | ||
|
|
3fc08793fe | ||
|
|
7f4fdcf4b9 | ||
|
|
bfc5da791a | ||
|
|
9a09399ca3 | ||
|
|
cf90d3e876 | ||
|
|
443020df4d | ||
|
|
3fc3beb4ed | ||
|
|
43ae292f39 | ||
|
|
026bf541e1 | ||
|
|
e75951318d | ||
|
|
bd236ed977 | ||
|
|
b07bf102a4 | ||
|
|
9bfe140367 | ||
|
|
545a17586e | ||
|
|
685c1bc05a | ||
|
|
a4f988c223 | ||
|
|
a05de7df1f | ||
|
|
aace1776d5 | ||
|
|
03965917ea | ||
|
|
dac6b70445 | ||
|
|
932616177a | ||
|
|
c1bfa64cc8 | ||
|
|
235cd297c5 | ||
|
|
0c91b1d493 | ||
|
|
9825270d64 | ||
|
|
ee9573fc97 | ||
|
|
d47394d4cc | ||
|
|
0abbcf0fd2 | ||
|
|
718aa466b6 | ||
|
|
916fd41555 | ||
|
|
65608646bb | ||
|
|
1278177e4d | ||
|
|
f36bb22635 | ||
|
|
fe24eb2dde | ||
|
|
e9d585f8d0 | ||
|
|
93e58c341d | ||
|
|
b8c73b446c | ||
|
|
58816b2356 | ||
|
|
ca2f5ac7c8 | ||
|
|
904dd6e329 | ||
|
|
8073125f3e | ||
|
|
f24a269af3 | ||
|
|
a5a10772d1 | ||
|
|
510a026de7 | ||
|
|
dd47ae94bf |
@@ -0,0 +1,68 @@
|
||||
# Host Naming Discrepancy Report
|
||||
|
||||
This report compares the current `modules/hosts` tree against the naming rules for new hosts in `hosts.instructions.md`.
|
||||
|
||||
## Rules Checked
|
||||
|
||||
- The host directory slug should be the canonical host identity.
|
||||
- A directory-backed host should prefer `default.nix` as its main entrypoint.
|
||||
- The canonical slug should line up with the primary `hostname` binding, `flake.nixosConfigurations.<slug>`, and `networking.hostName`.
|
||||
- A host-local Home Manager module should use the same slug.
|
||||
- A standalone Home Manager configuration should prefer the key `"<username>@<slug>"`.
|
||||
- Host-local helper module names should be prefixed by the host slug.
|
||||
|
||||
## Summary
|
||||
|
||||
- Recently normalized: `john-kde`, `omen-nixos`.
|
||||
- Hosts that fit the new rules closely: `janus`, `john-kde`, `omen-nixos`, `test-nix`.
|
||||
- Hosts with notable discrepancies: `john-p14s`, `soteria`.
|
||||
- Approved exception: `john-pc` directory uses immutable deployed slug `john-pc-ubuntu` for exported keys.
|
||||
|
||||
## Detailed Discrepancies
|
||||
|
||||
### `john-p14s`
|
||||
|
||||
- The main host export uses the canonical slug: `flake.nixosConfigurations.john-p14s`.
|
||||
- The host-local helper modules are named `p14sConfiguration` and `p14sHardware`, which drop the `john-` prefix and therefore do not use the full host slug.
|
||||
- `networking.hostname` uses the correct host value, but the option name differs from the dominant `networking.hostName` spelling used elsewhere in this tree.
|
||||
|
||||
### `john-pc`
|
||||
|
||||
- Approved exception for immutable deployed hostname:
|
||||
- Directory slug is `john-pc`, but exported keys and hostname bindings intentionally use `john-pc-ubuntu`.
|
||||
- `hostname` binding, `flake.modules.homeManager.<name>`, and `flake.homeConfigurations` are aligned to `john-pc-ubuntu`.
|
||||
- Shared SSH alias intentionally remains `john-pc-ubuntu` in `modules/services/ssh.nix`.
|
||||
|
||||
### `omen-nixos`
|
||||
|
||||
- No active discrepancy after normalization:
|
||||
- The directory slug, `flake.nixosConfigurations` key, host-local module keys, and `networking.hostName` are now aligned to `omen-nixos`.
|
||||
|
||||
### `john-kde`
|
||||
|
||||
- No active discrepancy after normalization:
|
||||
- The directory slug, `hostname` binding, host-local Home Manager module key, and standalone Home Manager key are now aligned to `john-kde`.
|
||||
|
||||
### `soteria`
|
||||
|
||||
- Naming is mostly coherent across directory slug, `hostname`, `flake.nixosConfigurations.soteria`, and `flake.modules.homeManager.soteria`.
|
||||
- The standalone Home Manager export is `flake.homeConfigurations.soteria`, which does not follow the preferred `"<username>@<slug>"` form.
|
||||
- The main host entrypoint is `soteria.nix` instead of the preferred `default.nix`.
|
||||
|
||||
## No Discrepancy Found
|
||||
|
||||
### `janus`
|
||||
|
||||
- Directory slug, `hostname`, `flake.nixosConfigurations.janus`, and `networking.hostName` are aligned.
|
||||
- The host-local helper module `janus-ca` is clearly prefixed by the host slug.
|
||||
|
||||
### `test-nix`
|
||||
|
||||
- The single-file host uses the same slug for the file stem, `hostname`, and `flake.nixosConfigurations.test-nix`.
|
||||
- It does not define `networking.hostName`, but it does not contradict the slug anywhere else.
|
||||
|
||||
## Follow-up Candidates
|
||||
|
||||
- Rename `john-p14s` helper module keys to include the full host slug (`john-p14s-*`) if strict slug consistency is desired.
|
||||
- Decide whether `soteria` should move to `default.nix` and whether its standalone Home Manager key should include `john@`.
|
||||
- If `john-pc-ubuntu` ever becomes changeable, decide whether to rename the directory to match or keep this as a permanent exception.
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
description: "Use when defining or modifying hosts and host-local data under modules/hosts. Explains how this repo exports nixosConfigurations and homeConfigurations, when to create flake.modules.nixos or flake.modules.homeManager helpers, and how secrets, defaults, keys, and hardware files fit into a host definition."
|
||||
applyTo: 'modules/hosts/**/*.nix, modules/hosts/**/secrets.yaml, modules/hosts/**/defaults.json, modules/hosts/**/fingerprint, modules/hosts/**/*.pub'
|
||||
---
|
||||
|
||||
# Host Definitions
|
||||
|
||||
Host files under `modules/hosts` are flake entrypoints, not just loose Nix snippets. Because `modules` is auto-imported through `import-tree` and `flake-file`, files here usually export one or more of these attributes:
|
||||
|
||||
- `flake.nixosConfigurations.<name>` for a bootable NixOS machine or container.
|
||||
- `flake.homeConfigurations.<name>` for a standalone Home Manager target.
|
||||
- `flake.modules.nixos.<name>` or `flake.modules.homeManager.<name>` when a host is split into reusable host-local modules that are then assembled by a nearby entrypoint.
|
||||
|
||||
Follow the existing host patterns in this tree:
|
||||
|
||||
- Keep the host entrypoint file focused on composing modules into `nixosSystem` or `homeManagerConfiguration`.
|
||||
- If a host has substantial machine-specific logic, put that logic in sibling files such as `configuration.nix` or `hardware.nix`, export them as `flake.modules.nixos.<name>`, and have the entrypoint import those modules.
|
||||
- For Home Manager only hosts, define `flake.modules.homeManager.<name>` and then expose a matching `flake.homeConfigurations` entry.
|
||||
- Prefer composing from shared modules in `self.modules.nixos` or `inputs.self.modules.homeManager` instead of re-implementing shared behavior inline.
|
||||
|
||||
Treat host-local data as part of the host definition:
|
||||
|
||||
- Keep hardware-specific settings in the host directory, typically in `hardware.nix`.
|
||||
- Keep install-time or machine-specific configuration in the same host directory, typically in `configuration.nix`.
|
||||
- Keep host-local secret references beside the host and wire them through `sops.defaultSopsFile` and `mysops.hostSecretFile` instead of pointing at unrelated locations.
|
||||
- Preserve nearby auxiliary files such as `defaults.json`, fingerprints, public keys, and `secrets.yaml`; these are part of the host contract.
|
||||
|
||||
For host-adjacent data files:
|
||||
|
||||
- Keep `secrets.yaml` scoped to the host that consumes it; do not reuse another host's secret file as a shortcut.
|
||||
- Treat files such as `defaults.json`, `fingerprint`, and `*.pub` as inputs consumed by the host module. Update references together with the data when changing paths or filenames.
|
||||
- Avoid moving or renaming these files unless the corresponding Nix references are updated in the same change.
|
||||
|
||||
Naming in this tree is not perfectly uniform, so preserve existing interfaces unless the task is explicitly a rename:
|
||||
|
||||
- The directory name, exported flake key, and `networking.hostName` may differ.
|
||||
- Some hosts export from `default.nix`, while others keep the main definition in a differently named file such as `soteria.nix`.
|
||||
- Do not normalize names or move files just for consistency unless the user asks for that structural change.
|
||||
|
||||
Naming rules for new hosts:
|
||||
|
||||
- Treat the host directory name as the canonical host slug for new work.
|
||||
- Prefer `modules/hosts/<slug>/default.nix` as the main entrypoint for a host directory.
|
||||
- Use the same slug for the primary `hostname` binding, `flake.nixosConfigurations.<slug>`, and `networking.hostName` unless the task explicitly requires a different deployed hostname.
|
||||
- If the host exports a host-local Home Manager module, name it `flake.modules.homeManager.<slug>`.
|
||||
- For standalone Home Manager configurations, prefer `flake.homeConfigurations."<username>@<slug>"` so the exported key still carries the host slug.
|
||||
- Name host-local helper modules with the same slug as a prefix, for example `flake.modules.nixos.<slug>-hardware` or `flake.modules.nixos.<slug>-configuration`, to make ownership obvious and avoid collisions with shared modules.
|
||||
- Keep host-local secrets and auxiliary files under the same host slug directory. Do not point a new host at another host's path just because the contents are similar.
|
||||
|
||||
Allowed exception for immutable deployed hostnames:
|
||||
|
||||
- If the deployed hostname is externally constrained and cannot change, use that deployed hostname as the canonical slug for exported keys and `hostname` bindings, even if the directory name differs.
|
||||
- In that case, keep the exception explicit in comments or reports so future cleanup work does not accidentally rename a live hostname contract.
|
||||
|
||||
When working in existing hosts that predate these rules:
|
||||
|
||||
- Preserve the current public names by default.
|
||||
- If the user asks for a rename or cleanup, update the directory slug, exported flake keys, host-local module names, and `networking.hostName` together in one change so the host identity stays coherent.
|
||||
|
||||
Use nearby hosts as composition examples:
|
||||
|
||||
- `john-p14s` splits the reusable machine logic into `configuration.nix` and `hardware.nix`, exports `flake.modules.nixos.p14sConfiguration` and `flake.modules.nixos.p14sHardware`, and assembles them from `default.nix`.
|
||||
- `janus` defines a host-local reusable module (`flake.modules.nixos.janus-ca`) in the same file that also exports the final `flake.nixosConfigurations.janus` host.
|
||||
- `john-pc` is a Home Manager target, so it exports a `flake.modules.homeManager` module and a `flake.homeConfigurations` entry rather than a `nixosConfiguration`.
|
||||
- `soteria` combines a NixOS host, a host-local Home Manager module, and host-local secrets in one host directory.
|
||||
|
||||
When adding or changing a host:
|
||||
|
||||
- Mirror the local style first. Small hosts may define the full configuration inline; larger hosts should split reusable modules out.
|
||||
- Keep `let` bindings such as `username`, `hostname`, `flakeDir`, and package aliases near the top when the file already follows that pattern.
|
||||
- Add shared behavior by importing existing modules, not by copying option blocks between hosts.
|
||||
- If the change affects both system and Home Manager state for a host, update both sides in the same host area when that host already models both.
|
||||
|
||||
For reviews and answers, distinguish between these layers:
|
||||
|
||||
- host entrypoint wiring in `modules/hosts/...`
|
||||
- reusable shared modules in `modules/nixos`, `modules/programs`, `modules/services`, and `modules/users`
|
||||
- host-local modules exported from a host directory and then consumed by its entrypoint
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
description: "Use when modifying the Janus host, Janus step-ca deployment, Step SSH CA, mTLS issuance, CA bootstrap artifacts, or Janus SOPS secrets. Covers idiomatic Nix, secret placement, and troubleshooting."
|
||||
name: "Janus Host Instructions"
|
||||
applyTo: "modules/hosts/janus/**, modules/services/step-ca/step-ca.nix, modules/features/step-client.nix"
|
||||
---
|
||||
|
||||
# Janus Host Instructions
|
||||
|
||||
Use this instruction when changing Janus host wiring, Janus CA material, or the shared Step CA module that Janus consumes.
|
||||
|
||||
## Host Intent And Boundaries
|
||||
|
||||
- Treat Janus as the homelab certificate authority host. It runs `step-ca` for X.509 issuance, SSH user and host certificates, and mTLS certificates.
|
||||
- Assume Janus CA endpoints are private/LAN-only unless the task explicitly changes the exposure model.
|
||||
- Keep Janus as a concrete host under `modules/hosts/janus`; keep reusable behavior in shared modules such as `modules/services/step-ca/step-ca.nix`, `modules/services/step-ca/ssh-host.nix`, `modules/features/step-client.nix`, and `modules/features/mtls.nix`.
|
||||
- Preserve host identity unless the task explicitly renames the deployed CA: `hostname = "janus"`, `flake.nixosConfigurations."${hostname}"`, and `networking.hostName = hostname`.
|
||||
- Keep Janus-specific paths, DNS names, IP SANs, and CA artifacts in the Janus host area or in Janus-specific option values. Do not move them into generic shared modules unless they become a deliberate module interface.
|
||||
|
||||
## Required Module Composition
|
||||
|
||||
When editing `modules/hosts/janus/default.nix`, preserve this composition unless the task explicitly changes Janus architecture:
|
||||
|
||||
- `nixos.lxc` for the container host shape.
|
||||
- `nixos.mysops` with `sops.defaultSopsFile = ./secrets.yaml` for host-local encrypted CA runtime material.
|
||||
- `nixos.step-ca` to run the CA daemon and render `/etc/smallstep/ca.json` from Nix plus SOPS paths.
|
||||
- `nixos.step-client` to install Step trust bootstrap defaults and root CA material.
|
||||
- `nixos.ssh-certs` so Janus itself participates in SSH host certificate flow.
|
||||
- `nixos.mtls` so Janus can issue and renew its own mTLS certificate bundle.
|
||||
|
||||
## Step CA Deployment Pattern
|
||||
|
||||
- Prefer structured Nix values rendered with `builtins.toJSON` over hand-written JSON strings for `ca.json`.
|
||||
- Prefer the NixOS `services.step-ca.settings` interface for normal `ca.json` settings. Use a `sops.templates`-rendered config only when the rendered JSON needs sops-nix placeholders or runtime secret paths.
|
||||
- Keep secret-bearing `ca.json` content rendered through `sops.templates` when it includes values from `config.sops.placeholder`; do not put decrypted private keys, passwords, or provisioner encrypted private-key values directly in the Nix store.
|
||||
- Continue using `services.step-ca` for the daemon contract: `enable`, `settings`, `address`, `port`, `openFirewall`, `package`, and `intermediatePasswordFile`.
|
||||
- Remember that the NixOS module overrides `services.step-ca.settings.address` with `services.step-ca.address` plus `services.step-ca.port`. Change the high-level address/port options when changing the listener.
|
||||
- Keep the intermediate password as a runtime string path from `config.sops.secrets.<name>.path`; do not use a Nix path literal or the password file can be copied to the globally readable Nix store. Do not pass passwords through command-line arguments or environment variables.
|
||||
- Account for upstream NixOS service behavior when debugging: the module writes `/etc/smallstep/ca.json`, starts `step-ca.service` as `Type=notify`, sets `DynamicUser = true`, uses `StateDirectory = "step-ca"`, and passes `intermediatePasswordFile` through systemd `LoadCredential`.
|
||||
- If `step-ca` cannot read key files referenced by `ca.json`, check sops-nix owner/group/mode and systemd credential handling before loosening permissions. The password file path and CA signing key paths are separate contracts.
|
||||
- Keep Step CA runtime state in `/var/lib/step-ca/db` unless the task explicitly migrates storage. If moving away from embedded Badger, document database backup and HA implications.
|
||||
- Keep `root`, `crt`, `key`, `ssh.hostKey`, and `ssh.userKey` in `ca.json` pointing at public cert paths or sops-nix materialized secret paths as appropriate.
|
||||
- Use an ACME provisioner for automated X.509 issuance, an `SSHPOP` provisioner for SSH certificate renewal/rekey behavior, and the `admin` JWK provisioner for explicit administrative or scripted issuance. Ensure JWK claims include `enableSSHCA = true` when it is expected to sign SSH certificates.
|
||||
- Treat Nix and SOPS as the source of truth. If `step ca provisioner add`, `remove`, or `update` is used for investigation, copy the intended result back into Nix/SOPS and reload or restart `step-ca`; do not leave live `ca.json` drift on the host.
|
||||
- Use `authority.claims` or provisioner-level claims for certificate lifetimes, renewal behavior, and SSH CA enablement. Avoid `allowRenewalAfterExpiry` unless the task explicitly accepts the added risk for intermittently connected clients.
|
||||
- Add `authority.policy` when constraining which X.509 SANs or SSH principals Janus may issue. This is mandatory for any move away from private/LAN-only operation.
|
||||
|
||||
## CA Material And Secret Placement
|
||||
|
||||
Store each kind of material in the narrowest place that matches who needs it:
|
||||
|
||||
- `modules/hosts/janus/secrets.yaml`: Janus runtime CA secrets used by `step-ca`, including `janus/ca_password`, `janus/intermediate_ca_key`, `janus/ssh_host_ca_key`, `janus/ssh_user_ca_key`, and `janus/admin_provisioner_encrypted_key`.
|
||||
- `keys/secrets.yaml`: shared provisioner credential `janus/admin_jwk`, because client and host certificate modules outside Janus consume it to request certs.
|
||||
- `modules/hosts/janus/root_ca.crt`, `intermediate_ca.crt`, `fingerprint`, `ssh_user_ca_key.pub`, and `ssh_host_ca_key.pub`: public trust artifacts that may be committed and reviewed.
|
||||
- Offline secret storage only: `root_ca_key` and the root key password. The running CA should not need the root private key for normal operation.
|
||||
|
||||
When editing secrets:
|
||||
|
||||
- Use SOPS commands (`sops`, `edit-secrets`, or `sops set`) rather than editing `ENC[...]` payloads.
|
||||
- Use YAML literal blocks for PEM or OpenSSH private keys so newlines are preserved.
|
||||
- After changing Janus recipient rules, run `sops --config .sops.yaml updatekeys -y modules/hosts/janus/secrets.yaml` so the host-local file is actually rewrapped.
|
||||
- Keep `.sops.yaml` scoped: Janus host runtime secrets should match `modules/hosts/janus/secrets.yaml`; shared credentials should match `keys/secrets.yaml`.
|
||||
|
||||
## Public Trust Artifacts
|
||||
|
||||
- If `root_ca.crt` changes, update `fingerprint` from `step certificate fingerprint <root_ca.crt>` and update `modules/features/step-client.nix` defaults in the same change.
|
||||
- If `intermediate_ca.crt` changes, confirm it still chains to the committed root and that the running CA uses the matching `intermediate_ca_key` from SOPS.
|
||||
- If SSH CA keys rotate, update the matching public key files and every SSH trust consumer, including `modules/services/ssh.nix` known-host CA values and `TrustedUserCAKeys` behavior.
|
||||
- Treat `README.md` bootstrap commands as part of the operational contract. Keep them aligned with secret names, public artifact paths, CA URL, DNS names, and IP SANs.
|
||||
|
||||
## mTLS And Client Trust
|
||||
|
||||
- Janus mTLS values should stay aligned with Step CA identity: update `step-ca.dnsNames`, `mtls.subject`, `mtls.san`, and Step client defaults together when CA names or addresses change.
|
||||
- `modules/features/step-client.nix` is the shared bootstrap surface. Its default CA URL, root certificate, and fingerprint should describe Janus unless the repo intentionally migrates to another CA.
|
||||
- mTLS certificate files are runtime outputs under the configured cert directory, not tracked source files. Do not store mTLS private keys in Git or the Nix store.
|
||||
|
||||
## Step/Nix Troubleshooting Checklist
|
||||
|
||||
Start with the smallest check that matches the change:
|
||||
|
||||
1. Host evaluation: `nix eval .#nixosConfigurations.janus.config.system.build.toplevel.drvPath`
|
||||
2. Build planning: `nix build .#nixosConfigurations.janus.config.system.build.toplevel --no-link --dry-run`
|
||||
3. Secret contract: confirm every `step-ca.secrets.*` value has a matching `sops.secrets` declaration and a matching key in `modules/hosts/janus/secrets.yaml`.
|
||||
4. Rendered config: on the host, inspect `/etc/smallstep/ca.json` or `/etc/step-ca` paths without copying secret content into logs.
|
||||
5. Service health: check `systemctl status step-ca.service`, `journalctl -u step-ca.service`, and `step ca health --ca-url https://janus.john-stream.com` after trust is bootstrapped.
|
||||
6. Provisioners: use `step ca provisioner list` to confirm ACME, SSHPOP, and admin JWK availability.
|
||||
7. SSH cert flow: use `ssh-host-cert-check`, `systemctl status ssh-certs-renew.service`, and `systemctl status ssh-certs-renew.timer`.
|
||||
8. mTLS flow: use `mtls-check`, `mtls-generate`, and `systemctl status mtls-renew.timer` for host certificate renewal issues.
|
||||
|
||||
## Change Safety Rules
|
||||
|
||||
- Do not deploy a root private key to Janus unless the task explicitly changes the trust model.
|
||||
- Do not rename `janus/admin_jwk` without updating all consumers in the same change.
|
||||
- Do not expose ACME or `/provisioners` endpoints publicly without an explicit migration away from the private/LAN-only model plus policy and endpoint exposure review.
|
||||
- Do not use certificate templates casually; if templates are added, keep them narrow and use `toJson` for user-controlled values.
|
||||
- If changing certificate durations, prefer short-lived leaf certs with automated renewal over long-lived leaf certs.
|
||||
- If changing service ports or proxying, account for Step renewal behavior: mTLS renewal is sensitive to layer-7 proxies unless configured for token-based renewal.
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
description: "Use when defining or modifying reusable Nix modules outside modules/hosts. Covers flake.modules.nixos, flake.modules.homeManager, and flake.factory.user exports, and explains how shared modules differ from concrete host wiring."
|
||||
applyTo: 'modules/nixos/**/*.nix, modules/users/**/*.nix, modules/features/**/*.nix, modules/programs/**/*.nix, modules/services/**/*.nix, modules/nix-tools/**/*.nix'
|
||||
---
|
||||
|
||||
# Shared Modules
|
||||
|
||||
Files outside `modules/hosts` generally define reusable building blocks, not concrete machines. In this repo, those files usually export one of these surfaces:
|
||||
|
||||
- `flake.modules.nixos.<name>` for reusable NixOS modules.
|
||||
- `flake.modules.homeManager.<name>` for reusable Home Manager modules.
|
||||
- `config.flake.factory.user` for factories that generate per-user module sets.
|
||||
- `flake.meta.*` when the file owns reusable metadata that other modules consume.
|
||||
|
||||
Keep the boundary between shared modules and hosts clear:
|
||||
|
||||
- Shared modules should not define `flake.nixosConfigurations.<name>` or `flake.homeConfigurations.<name>`; those belong in `modules/hosts`.
|
||||
- Prefer generic options, imports, and composition over host-specific literals.
|
||||
- If a setting only makes sense for one machine, keep it in that host directory instead of moving it into a shared module.
|
||||
- When a host imports a shared module, treat the shared module as part of the stable interface that multiple hosts may depend on.
|
||||
|
||||
Follow the existing export patterns in this repo:
|
||||
|
||||
- Simple reusable modules may export a single module directly, such as `flake.modules.nixos.games`.
|
||||
- Cross-cutting features often live under `modules/features` even when they export `flake.modules.nixos.*` or `flake.modules.homeManager.*`.
|
||||
- Program and service integrations commonly export one or both module types from a single file.
|
||||
- User definitions under `modules/users` may export metadata plus paired NixOS and Home Manager modules for the same user.
|
||||
|
||||
For user modules specifically:
|
||||
|
||||
- Keep reusable user facts under `flake.meta.users.<name>` when other modules need to reference them.
|
||||
- Prefer deriving the NixOS side from `self.factory.user` when the file already follows that pattern.
|
||||
- Keep the user-facing Home Manager module in `flake.modules.homeManager.<name>` and let the factory or host wire it into a concrete configuration.
|
||||
- Put user-specific authorized keys, identity, and shared defaults here rather than duplicating them across hosts.
|
||||
|
||||
Design shared modules as composable interfaces:
|
||||
|
||||
- Import other shared modules instead of copying option blocks.
|
||||
- Add options or parameters when behavior needs to vary between hosts.
|
||||
- Avoid embedding host-specific paths, hostnames, addresses, or secret file locations unless the file is intentionally host-local.
|
||||
- Preserve exported attribute names even when the filename is different. In this repo, the path is not always the public API name.
|
||||
|
||||
Use nearby files as examples:
|
||||
|
||||
- `modules/nixos/games.nix` is a minimal shared NixOS module with no host wiring.
|
||||
- `modules/features/nixos-base.nix` defines a reusable base system module that other hosts import.
|
||||
- `modules/users/john.nix` combines `flake.meta.users.john`, a reusable NixOS user module, and a reusable Home Manager user module built around the user factory.
|
||||
- `modules/nix-tools/user.nix` defines the `flake.factory.user` helper that shared user modules build on.
|
||||
|
||||
For reviews and answers, separate these concerns clearly:
|
||||
|
||||
- reusable module API and option design in shared module directories
|
||||
- concrete host assembly in `modules/hosts`
|
||||
- whether a change increases reuse or accidentally pulls machine-specific behavior into a shared layer
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
description: "Use when working with SOPS in this repo: editing secrets, adding multiline values, changing recipients, or wiring sops-nix consumers. Captures repo conventions, safe commands, and validation habits."
|
||||
applyTo: ".sops.yaml, keys/secrets.yaml, modules/hosts/**/secrets.yaml, modules/hosts/**/*.nix, modules/programs/sops.nix, modules/features/**/*.nix, modules/services/**/*.nix"
|
||||
---
|
||||
|
||||
# Using SOPS In This Repo
|
||||
|
||||
This repo treats SOPS files as the source of encrypted runtime material, and Nix code as the wiring that exposes those secrets to services. Keep those roles separate: edit secret values with SOPS, consume them through sops-nix paths, and keep recipient policy scoped to the machines or operators that actually need access.
|
||||
|
||||
## Mental Model
|
||||
|
||||
- Secrets live in encrypted YAML files; Nix modules should refer to secret paths, not decrypted values.
|
||||
- Host-only credentials belong in `modules/hosts/<host>/secrets.yaml`.
|
||||
- Shared credentials belong in `keys/secrets.yaml` only when multiple hosts intentionally consume the same material.
|
||||
- `.sops.yaml` controls who can decrypt each file; keep those recipient sets narrow and explicit.
|
||||
- Secret key names are an interface. Rename them only when every consumer changes in the same edit.
|
||||
|
||||
## Editing Secrets
|
||||
|
||||
- Use SOPS tooling for all value changes: `sops <file>`, `edit-secrets`, or `sops set ...`.
|
||||
- Do not hand-edit `ENC[...]` payloads. That bypasses SOPS and breaks the encrypted document's integrity metadata.
|
||||
- Keep plaintext out of tracked files. Temporary plaintext files are acceptable only as local working inputs and should be removed after use.
|
||||
- Prefer `SOPS_EDITOR=nvim sops <file>` for interactive edits when editor choice matters.
|
||||
|
||||
For large or generated values, avoid putting the secret directly in shell history:
|
||||
|
||||
```bash
|
||||
sops set --value-file <secrets-file> '["parent"]["key"]' /path/to/plaintext-value
|
||||
```
|
||||
|
||||
If using inline `sops set`, the value argument must be valid JSON.
|
||||
|
||||
## Multiline Values
|
||||
|
||||
Use YAML literal blocks for private keys, certificates, provisioner keys, and other values where newlines matter:
|
||||
|
||||
```yaml
|
||||
janus:
|
||||
ssh_user_ca_key: |-
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
...
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
```
|
||||
|
||||
- Use `|-` when the final trailing newline should be stripped.
|
||||
- Use `|` when the consuming program expects the final trailing newline.
|
||||
- Avoid folded style (`>`) for keys and certs because it rewrites line breaks.
|
||||
|
||||
## Wiring Secrets Into Nix
|
||||
|
||||
- Consume secrets through `config.sops.secrets."<key>".path` or `config.sops.placeholder` where templating requires it.
|
||||
- Do not read decrypted secret contents during Nix evaluation.
|
||||
- Set `owner`, `group`, and `mode` explicitly for non-root services.
|
||||
- Use `path` or `sops.templates` when a service needs a specific file layout instead of copying secret contents into the store or tracked files.
|
||||
- If moving a secret between shared and host-local files, update `sops.defaultSopsFile`, `mysops.hostSecretFile`, and every affected `sops.secrets` declaration together.
|
||||
|
||||
## Recipient And Rotation Practices
|
||||
|
||||
- Add dedicated `.sops.yaml` `path_regex` rules for host-local files before broad fallback rules.
|
||||
- Keep broad fallback rules conservative; do not expand them to grant casual access to every YAML or JSON file.
|
||||
- Run `sops updatekeys <file>` after changing recipients so data is rewrapped for the new key set.
|
||||
- Rotate or migrate in order: update recipients, re-encrypt affected files, update consumers, then validate affected hosts.
|
||||
|
||||
## Validation Habits
|
||||
|
||||
After SOPS or sops-nix changes, check the contract rather than only the syntax:
|
||||
|
||||
1. The expected secrets file is still selected by `sops.defaultSopsFile` or `mysops.hostSecretFile`.
|
||||
2. `.sops.yaml` has a specific rule for any host-local `secrets.yaml` touched.
|
||||
3. Secret names in YAML still match the `sops.secrets."<key>"` declarations.
|
||||
4. Services that need restart or reload behavior have `restartUnits` or `reloadUnits` set.
|
||||
5. Affected host evaluation/build catches missing secret keys before deployment.
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
description: "Use when modifying the Soteria host definition or host-local secrets under modules/hosts/soteria. Covers host wiring and SSH cert mechanics specific to Soteria."
|
||||
name: "Soteria Host Instructions"
|
||||
applyTo: "modules/hosts/soteria/**/*.nix, modules/hosts/soteria/secrets.yaml"
|
||||
---
|
||||
|
||||
# Soteria Host Instructions
|
||||
|
||||
Use this instruction when changing Soteria host wiring in `modules/hosts/soteria`.
|
||||
|
||||
## Host Intent And Shape
|
||||
|
||||
- Treat Soteria as a NixOS service node for Forgejo, RESTic server, mTLS, and a paired Home Manager profile for `john`.
|
||||
- Keep host identity aligned: `hostname = "soteria"`, `networking.hostName = hostname`, and `flake.nixosConfigurations."${hostname}"`.
|
||||
- Keep host-local secrets in `modules/hosts/soteria/secrets.yaml` and wire with `sops.defaultSopsFile = ./secrets.yaml`.
|
||||
- Keep host-specific Home Manager wiring in `flake.modules.homeManager.soteria`, and keep `mysops.hostSecretFile` pointing at the Soteria secrets file.
|
||||
|
||||
## Required Module Composition
|
||||
|
||||
When editing `modules/hosts/soteria/default.nix`, preserve this composition unless the task explicitly changes architecture:
|
||||
|
||||
- Import `nixos.ssh-certs` to enable host SSH cert integration.
|
||||
- Import `nixos.janus-ca` so Step client trust material and defaults remain available.
|
||||
- Import `nixos.restic-server` so the RESTic REST server implementation lives in the shared module rather than inline host wiring.
|
||||
- Keep `ssh-certs.hostname = hostname;` so cert principals match host naming.
|
||||
|
||||
## SSH Cert Mechanics On Soteria
|
||||
|
||||
Soteria SSH certificate behavior is the result of multiple modules. Keep this flow intact:
|
||||
|
||||
1. `soteria/default.nix` imports `nixos.ssh-certs` and sets `ssh-certs.hostname = "soteria"` (via `hostname`).
|
||||
2. `modules/services/step-ca/ssh-host.nix`:
|
||||
- Enables `ssh.certificates.enable = true`.
|
||||
- Requires `sops.secrets."janus/admin_jwk"` (provisioner credential) from Soteria's `secrets.yaml`.
|
||||
- Defines cert paths at `/etc/ssh/ssh_host_ed25519_key` and `/etc/ssh/ssh_host_ed25519_key-cert.pub`.
|
||||
- Exposes `ssh-host-cert-renew` and `ssh-host-cert-check` helper binaries.
|
||||
- Schedules `ssh-certs-renew.timer` every 4h with jitter.
|
||||
3. `modules/services/ssh.nix` consumes `ssh.certificates.enable` and configures OpenSSH to:
|
||||
- Set `TrustedUserCAKeys = /etc/ssh/ssh_user_ca_key.pub`.
|
||||
- Set `HostCertificate = /etc/ssh/ssh_host_ed25519_key-cert.pub`.
|
||||
- Install the trusted user CA file into `/etc/ssh/ssh_user_ca_key.pub`.
|
||||
4. `nixos.janus-ca` provides Step CA trust bootstrapping (`/etc/step-ca/defaults.json` and linked root CA material), allowing Step CLI operations to trust and reach the CA endpoint.
|
||||
|
||||
Current implementation note:
|
||||
|
||||
- `ssh-certs-renew.service` currently checks renewal state via `step ssh needs-renewal` and logs status. It does not directly invoke `ssh-host-cert-renew` in the service script. Preserve this behavior unless the task explicitly asks to change renewal execution semantics.
|
||||
|
||||
## SOPS/Secrets Mechanics On Soteria
|
||||
|
||||
Soteria secret handling spans host-local secrets, NixOS secret materialization, and Home Manager secret tooling. Keep this flow intact:
|
||||
|
||||
1. `modules/hosts/soteria/default.nix` imports `nixos.mysops` and sets `sops.defaultSopsFile = ./secrets.yaml`.
|
||||
2. The SOPS file `modules/hosts/soteria/secrets.yaml` is the canonical encrypted source for this host's system secrets.
|
||||
3. Shared modules imported by Soteria declare required secret entries under `sops.secrets` and consume them through `config.sops.secrets.<name>.path`:
|
||||
- `modules/services/step-ca/ssh-host.nix` declares and consumes `janus/admin_jwk` as the Step provisioner password file.
|
||||
- `modules/features/forgejo.nix` declares and consumes `forgejo/secret_key`, `forgejo/internal_token`, `forgejo/jwt_secret`, and `forgejo/lfs_jwt_secret`.
|
||||
4. Secret ownership is module-defined and must stay aligned with service users:
|
||||
- Forgejo secrets are owned by `config.services.forgejo.user`.
|
||||
- Step SSH provisioner secret is locked to root ownership and `0400` mode.
|
||||
5. `flake.modules.homeManager.soteria` imports `homeManager.mysops` and sets `mysops.hostSecretFile` to the same Soteria host secret file path. This drives helper tooling such as `edit-secrets` while Home Manager keeps its own default SOPS context from `modules/programs/sops.nix`.
|
||||
|
||||
Current implementation note:
|
||||
|
||||
- `restic_password` exists in `modules/hosts/soteria/secrets.yaml`, but it is not currently consumed by the Soteria NixOS module graph.
|
||||
- Changing, deleting, or renaming seemingly unused keys in `secrets.yaml` should be treated as a compatibility change and confirmed by the task.
|
||||
|
||||
## Restic REST Server On Soteria
|
||||
|
||||
Soteria's RESTic REST server is implemented by the shared `nixos.restic-server` module and configured by host-local `resticServer` values. Trace it this way:
|
||||
|
||||
1. `modules/hosts/soteria/default.nix` imports `nixos.restic-server` and configures the host-local `resticServer` option set with:
|
||||
- `enable = true`
|
||||
- `dataDir = "/mnt/restic"`
|
||||
- `privateRepos = true`
|
||||
- `listenAddress = "0.0.0.0:8000"`
|
||||
- `tls.certFile = config.mtls.certFile`
|
||||
- `tls.keyFile = config.mtls.keyFile`
|
||||
2. `modules/features/restic.nix` maps those `resticServer` options into `services.restic.server` and appends TLS flags when both TLS paths are set.
|
||||
3. The shared module keeps the server base behavior in one place:
|
||||
- `services.restic.server.enable = true`
|
||||
- `services.restic.server.dataDir = cfg.dataDir`
|
||||
- `services.restic.server.listenAddress = cfg.listenAddress`
|
||||
- `services.restic.server.privateRepos = cfg.privateRepos`
|
||||
- `services.restic.server.extraFlags = cfg.extraFlags ++ tlsFlags`
|
||||
4. `modules/features/restic.nix` also opens the matching TCP port through `networking.firewall.allowedTCPPorts`, deriving it from `resticServer.listenAddress`.
|
||||
5. The mTLS renewal hook includes `restic-rest-server.service` in `mtls.renew.reloadUnits`, so certificate rotation reloads the REST server alongside Forgejo.
|
||||
6. `loginText.extraServiceStatus` exposes the running unit as `restic-rest-server.service`, which is the service name to keep in mind for status and reload behavior.
|
||||
|
||||
### Storage Location
|
||||
|
||||
- Soteria explicitly sets `resticServer.dataDir = "/mnt/restic"`.
|
||||
- The shared module maps that value into `services.restic.server.dataDir`, so `/mnt/restic` is now the intended repository storage location for this host.
|
||||
- The shared module also derives the firewall port from `resticServer.listenAddress`, so Soteria no longer needs a separate `networking.firewall.allowedTCPPorts = [ 8000 ]` line.
|
||||
- If storage needs to move later, change the `resticServer.dataDir` input or the shared module contract, not ad hoc service cleanup logic.
|
||||
- `privateRepos = true` constrains repo exposure behavior, but it does not define storage location by itself.
|
||||
|
||||
Current implementation note:
|
||||
|
||||
- The REST server is TLS-protected but currently passed `--no-auth`, so client access control relies on transport/security model rather than rest-server password auth.
|
||||
- The shared module default listen address is not Soteria's deployed value; Soteria deliberately overrides it to `0.0.0.0:8000` in the host.
|
||||
|
||||
## Forgejo Implementation On Soteria
|
||||
|
||||
Soteria's Forgejo setup is split between host-local option values and the shared `nixos.forgejo` module. Trace it this way:
|
||||
|
||||
1. `modules/hosts/soteria/default.nix` imports `nixos.forgejo` and enables it with:
|
||||
- `forgejo.enable = true`
|
||||
- `forgejo.root_url = "https://forgejo.john-stream.com"`
|
||||
- `forgejo.https = true`
|
||||
- `forgejo.port = 443`
|
||||
2. `modules/features/forgejo.nix` defines `forgejo.port` as the controlling option for the web listener. Its default is `3000`, but Soteria overrides it to `443`.
|
||||
3. The shared module wires that option into multiple places:
|
||||
- `services.forgejo.settings.server.HTTP_PORT = cfg.port`
|
||||
- `networking.firewall.allowedTCPPorts = [ cfg.port ]` when `openFirewall = true`
|
||||
- If `cfg.port < 1024`, the Forgejo systemd unit gets `CAP_NET_BIND_SERVICE` so it can bind a privileged port.
|
||||
4. HTTPS on Soteria is terminated directly by Forgejo using mTLS-managed files:
|
||||
- `services.forgejo.settings.server.PROTOCOL = "https"`
|
||||
- `KEY_FILE = config.mtls.keyFile`
|
||||
- `CERT_FILE = config.mtls.certFile`
|
||||
|
||||
### Storage Locations
|
||||
|
||||
- This repo does not override Forgejo's primary state directory in the Soteria host or in `modules/features/forgejo.nix`.
|
||||
- That means Forgejo storage follows the underlying NixOS `services.forgejo` defaults, exposed in this config as `config.services.forgejo.stateDir`.
|
||||
- PostgreSQL storage is also left at the underlying module default and is referenced as `config.services.postgresql.dataDir`.
|
||||
- The cleanup helpers in `modules/features/forgejo.nix` confirm these are the intended storage anchors because they remove exactly `config.services.forgejo.stateDir` and `config.services.postgresql.dataDir`.
|
||||
- Forgejo backup dumps are generated by `forgejo-dump.service` using `--work-path ${config.services.forgejo.dump.backupDir}`, so dump staging/output follows the Forgejo module's backup directory setting rather than a Soteria-specific path override.
|
||||
|
||||
Current implementation note:
|
||||
|
||||
- If you need to change Forgejo storage location on Soteria, do it by setting the underlying Forgejo or PostgreSQL service directory options explicitly, not by changing cleanup scripts alone.
|
||||
- If you need to change the external Forgejo endpoint, update `forgejo.root_url`, `forgejo.port`, and the Soteria mTLS SAN list together.
|
||||
|
||||
## Change Safety Rules
|
||||
|
||||
- Do not rename the secret key `janus/admin_jwk` without updating all consumers.
|
||||
- Do not change SSH host key/cert filenames unless OpenSSH `HostKey`/`HostCertificate` paths are updated together.
|
||||
- If changing hostnames or domains, update cert principals in `ssh-certs` and corresponding SSH client targets together.
|
||||
- If adding or removing `nixos.ssh-certs`, explain impact on SSH cert issuance, renewal, and trust in the change summary.
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
description: "Use when modifying modules/services/step-ca/ssh-host.nix. Covers Step SSH host certificate wiring, secret key contract, and host-side expectations required by consumers."
|
||||
name: "Step SSH Host Module Instructions"
|
||||
applyTo: "modules/services/step-ca/ssh-host.nix"
|
||||
---
|
||||
|
||||
# Step SSH Host Module Instructions
|
||||
|
||||
Use this instruction when changing the shared NixOS module in modules/services/step-ca/ssh-host.nix.
|
||||
|
||||
## Module Contract
|
||||
|
||||
- Keep this module reusable across hosts; do not hard-code host-local file paths beyond SSH system paths under /etc/ssh.
|
||||
- Preserve the option interface unless the task explicitly changes it:
|
||||
- ssh-certs.hostname (required host identity used for cert principals)
|
||||
- ssh-certs.provisioner (default: "admin")
|
||||
- Keep imports = [ inputs.self.modules.nixos.ssh ] so OpenSSH host certificate settings stay composed through the shared SSH module.
|
||||
|
||||
## Secret Key Contract
|
||||
|
||||
- This module currently consumes `config.sops.secrets."janus/admin_jwk"` as the Step provisioner password file.
|
||||
- Treat `janus/admin_jwk` as a public contract key name for current consumers. If you rename it, update all consumers and host secret files in the same change.
|
||||
- Keep secret permissions strict (root ownership and 0400 mode).
|
||||
- Do not assume a local sops file path in this module. The consuming host must define sops.defaultSopsFile.
|
||||
|
||||
## Host Expectations
|
||||
|
||||
When this module is enabled by a host, the host is expected to provide:
|
||||
|
||||
- A working Step trust/bootstrap path (for example via janus-ca or equivalent trust material).
|
||||
- `sops.defaultSopsFile` containing `janus.admin_jwk` (or equivalent if contract is intentionally changed everywhere).
|
||||
- `ssh-certs.hostname` matching deployed host identity and DNS naming expectations.
|
||||
|
||||
## SSH Certificate Paths And Principals
|
||||
|
||||
- Preserve the default host key path contract at `/etc/ssh/ssh_host_ed25519_key` unless task scope requires coordinated changes.
|
||||
- Keep host certificate path aligned to `${sshKeyPath}-cert.pub`.
|
||||
- Keep principal issuance based on hostname and hostname.john-stream.com unless domain policy is intentionally migrated.
|
||||
- If changing key/cert filenames or principal naming, update `modules/services/ssh.nix` integration and host rollout notes in the same change.
|
||||
|
||||
## Renewal Behavior
|
||||
|
||||
- Maintain timer/service cadence unless explicitly requested:
|
||||
- timer OnUnitActiveSec = 4h
|
||||
- randomized delay enabled
|
||||
- Keep explicit handling for step ssh needs-renewal return codes to avoid silent failures.
|
||||
- If changing renewal semantics, document whether the service now only checks state or also performs certificate issuance.
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
After editing this module, validate at least one consuming host with:
|
||||
|
||||
1. nix eval .#nixosConfigurations.janus.config.system.build.toplevel.drvPath
|
||||
2. nix build .#nixosConfigurations.janus.config.system.build.toplevel --no-link --dry-run
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
description: "Use when modifying jsl-zsh, home-manager programs.zsh settings, or shell-tools wiring. Explains when to use wrapped jsl-zsh versus pkgs.zsh and how shell-tools composes into user and host configs."
|
||||
applyTo: 'modules/programs/zsh.nix, modules/features/shell-tools.nix, modules/users/**/*.nix, modules/nix-tools/user.nix, modules/services/ssh.nix, modules/hosts/**/*.nix'
|
||||
---
|
||||
|
||||
# jsl-zsh and shell-tools wiring
|
||||
|
||||
This repo intentionally uses two zsh variants for different layers:
|
||||
|
||||
- `pkgs.zsh` is the base system shell used for user login shell declarations.
|
||||
- `inputs.self.packages.<system>.jsl-zsh` is the wrapped interactive shell used by Home Manager and remote shell entrypoints.
|
||||
|
||||
Treat these as distinct roles, not interchangeable defaults.
|
||||
|
||||
## Canonical definitions
|
||||
|
||||
The canonical wrapped shell is defined in `modules/programs/zsh.nix`:
|
||||
|
||||
- `perSystem.packages.jsl-zsh` wraps zsh via `wrapperModules.zsh.apply`.
|
||||
- Wrapper settings, aliases, history behavior, devenv hook, and extra package PATH come from this package definition.
|
||||
- `binName = "jsl-zsh"` is part of the external contract; keep it stable unless performing an explicit repo-wide rename.
|
||||
|
||||
The Home Manager zsh module in the same file is the canonical consumer:
|
||||
|
||||
- `flake.modules.homeManager.zsh` sets `programs.zsh.package` to `self.packages.<system>.jsl-zsh`.
|
||||
- Keep Home Manager interactive zsh bound to the wrapped package, not plain `pkgs.zsh`.
|
||||
|
||||
## shell-tools composition
|
||||
|
||||
`modules/features/shell-tools.nix` is a composition module, not just a package list:
|
||||
|
||||
- `flake.modules.homeManager.shell-tools` imports `homeManager.zsh` and `homeManager.files`.
|
||||
- It adds the wrapped `shell-tools` package to `home.packages`.
|
||||
- `home.shell.enableShellIntegration = true` is expected to stay with this module.
|
||||
|
||||
If a user module imports `homeManager.shell-tools` (for example `modules/users/john.nix`), that user already receives the Home Manager zsh wiring transitively.
|
||||
|
||||
## System-layer expectations
|
||||
|
||||
`modules/nix-tools/user.nix` keeps system login semantics separate from Home Manager interactive behavior:
|
||||
|
||||
- `users.users.<name>.shell = pkgs.zsh` remains the login shell declaration.
|
||||
- `programs.zsh.enable = true` enables system zsh support.
|
||||
- `environment.shells` includes both `${lib.getExe pkgs.zsh}` and `${lib.getExe self.packages.<system>.jsl-zsh}` so wrapped shell paths are recognized when needed.
|
||||
|
||||
Do not replace login shell declarations with `jsl-zsh` unless the task is explicitly changing system login policy.
|
||||
|
||||
## Host and SSH usage contracts
|
||||
|
||||
Hosts may include `selfPkgs.jsl-zsh` in `environment.systemPackages` or `home.packages` so the wrapped shell is available directly.
|
||||
|
||||
`modules/services/ssh.nix` includes host entries that use `RemoteCommand = "~/.nix-profile/bin/jsl-zsh"` (and commented variants). This path-level usage means:
|
||||
|
||||
- keep the wrapper executable name stable (`jsl-zsh`),
|
||||
- update SSH remote command references in the same change if shell invocation paths are changed.
|
||||
|
||||
## Change guidelines
|
||||
|
||||
When editing this area:
|
||||
|
||||
- Keep wrapper behavior changes in `packages.jsl-zsh` focused and centralized.
|
||||
- Keep Home Manager zsh package selection pointed at wrapped `jsl-zsh`.
|
||||
- Keep system login shell semantics (`pkgs.zsh`) and interactive wrapper semantics (`jsl-zsh`) separated unless intentionally redesigning both layers.
|
||||
- If modifying `shell-tools`, verify it still composes zsh and tool packages for users importing the module.
|
||||
- If changing executable names or invocation paths, update all dependent consumers (including SSH remote commands and host package lists) in one coherent change.
|
||||
@@ -0,0 +1,3 @@
|
||||
result/
|
||||
.devenv/
|
||||
*.gz
|
||||
+17
-2
@@ -2,13 +2,28 @@ keys:
|
||||
- &john-p14s age1f6drjusg866yscj8029tk4yfpgecklrvezldm02ankm6h8nnwu5s2u6ahy
|
||||
- &john-pc age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
|
||||
- &test-nix age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
|
||||
- &soteria age1h0prahyukq4l564yqwgcpg3g6gdrjflk0suklussjjrjstxd9uesws8633
|
||||
- &soteria age1p4gyh5260ewp7t2ctzv5ewj4a06szl389fm4gzy6vltxhjj73ers87u33g
|
||||
- &janus age1qahhlzeanprtykym9jymk2t95uedr7cwx9sdshx46q2m6u66fucsqua8l3
|
||||
creation_rules:
|
||||
- path_regex: janus/secrets\.yaml$
|
||||
key_groups:
|
||||
- age:
|
||||
- *janus
|
||||
- *john-pc
|
||||
- *test-nix
|
||||
- path_regex: keys/secrets\.yaml$
|
||||
key_groups:
|
||||
- age:
|
||||
- *john-p14s
|
||||
- *john-pc
|
||||
- *test-nix
|
||||
- *soteria
|
||||
- *janus
|
||||
- path_regex: soteria/secrets\.yaml$
|
||||
key_groups:
|
||||
- age:
|
||||
- *john-pc
|
||||
- *soteria
|
||||
- *john-pc
|
||||
- *test-nix
|
||||
- path_regex: john-p14s/secrets\.yaml$
|
||||
key_groups:
|
||||
|
||||
@@ -12,6 +12,22 @@ home-manager switch --flake .#desktop
|
||||
nix flake show --all-systems
|
||||
```
|
||||
|
||||
```shell
|
||||
nix run nixpkgs#nh home switch -- --configuration john@john-pc-ubuntu .
|
||||
```
|
||||
|
||||
## Host Intent
|
||||
|
||||
| Host key | Type | Intent | Entrypoint |
|
||||
| -------- | ---- | ------ | ---------- |
|
||||
| `janus` | NixOS (LXC) | Base homelab/service host with local step-ca trust bootstrap (`janus-ca`) and mTLS support. | `modules/hosts/janus/default.nix` |
|
||||
| `john-kde` | Home Manager (generic Linux) | KDE-focused user environment profile for `john` with desktop + docker + step client tooling. | `modules/hosts/john-kde/default.nix` |
|
||||
| `john-p14s` | NixOS (laptop) | Main ThinkPad P14s NixOS workstation using hardware presets and desktop/dev modules. | `modules/hosts/john-p14s/default.nix` |
|
||||
| `john-pc-ubuntu` | Home Manager (generic Linux) | Ubuntu-hosted home profile for `john`, includes desktop/dev tooling and restic backup client settings. | `modules/hosts/john-pc/default.nix` |
|
||||
| `omen-nixos` | NixOS | Omen machine wired through shared `omen-nixos` + `john` modules. | `modules/hosts/omen-nixos/default.nix` |
|
||||
| `soteria` | NixOS + Home Manager | Service node running Forgejo + restic server with mTLS and host-local secrets. | `modules/hosts/soteria/soteria.nix` |
|
||||
| `test-nix` | NixOS (LXC) | Minimal sandbox/test target for validating module composition and remote push/switch flows. | `modules/hosts/test-nix.nix` |
|
||||
|
||||
## Layout
|
||||
|
||||
- Everything under `./modules` gets auto-imported by `import-tree`
|
||||
|
||||
Generated
+57
-97
@@ -3,11 +3,11 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1751685974,
|
||||
"narHash": "sha256-NKw96t+BgHIYzHUjkTK95FqYRVKB8DHpVhefWSz/kTw=",
|
||||
"lastModified": 1777699697,
|
||||
"narHash": "sha256-Eg9b/rq/ECYwNwEXs5i9wHyhxNI0JrYx2srdI2uZMaQ=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "549f2762aebeff29a2e5ece7a7dc0f955281a1d1",
|
||||
"revCount": 92,
|
||||
"rev": "382052b74656a369c5408822af3f2501e9b1af81",
|
||||
"revCount": 94,
|
||||
"type": "git",
|
||||
"url": "https://git.lix.systems/lix-project/flake-compat.git"
|
||||
},
|
||||
@@ -18,11 +18,11 @@
|
||||
},
|
||||
"flake-file": {
|
||||
"locked": {
|
||||
"lastModified": 1775848911,
|
||||
"narHash": "sha256-dqva/tlWxsXj32wYPdt06UrrR4l2QdK9JWyvPooWRi4=",
|
||||
"lastModified": 1781217157,
|
||||
"narHash": "sha256-N3q/SP2Ropk336e9KSgLh7kpROY6P70dprYdbPIfd5c=",
|
||||
"owner": "vic",
|
||||
"repo": "flake-file",
|
||||
"rev": "b36cbd5fc01e9794a001ccb0c58b314efaabae08",
|
||||
"rev": "ce63eaf7ebfe04a176653f66385a7f0a36380cee",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -34,15 +34,15 @@
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"nixpkgs-lib"
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775087534,
|
||||
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
|
||||
"lastModified": 1782949081,
|
||||
"narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
|
||||
"rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -59,11 +59,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1769996383,
|
||||
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
|
||||
"lastModified": 1778716662,
|
||||
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
|
||||
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -95,11 +95,11 @@
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776046499,
|
||||
"narHash": "sha256-Wzc4nn07/0RL21ypPHRzNDQZcjhIC8LaYo7QJQjM5T4=",
|
||||
"lastModified": 1783134515,
|
||||
"narHash": "sha256-qMoZazubXlXUD9k/syJ/aiWC4X4g73mwVmZ7Z4+rdpM=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "287f84846c1eb3b72c986f5f6bebcff0bd67440d",
|
||||
"rev": "b885baad531fa3d3beae2ba9a0712d22974d8016",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -110,11 +110,11 @@
|
||||
},
|
||||
"import-tree": {
|
||||
"locked": {
|
||||
"lastModified": 1773693634,
|
||||
"narHash": "sha256-BtZ2dtkBdSUnFPPFc+n0kcMbgaTxzFNPv2iaO326Ffg=",
|
||||
"lastModified": 1778781969,
|
||||
"narHash": "sha256-Jjuz5CmSkur8KvLDoGa+vylEp+RkQtv4mt/qcMznpH0=",
|
||||
"owner": "vic",
|
||||
"repo": "import-tree",
|
||||
"rev": "c41e7d58045f9057880b0d85e1152d6a4430dbf1",
|
||||
"rev": "d321337efd0f23a9eb14a42adb7b2c29313ab274",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -125,11 +125,11 @@
|
||||
},
|
||||
"mnw": {
|
||||
"locked": {
|
||||
"lastModified": 1770419553,
|
||||
"narHash": "sha256-b1XqsH7AtVf2dXmq2iyRr2NC1yG7skY7Z6N2MpWHlK4=",
|
||||
"lastModified": 1780772958,
|
||||
"narHash": "sha256-VKKe8r4pwCGWZ3Yr9CPN129R4S3CKLSrlYqdYz3vKpM=",
|
||||
"owner": "Gerg-L",
|
||||
"repo": "mnw",
|
||||
"rev": "2aaffa8030d0b262176146adbb6b0e6374ce2957",
|
||||
"rev": "0871dbf63a53610c95db04439ed8ea4d6ec9c160",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -138,28 +138,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"ndg": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nvf",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1768214250,
|
||||
"narHash": "sha256-hnBZDQWUxJV3KbtvyGW5BKLO/fAwydrxm5WHCWMQTbw=",
|
||||
"owner": "feel-co",
|
||||
"repo": "ndg",
|
||||
"rev": "a6bd3c1ce2668d096e4fdaaa03ad7f03ba1fbca8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "feel-co",
|
||||
"ref": "refs/tags/v2.6.0",
|
||||
"repo": "ndg",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixgl": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
@@ -184,11 +162,11 @@
|
||||
"nixos-hardware": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1775490113,
|
||||
"narHash": "sha256-2ZBhDNZZwYkRmefK5XLOusCJHnoeKkoN95hoSGgMxWM=",
|
||||
"lastModified": 1782562157,
|
||||
"narHash": "sha256-a7+T6QSeowynwZ1ZJJbP8T8ntAytvrui8kFGJmIZt2c=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixos-hardware",
|
||||
"rev": "c775c2772ba56e906cbeb4e0b2db19079ef11ff7",
|
||||
"rev": "a9cf7546a938c737b079e738de73934a13de9784",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -199,40 +177,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1775423009,
|
||||
"narHash": "sha256-vPKLpjhIVWdDrfiUM8atW6YkIggCEKdSAlJPzzhkQlw=",
|
||||
"lastModified": 1782175435,
|
||||
"narHash": "sha256-EMzXKmnOtBQ2MnvpiNOm7E+kOMvdPrIKaeg52Tip2Uk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "68d8aa3d661f0e6bd5862291b5bb263b2a6595c9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1775888245,
|
||||
"narHash": "sha256-qTVvODr6edFBLD2lncXPF8yTQeCafZUuKVtpV3Xb3yM=",
|
||||
"rev": "13043924aaa7375ce482ebe2494338e058282925",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre978638.13043924aaa7/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1775579569,
|
||||
"narHash": "sha256-/m3yyS/EnXqoPGBJYVy4jTOsirdgsEZ3JdN2gGkBr14=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "dfd9566f82a6e1d55c30f861879186440614696e",
|
||||
"rev": "89570f24e97e614aa34aa9ab1c927b6578a43775",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -242,23 +191,35 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1782948114,
|
||||
"narHash": "sha256-VGtEDss4T0hPTnlWiiaa29y/lXM9SxuSMVtzPYf3wS0=",
|
||||
"rev": "9e92285f211dad236540fd617d7e30e0b99bc0e1",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1026211.9e92285f211d/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"
|
||||
}
|
||||
},
|
||||
"nvf": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-parts": "flake-parts_2",
|
||||
"mnw": "mnw",
|
||||
"ndg": "ndg",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775892726,
|
||||
"narHash": "sha256-1TK1pe33cEHNvGW41TP5xAzrbG1Gp7LfyFL6c3+xf+I=",
|
||||
"lastModified": 1783164413,
|
||||
"narHash": "sha256-7DMamcvS35/tI+KZKXt45bHAq2Vnucl74Xetvp7DR3I=",
|
||||
"owner": "notashelf",
|
||||
"repo": "nvf",
|
||||
"rev": "5ab359ee7dfd3fa09a5c6f863efaf810bb9a9436",
|
||||
"rev": "c8386ccfbc7053d454fa2d12d716c6ebb4167d51",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -276,9 +237,6 @@
|
||||
"nixgl": "nixgl",
|
||||
"nixos-hardware": "nixos-hardware",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs-lib": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nvf": "nvf",
|
||||
"sops-nix": "sops-nix",
|
||||
"wrapper-modules": "wrapper-modules",
|
||||
@@ -292,11 +250,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775971308,
|
||||
"narHash": "sha256-VKp9bhVSm0bT6JWctFy06ocqxGGnWHi1NfoE90IgIcY=",
|
||||
"lastModified": 1783174389,
|
||||
"narHash": "sha256-aCWC8ngycU7OdJrU2+Je3qf+1a2ykuBvpPhZT/9tXMc=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "31ac5fe5d015f76b54058c69fcaebb66a55871a4",
|
||||
"rev": "f1406619a3884cd5c47992a70b8b35c9c0fcb4c9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -337,14 +295,16 @@
|
||||
},
|
||||
"wrapper-modules": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775940860,
|
||||
"narHash": "sha256-8Jxnn2uoFhm2H579ycVxFWDtrywJ6Mc8RmqWbZwk5So=",
|
||||
"lastModified": 1782135443,
|
||||
"narHash": "sha256-vAmbArdCyjqpVW+37aCy/PMBOLIqukUXLQuEKLwUhA4=",
|
||||
"owner": "BirdeeHub",
|
||||
"repo": "nix-wrapper-modules",
|
||||
"rev": "fb62851ffc5f6a4d53ebc00b93743e29b41e6224",
|
||||
"rev": "6e7f66fa2cdf4d63162580b438f7fcf87c28a46f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -360,11 +320,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775600302,
|
||||
"narHash": "sha256-2fgKImv78CXIcfo1RsY7EI4uMZ84x/MggA5rrusYc7c=",
|
||||
"lastModified": 1782375095,
|
||||
"narHash": "sha256-bRepNpAluz2i0IIk7Tmzw/4BkyOrwABvc66NEgXPEhA=",
|
||||
"owner": "lassulus",
|
||||
"repo": "wrappers",
|
||||
"rev": "9d8397d8ef1ac35763085f3338589f558128f7db",
|
||||
"rev": "cd780e92c0fa66c2e721e1d91894f3db13b9f2b4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
flake-file.url = "github:vic/flake-file";
|
||||
flake-parts = {
|
||||
url = "github:hercules-ci/flake-parts";
|
||||
inputs.nixpkgs-lib.follows = "nixpkgs-lib";
|
||||
inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||
};
|
||||
home-manager.url = "github:nix-community/home-manager";
|
||||
import-tree.url = "github:vic/import-tree";
|
||||
@@ -20,7 +20,6 @@
|
||||
flake = false;
|
||||
};
|
||||
nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
|
||||
nixpkgs-lib.follows = "nixpkgs";
|
||||
nvf = {
|
||||
url = "github:notashelf/nvf";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
@@ -29,7 +28,10 @@
|
||||
url = "github:Mic92/sops-nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
wrapper-modules.url = "github:BirdeeHub/nix-wrapper-modules";
|
||||
wrapper-modules = {
|
||||
url = "github:BirdeeHub/nix-wrapper-modules";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
wrappers = {
|
||||
url = "github:lassulus/wrappers";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
||||
+39
-21
@@ -9,33 +9,51 @@ api:
|
||||
gmail_client_secret: ENC[AES256_GCM,data:du2gEY5TQIwpUEvJKDWKY3noLRGeiKek4IMwPUusVx8NMys=,iv:hIYi1xQYf6+hDhK0pNprBYu6wXwRH2yOTwQg6pzQa0A=,tag:sqmQ5GCkKbHpIy2R+Y5G/A==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1f6drjusg866yscj8029tk4yfpgecklrvezldm02ankm6h8nnwu5s2u6ahy
|
||||
enc: |
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0UEpja2kxdThZVWZhOGVP
|
||||
S0NtSi84MjhnN0RORkh2NjZ4YlYvWS9kZDBNClFzYnVxWnhmQkpCRkRFVUx1RDdX
|
||||
ZHFqYXRqYXM0cWJzcU5EeEtSR1BUVzAKLS0tIDdEY2pnVTJqWlNZVkZldXVYVmFH
|
||||
dVNBRUVodU5sRnpVcG1GZ1RiZzhjTXMKefqBvvD/qZwcSHmFjUnleukVRLueG36Y
|
||||
Q81KlwQweF2F8kHl7Bqsi+3hH1dZZbVm3vjuGpWFOoti7fowUV55Kw==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5dTVZQ2hFanhqbVJXTmxG
|
||||
YUFFanBiTzgyek1WZW5YajlaL3hVNElORHpJCm1JN2hxell5dmJVczlBbVI2VktH
|
||||
Z21lelF6UDBHcXIyUDE0clpxcEV4MGsKLS0tIG0zMktBVEt1SFZ6aGFaNW0wdzlB
|
||||
QkpQc3lkNmZCd0d6ZVRCc1ZaWjFYaDAK9RXWeW6dCIhYfVGPywsHlRigORVMuVfl
|
||||
d+Evyp72wV5C1MnhV9jv8O4S8aQkcNae1PALacnFw0GJAI1iXn0bqw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
|
||||
enc: |
|
||||
recipient: age1f6drjusg866yscj8029tk4yfpgecklrvezldm02ankm6h8nnwu5s2u6ahy
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqZFZxbDhVUWFEUGhPMlZI
|
||||
SFdBYkpxSnAxTUZXbjVwQnlZQ3l1SWtuZGg0CmVBdnVHbTNUcmwvK01iMnZKZTJh
|
||||
ajFla3kzYUl4ZWY3czA0WUdNM2lpVFUKLS0tIHo5Uk1pV296MXdnUTZGQ25haWZG
|
||||
QWZDWGRaRDBhY1ZkZk5oTHY0ZVV2RXMKanv+WWRhf5nl+aw/T6QZFVQQmhV1DZfB
|
||||
jkSzOAKOgPx7toYFmpq9E8fAH+zrMzDbxI2z2uyrOFI6v+QE0Ul/iQ==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBTWt3MEFDL3JGOUw0LzNa
|
||||
RmNLMmFRRzh5NEtZWENjNTlrcDg1MHpjWWg0Cjl5THh3ckdyMWVqR0M2Q0Y0czUr
|
||||
Y0ZNRGVIbS9TNXlCcFo3R3lPZUl4cW8KLS0tIDN4QmhSYjBPMDZLWURhMndsK2hE
|
||||
WVY1T0JnZ3dmSjNIMDNBYmhMellvMWcKp15FwF7lJnHoZ/tLNQTGvwP7Kffj8hms
|
||||
6G5ZG2howsNFiRFFdo/uL08ShaycL2PAL/Kge/0pPL1ygiDaDLjGGg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
|
||||
enc: |
|
||||
recipient: age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZcGtMNi9RSG5aTVV5cWdT
|
||||
NittUXN0Qnpld1YvOU50OUh3Z3ZiSzhHOHdNCnc4TmdYbS9QQnBLbldHSytIdkJl
|
||||
R0psQWxkZTgyZTRzckkrTGpyNCsvR2sKLS0tIEdLb05aT2I2S3BKcFRrVmtvTGw5
|
||||
Z1orRCtkTDVXSktuck5pTmV4K05qZHMKZlHHu07q+GnyDDgdwW2Ic3P23PmoSPwn
|
||||
WuNLZdlZQleROaRb+zpD+9P1HGGJ3mWAlNlnmjGrRk453k1PbBQ5Og==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwT1FoTDNmRXM4Tk9HZHNu
|
||||
ZSszMEM1R0Z5bzY1ZzRvR3VGR2srVVdXbDBNCmp2c0tacndoQ3RiZUpNTnk1MTNH
|
||||
V0Q4ckhWaDBjemYxZEhrSGJHcVlwYzQKLS0tIHNDZkNrNmZvM2plNEp2TTJmVlpT
|
||||
U1JhN2MrT2NzeURuVzRIZlNEVEd0S0EKBcTG4hWKdXqNfajVZ5DOClOeZsFYktPb
|
||||
dAauLGwx1zkXbVxopUpH5+vCXp02kvvwgO9kiqJvSf0U+l/5VIp6Ww==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5bTVkK0pQL3kxZzdaN0xq
|
||||
YU9rVTNvQXE1V1d0Ykx1bEN3cXp4eWdKaWpjCkEzNUs0N1JlMjlrdnA4UUxIRkZ1
|
||||
aFl2Qk9PeTNTQ0grMjFJMzZvT05adGMKLS0tIEIwd3NyQ0xWWkVBMFl4Z0VUVFBY
|
||||
eWFlSFYrL1E1aXN2bVp5YTF5VXVOYUEK3NhyHGnY+a/Fj6TJFzljSX+49G9x5F3x
|
||||
M16Nx7JGsD5IV6GfdiMYSEwgT1pm0/XY8rhvWBO1fArkleMz1HKewg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1p4gyh5260ewp7t2ctzv5ewj4a06szl389fm4gzy6vltxhjj73ers87u33g
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwWGpwUkRueVhhcVErQ0ZF
|
||||
RXdxV1lMNzh6VFhubmc3TDBMaSsrYjhlS1Q0CnlZMmJSaXN5d1daRmpUVTlWeU1i
|
||||
Nkxsd3JoTGpoelhiTXBMclhoL3RKQlEKLS0tIHRyQjk0akN3WnhKKy82T0ZOYTNK
|
||||
QU5tYkZuTVc2Yzc0bzJrNXd1aWJMVlEKoMHFqDQYmT+q4eSNtmIb6drI/pFt9Z4W
|
||||
gHI/QxeWQq6BWssk4EAonRXhIE0iJPOsXz2zyT+dRRDqcKBod9h01w==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1qahhlzeanprtykym9jymk2t95uedr7cwx9sdshx46q2m6u66fucsqua8l3
|
||||
lastmodified: "2026-03-15T15:06:29Z"
|
||||
mac: ENC[AES256_GCM,data:cF/TJ8VkzrHRUrO5iGdRdlFtqV/5EQ15JwQKIywJvsh0NERK67T21czSP7923MiL0u5QTVPn/rO8R5E/8gBu3r8+fLq+CFl9PDQHEX2JhnYOD5WZR412WMZq3MVR94IMTOrQANMVpS4uhMyvnrqOe4AenxLDyzrYhkwf1KQh4w0=,iv:Qwy8z4uXGMlf+kTMNiE42M9l8LtSJ+O7diknRrsSeYI=,tag:qlCY9r8HnEDmq/jw59C/sg==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.modules.nixos.audio = { config, pkgs, lib, ... }: {
|
||||
# Enable sound with pipewire.
|
||||
services.pulseaudio.enable = false;
|
||||
security.rtkit.enable = true; # PulseAudio server uses this to acquire realtime priority.
|
||||
services.pipewire = {
|
||||
enable = true;
|
||||
alsa.enable = true;
|
||||
alsa.support32Bit = true;
|
||||
pulse.enable = true;
|
||||
# If you want to use JACK applications, uncomment this
|
||||
#jack.enable = true;
|
||||
|
||||
# use the example session manager (no others are packaged yet so this is enabled by default,
|
||||
# no need to redefine it in your config for now)
|
||||
# media-session.enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
# This module is for programs with GUIs that run in a desktop environment
|
||||
{ self, inputs, ... }: {
|
||||
flake.modules.homeManager.desktop = { config, pkgs, lib, ... }: {
|
||||
nixpkgs.config.problems.handlers.sublimetext4.broken = "ignore";
|
||||
imports = with inputs.self.modules.homeManager; [
|
||||
brave
|
||||
ghostty
|
||||
@@ -10,6 +11,10 @@
|
||||
home.packages = with pkgs; [
|
||||
mangohud
|
||||
sublime4
|
||||
proton-vpn
|
||||
joplin-desktop
|
||||
discord
|
||||
github-copilot-cli
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
desktopManager.gnome.enable = true;
|
||||
displayManager.gdm = {
|
||||
enable = true;
|
||||
wayland = true;
|
||||
banner = "Welcome to John's NixOS implementation";
|
||||
};
|
||||
udev.packages = [
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
# https://github.com/glabrie/dotfiles/blob/main/modules/system/settings/greetd.nix
|
||||
{ inputs, ... }: {
|
||||
flake.modules.nixos.greetd = { pkgs, lib, ... }: {
|
||||
flake.modules.nixos.greetd = { pkgs, lib, config, ... }:
|
||||
let
|
||||
niriPackage = if config.programs.niri.enable then config.programs.niri.package else pkgs.niri;
|
||||
niriExe = lib.getExe niriPackage;
|
||||
regreetExe = lib.getExe config.programs.regreet.package;
|
||||
greeterNiriConfig = pkgs.writeText "niri-greeter.kdl" ''
|
||||
spawn-at-startup "${regreetExe}"
|
||||
hotkey-overlay {
|
||||
skip-at-startup
|
||||
}
|
||||
'';
|
||||
in {
|
||||
programs.regreet.enable = true;
|
||||
|
||||
services.greetd = {
|
||||
enable = true;
|
||||
settings = {
|
||||
default_session = {
|
||||
command = "${lib.getExe pkgs.tuigreet} --time --remember --cmd niri-session";
|
||||
command = "${pkgs.dbus}/bin/dbus-run-session -- ${niriExe} --config ${greeterNiriConfig}";
|
||||
user = "greeter";
|
||||
};
|
||||
};
|
||||
|
||||
+311
-159
@@ -48,7 +48,7 @@ let
|
||||
enable = lib.mkOption {
|
||||
description = "Enable automatic mTLS certificate renewal using a systemd timer.";
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
default = cfg.enable;
|
||||
};
|
||||
onCalendar = lib.mkOption {
|
||||
description = "systemd OnCalendar schedule for mTLS certificate renewal checks.";
|
||||
@@ -115,154 +115,27 @@ let
|
||||
'';
|
||||
};
|
||||
|
||||
mkMtlsCheckScript = { pkgs, bundleFile }: pkgs.writeShellApplication {
|
||||
name = "mtls-check";
|
||||
runtimeInputs = with pkgs; [ openssl ];
|
||||
text = ''
|
||||
openssl x509 -noout -in ${bundleFile} \
|
||||
-subject -issuer \
|
||||
-ext subjectAltName,extendedKeyUsage \
|
||||
-enddate
|
||||
'';
|
||||
};
|
||||
|
||||
mkMtlsRenewScript = {
|
||||
pkgs,
|
||||
cfg,
|
||||
systemctlArgs ? [ ],
|
||||
}:
|
||||
let
|
||||
systemctlCmd = "systemctl ${lib.escapeShellArgs systemctlArgs}";
|
||||
|
||||
hasReloadUnits = cfg.renew.reloadUnits != [ ];
|
||||
renewReloadScript = lib.concatMapStringsSep "\n" (unit: ''
|
||||
if ${systemctlCmd} --quiet is-active "${unit}"; then
|
||||
${systemctlCmd} try-reload-or-restart "${unit}"
|
||||
fi
|
||||
'') cfg.renew.reloadUnits;
|
||||
|
||||
hasPostCommands = cfg.renew.postCommands != [ ];
|
||||
renewPostCommands = lib.concatStringsSep "\n" cfg.renew.postCommands;
|
||||
|
||||
fileOwner = "${cfg.renew.user}:${cfg.renew.group}";
|
||||
in
|
||||
pkgs.writeShellApplication {
|
||||
name = "mtls-renew";
|
||||
runtimeInputs = with pkgs; [ coreutils step-cli systemd ];
|
||||
text = ''
|
||||
set -euo pipefail
|
||||
|
||||
YELLOW_BANG="\e[33m!\e[0m"
|
||||
|
||||
force=0
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--force)
|
||||
force=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo -e "$YELLOW_BANG Warning: ignoring unrecognized argument '$1'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ $force -eq 0 ]] && ! step certificate needs-renewal "${cfg.certFile}"; then
|
||||
echo "Skipping renew"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Renewing mTLS certificate"
|
||||
step ca renew --force "${cfg.certFile}" "${cfg.keyFile}"
|
||||
(umask 077; cat "${cfg.certFile}" "${cfg.keyFile}" > "${cfg.bundleFile}")
|
||||
chown ${fileOwner} ${cfg.certFile} ${cfg.keyFile} ${cfg.bundleFile}
|
||||
chmod 640 ${cfg.certFile} ${cfg.keyFile} ${cfg.bundleFile}
|
||||
|
||||
${lib.optionalString hasReloadUnits ''
|
||||
echo "Reloading units: ${lib.concatStringsSep ", " cfg.renew.reloadUnits}"
|
||||
${renewReloadScript}
|
||||
''}
|
||||
|
||||
${lib.optionalString hasPostCommands ''echo "Post commands:" ${renewPostCommands}''}
|
||||
'';
|
||||
};
|
||||
|
||||
mkNixosMtlsRenewService = { pkgs, cfg, ... }:
|
||||
{
|
||||
description = "Renew the mTLS certificate when Smallstep marks it ready";
|
||||
wantedBy = [ ];
|
||||
flake.modules.nixos.mtls = { config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.mtls;
|
||||
mtlsRenewWrapper = inputs.self.wrappers.mtlsRenew.apply {
|
||||
inherit pkgs;
|
||||
inherit (cfg) certDir keyFile certFile bundleFile;
|
||||
inherit (cfg.renew) user group reloadUnits postCommands;
|
||||
systemd = {
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = cfg.renew.user;
|
||||
Group = cfg.renew.group;
|
||||
ExecStart = lib.getExe (mkMtlsRenewScript { inherit pkgs cfg; });
|
||||
};
|
||||
} // lib.optionalAttrs cfg.renew.enable {
|
||||
startAt = cfg.renew.onCalendar;
|
||||
};
|
||||
};
|
||||
|
||||
mkNixosMtlsRenewTimer = {
|
||||
onCalendar,
|
||||
randomizedDelaySec,
|
||||
unit ? "mtls-renew.service",
|
||||
}: {
|
||||
description = "Periodic Smallstep renewal for the mTLS certificate";
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
Persistent = true;
|
||||
OnCalendar = onCalendar;
|
||||
AccuracySec = "1us";
|
||||
RandomizedDelaySec = randomizedDelaySec;
|
||||
Unit = unit;
|
||||
};
|
||||
};
|
||||
|
||||
mkHomeManagerMtlsRenewService = { pkgs, cfg, ... }:
|
||||
let
|
||||
renewScript = mkMtlsRenewScript {
|
||||
inherit pkgs cfg;
|
||||
systemctlArgs = [ "--user" ];
|
||||
};
|
||||
in
|
||||
{
|
||||
Unit = {
|
||||
Description = "Renew the mTLS certificate when Smallstep marks it ready";
|
||||
After = [ "network-online.target" ];
|
||||
Wants = [ "network-online.target" ];
|
||||
};
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
ExecStart = lib.getExe renewScript;
|
||||
};
|
||||
};
|
||||
|
||||
mkHomeManagerMtlsRenewTimer = {
|
||||
onCalendar,
|
||||
randomizedDelaySec,
|
||||
unit ? "mtls-renew.service",
|
||||
}: {
|
||||
Unit = {
|
||||
Description = "Periodic Smallstep renewal for the mTLS certificate";
|
||||
};
|
||||
Timer = {
|
||||
Persistent = true;
|
||||
OnCalendar = onCalendar;
|
||||
AccuracySec = "1us";
|
||||
RandomizedDelaySec = randomizedDelaySec;
|
||||
Unit = unit;
|
||||
};
|
||||
Install = {
|
||||
WantedBy = [ "timers.target" ];
|
||||
};
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.mtls = { config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.mtls;
|
||||
sanArgs = lib.concatMapStringsSep " " (san: "--san \"${san}\"") cfg.san;
|
||||
in
|
||||
{
|
||||
options.mtls = (mkOpts config) // {
|
||||
@@ -271,6 +144,33 @@ in
|
||||
type = lib.types.str;
|
||||
default = "/etc/step-ca/certs";
|
||||
};
|
||||
bootstrap = {
|
||||
enable = lib.mkOption {
|
||||
description = "Enable initial mTLS issuance when cert material is missing or invalid.";
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
};
|
||||
wantedBy = lib.mkOption {
|
||||
description = "systemd targets that should pull in mtls-bootstrap.service.";
|
||||
type = with lib.types; listOf str;
|
||||
default = [ "multi-user.target" ];
|
||||
};
|
||||
after = lib.mkOption {
|
||||
description = "systemd units/targets that mtls-bootstrap.service should run after.";
|
||||
type = with lib.types; listOf str;
|
||||
default = [ "network-online.target" ];
|
||||
};
|
||||
wants = lib.mkOption {
|
||||
description = "systemd units/targets that mtls-bootstrap.service should pull in.";
|
||||
type = with lib.types; listOf str;
|
||||
default = [ "network-online.target" ];
|
||||
};
|
||||
provisionerPasswordFile = lib.mkOption {
|
||||
description = "Optional path passed to mtls-generate as --provisioner-password-file for noninteractive issuance.";
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
certReaders = lib.mkOption {
|
||||
description = "";
|
||||
type = lib.types.listOf lib.types.str;
|
||||
@@ -291,30 +191,76 @@ in
|
||||
inherit (cfg) subject provisioner san certFile keyFile bundleFile lifetime;
|
||||
inherit (cfg.renew) user group;
|
||||
})
|
||||
(mkMtlsCheckScript { inherit pkgs; inherit (cfg) bundleFile; })
|
||||
(mkMtlsRenewScript { inherit pkgs cfg; })
|
||||
(inputs.self.wrappers.mtlsCheck.apply {
|
||||
inherit pkgs;
|
||||
inherit (cfg) bundleFile;
|
||||
}).wrapper
|
||||
mtlsRenewWrapper.wrapper
|
||||
];
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.certDir} 0750 ${cfg.renew.user} ${cfg.renew.group} -"
|
||||
];
|
||||
|
||||
systemd.services.mtls-renew = lib.mkIf cfg.renew.enable
|
||||
(mkNixosMtlsRenewService { inherit pkgs cfg; });
|
||||
systemd.packages = lib.mkIf cfg.renew.enable [
|
||||
mtlsRenewWrapper.outputs.systemd-system
|
||||
];
|
||||
|
||||
systemd.timers.mtls-renew = lib.mkIf cfg.renew.enable (mkNixosMtlsRenewTimer {
|
||||
inherit (cfg.renew) onCalendar randomizedDelaySec;
|
||||
});
|
||||
systemd.services.mtls-bootstrap = lib.mkIf cfg.bootstrap.enable {
|
||||
description = "Issue initial mTLS certificate if missing or invalid";
|
||||
wantedBy = cfg.bootstrap.wantedBy;
|
||||
after = cfg.bootstrap.after;
|
||||
wants = cfg.bootstrap.wants;
|
||||
path = with pkgs; [ coreutils step-cli ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = cfg.renew.user;
|
||||
Group = cfg.renew.group;
|
||||
};
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
if [ -s "${cfg.certFile}" ] \
|
||||
&& [ -s "${cfg.keyFile}" ] \
|
||||
&& [ -s "${cfg.bundleFile}" ] \
|
||||
&& step certificate inspect "${cfg.certFile}" >/dev/null 2>&1; then
|
||||
echo "mTLS certificate already exists"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Issuing initial mTLS certificate"
|
||||
cmd=(/run/current-system/sw/bin/mtls-generate)
|
||||
${lib.optionalString (cfg.bootstrap.provisionerPasswordFile != null) ''
|
||||
cmd+=(--provisioner-password-file "${cfg.bootstrap.provisionerPasswordFile}")
|
||||
''}
|
||||
|
||||
"''${cmd[@]}"
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.timers.mtls-renew = lib.mkIf cfg.renew.enable {
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
Persistent = true;
|
||||
AccuracySec = "1us";
|
||||
RandomizedDelaySec = cfg.renew.randomizedDelaySec;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
flake.modules.homeManager.mtls = { config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.mtls;
|
||||
keyFile = cfg.keyFile;
|
||||
certFile = cfg.certFile;
|
||||
bundleFile = cfg.bundleFile;
|
||||
sanArgs = lib.concatMapStringsSep " " (san: "--san \"${san}\"") cfg.san;
|
||||
mtlsRenewWrapper = inputs.self.wrappers.mtlsRenew.apply {
|
||||
inherit pkgs;
|
||||
inherit (cfg) certDir keyFile certFile bundleFile;
|
||||
inherit (cfg.renew) reloadUnits postCommands;
|
||||
systemctlArgs = [ "--user" ];
|
||||
systemd = lib.optionalAttrs cfg.renew.enable {
|
||||
startAt = cfg.renew.onCalendar;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
options.mtls = (mkOpts config) // {
|
||||
@@ -329,24 +275,230 @@ in
|
||||
home.packages = with pkgs; lib.optionals cfg.enable [
|
||||
# step-cli
|
||||
(mkMtlsGenerateScript {
|
||||
inherit pkgs;
|
||||
inherit (cfg) keyFile certFile bundleFile;
|
||||
inherit (cfg) subject provisioner san lifetime;
|
||||
inherit (cfg.renew) user group;
|
||||
inherit pkgs certFile keyFile bundleFile;
|
||||
})
|
||||
(mkMtlsCheckScript { inherit pkgs bundleFile; })
|
||||
(mkMtlsRenewScript { inherit pkgs cfg; systemctlArgs = [ "--user" ]; })
|
||||
(inputs.self.wrappers.mtlsCheck.apply {
|
||||
inherit pkgs;
|
||||
inherit (cfg) bundleFile;
|
||||
}).wrapper
|
||||
];
|
||||
|
||||
systemd.user.tmpfiles.rules = lib.mkIf cfg.enable [
|
||||
"d ${cfg.certDir} 0700 - - -"
|
||||
"d ${cfg.certDir} 0700 - - -" # Ensure the cert directory exists and is writable by the user
|
||||
];
|
||||
|
||||
systemd.user.services.mtls-renew = lib.mkIf cfg.renew.enable
|
||||
(mkHomeManagerMtlsRenewService { inherit pkgs cfg; });
|
||||
# Create the systemd service files for the user.
|
||||
xdg.configFile = lib.mkIf cfg.renew.enable {
|
||||
"systemd/user/mtls-renew.service".source =
|
||||
"${mtlsRenewWrapper.outputs.systemd-user}/systemd/user/mtls-renew.service";
|
||||
"systemd/user/mtls-renew.timer".source =
|
||||
"${mtlsRenewWrapper.outputs.systemd-user}/systemd/user/mtls-renew.timer";
|
||||
"systemd/user/mtls-renew.timer.d/override.conf".text = ''
|
||||
[Timer]
|
||||
Persistent=true
|
||||
AccuracySec=1us
|
||||
RandomizedDelaySec=${cfg.renew.randomizedDelaySec}
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.user.timers.mtls-renew = lib.mkIf cfg.renew.enable (mkHomeManagerMtlsRenewTimer {
|
||||
inherit (cfg.renew) onCalendar randomizedDelaySec;
|
||||
# Ensure the timer gets started
|
||||
home.activation.mtlsRenewTimer = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||
if [ -n "$XDG_RUNTIME_DIR" ] && [ -S "$XDG_RUNTIME_DIR/systemd/private" ]; then
|
||||
if [ "${lib.boolToString (cfg.enable && cfg.renew.enable)}" = "true" ]; then
|
||||
run ${pkgs.systemd}/bin/systemctl --user daemon-reload
|
||||
if ${pkgs.systemd}/bin/systemctl --user cat mtls-renew.timer >/dev/null 2>&1; then
|
||||
run ${pkgs.systemd}/bin/systemctl --user enable --now mtls-renew.timer
|
||||
else
|
||||
verboseEcho "mtls-renew.timer unit file is not available; skipping enable"
|
||||
fi
|
||||
else
|
||||
run ${pkgs.systemd}/bin/systemctl --user disable --now mtls-renew.timer || true
|
||||
run ${pkgs.systemd}/bin/systemctl --user daemon-reload || true
|
||||
fi
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
flake.wrappers = {
|
||||
mtlsCheck = inputs.wrappers.lib.wrapModule ({ config, lib, wlib, ... }: {
|
||||
options = {
|
||||
bundleFile = lib.mkOption {
|
||||
description = "String path for the mTLS key bundle";
|
||||
type = lib.types.str;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
binName = "mtls-check";
|
||||
# This pattern is necessary to wrap packages like openssl that provide more than one binary
|
||||
package = config.pkgs.symlinkJoin {
|
||||
name = "openssl";
|
||||
paths = [ config.pkgs.openssl.bin config.pkgs.openssl.man ];
|
||||
meta.mainProgram = "openssl";
|
||||
};
|
||||
args = [
|
||||
"x509"
|
||||
"-noout"
|
||||
"-in" config.bundleFile
|
||||
"-subject"
|
||||
"-issuer"
|
||||
"-ext" "subjectAltName,extendedKeyUsage"
|
||||
"-enddate"
|
||||
];
|
||||
};
|
||||
});
|
||||
|
||||
mtlsNeedsRenewal = inputs.wrappers.lib.wrapModule ({ config, lib, wlib, ... }: {
|
||||
options = {
|
||||
certFile = lib.mkOption {
|
||||
description = "String path for the public cert";
|
||||
type = lib.types.str;
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
binName = "mtls-needs-renewal";
|
||||
package = config.pkgs.step-cli;
|
||||
preHook = ''
|
||||
echo "Checking renewal status..."
|
||||
'';
|
||||
args = [ "certificate" "needs-renewal" "${config.certFile}" ];
|
||||
};
|
||||
});
|
||||
|
||||
mtlsRenew = inputs.wrappers.lib.wrapModule ({ config, lib, wlib, ... }: {
|
||||
imports = [ wlib.modules.systemd ];
|
||||
|
||||
options = {
|
||||
certDir = lib.mkOption {
|
||||
description = "String path to the directory where the certs will be stored";
|
||||
type = lib.types.str;
|
||||
};
|
||||
keyFile = lib.mkOption {
|
||||
description = "String path for the private key";
|
||||
type = lib.types.str;
|
||||
default = "${config.certDir}/key.pem";
|
||||
};
|
||||
certFile = lib.mkOption {
|
||||
description = "String path for the public cert";
|
||||
type = lib.types.str;
|
||||
default = "${config.certDir}/cert.pem";
|
||||
};
|
||||
bundleFile = lib.mkOption {
|
||||
description = "String path for the mTLS key bundle";
|
||||
type = lib.types.str;
|
||||
default = "${config.certDir}/mtls.pem";
|
||||
};
|
||||
user = lib.mkOption {
|
||||
description = "User that should own the renewed certificate files.";
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
};
|
||||
group = lib.mkOption {
|
||||
description = "Group that should own the renewed certificate files.";
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
};
|
||||
reloadUnits = lib.mkOption {
|
||||
description = "systemd units to try-reload-or-restart after a successful renewal.";
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
};
|
||||
postCommands = lib.mkOption {
|
||||
description = "Shell commands to run after a successful renewal.";
|
||||
type = lib.types.listOf lib.types.lines;
|
||||
default = [ ];
|
||||
};
|
||||
systemctlArgs = lib.mkOption {
|
||||
description = "Additional arguments to pass to systemctl when reloading units.";
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
binName = "mtls-renew";
|
||||
package = let
|
||||
systemctlCmd = "systemctl ${lib.escapeShellArgs config.systemctlArgs}";
|
||||
|
||||
hasReloadUnits = config.reloadUnits != [ ];
|
||||
renewReloadScript = lib.concatMapStringsSep "\n" (unit: ''
|
||||
if ${systemctlCmd} --quiet is-active "${unit}"; then
|
||||
${systemctlCmd} try-reload-or-restart "${unit}"
|
||||
fi
|
||||
'') config.reloadUnits;
|
||||
|
||||
hasPostCommands = config.postCommands != [ ];
|
||||
renewPostCommands = lib.concatStringsSep "\n" config.postCommands;
|
||||
|
||||
hasOwnership = config.user != null && config.group != null;
|
||||
in
|
||||
config.pkgs.writeShellApplication {
|
||||
name = "mtls-renew";
|
||||
runtimeInputs = with config.pkgs; [ coreutils step-cli systemd ];
|
||||
text = ''
|
||||
set -euo pipefail
|
||||
|
||||
YELLOW_BANG="\e[33m!\e[0m"
|
||||
|
||||
force=0
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--force)
|
||||
force=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo -e "$YELLOW_BANG Warning: ignoring unrecognized argument '$1'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ $force -eq 0 ]] && ! step certificate needs-renewal "${config.certFile}"; then
|
||||
echo "Skipping renew"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Renewing mTLS certificate"
|
||||
step ca renew --force "${config.certFile}" "${config.keyFile}"
|
||||
(umask 077; cat "${config.certFile}" "${config.keyFile}" > "${config.bundleFile}")
|
||||
|
||||
${lib.optionalString hasOwnership ''
|
||||
chown ${config.user}:${config.group} ${config.certFile} ${config.keyFile} ${config.bundleFile}
|
||||
chmod 640 ${config.certFile} ${config.keyFile} ${config.bundleFile}
|
||||
''}
|
||||
|
||||
${lib.optionalString hasReloadUnits ''
|
||||
echo "Reloading units: ${lib.concatStringsSep ", " config.reloadUnits}"
|
||||
${renewReloadScript}
|
||||
''}
|
||||
|
||||
${lib.optionalString hasPostCommands ''
|
||||
echo "Running post commands"
|
||||
${renewPostCommands}
|
||||
''}
|
||||
'';
|
||||
};
|
||||
extraPackages = [
|
||||
config.pkgs.step-cli
|
||||
];
|
||||
systemd = {
|
||||
description = "Renew the mTLS certificate when Smallstep marks it ready";
|
||||
documentation = [
|
||||
"https://smallstep.com/docs/step-ca/certificate-authority-server-production"
|
||||
];
|
||||
startLimitIntervalSec = 0;
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
serviceConfig.Type = lib.mkDefault "oneshot";
|
||||
# serviceConfig.ExecCondition = "";
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.modules.nixos.niri = { pkgs, lib, ... }:
|
||||
let
|
||||
niriPkg = self.packages.${pkgs.stdenv.hostPlatform.system}.myNiri;
|
||||
in
|
||||
{
|
||||
programs.niri = {
|
||||
enable = true;
|
||||
package = niriPkg;
|
||||
};
|
||||
systemd.user.services.niri.enableDefaultPath = false;
|
||||
};
|
||||
|
||||
perSystem = { pkgs, lib, self', ... }:
|
||||
let
|
||||
nixGLPackage = inputs.nixgl.packages.${pkgs.stdenv.hostPlatform.system}.nixGLIntel;
|
||||
noctaliaPkg = self'.packages.myNoctaliaNixGL;
|
||||
terminalPkg = pkgs.ghostty;
|
||||
myNiriUnwrapped = inputs.wrapper-modules.wrappers.niri.wrap {
|
||||
inherit pkgs;
|
||||
settings = {
|
||||
spawn-at-startup = [
|
||||
(lib.getExe noctaliaPkg)
|
||||
];
|
||||
|
||||
xwayland-satellite.path = lib.getExe pkgs.xwayland-satellite;
|
||||
|
||||
input.keyboard.xkb.layout = "us,ua";
|
||||
|
||||
layout.gaps = 5;
|
||||
|
||||
binds = {
|
||||
"Mod+Return".spawn-sh = lib.getExe terminalPkg;
|
||||
# "Mod+Ctrl+Return".spawn-sh = "${lib.getExe noctaliaPkg} ipc call launcher toggle";
|
||||
"Mod+Space".spawn-sh = "${lib.getExe noctaliaPkg} ipc call launcher toggle";
|
||||
"Mod+B".spawn-sh = "${lib.getExe pkgs.brave}";
|
||||
"Mod+E".spawn-sh = "xdg-open .";
|
||||
"Mod+Alt+L".spawn-sh = "loginctl lock-session";
|
||||
|
||||
"Alt+F4".spawn-sh = "${lib.getExe' pkgs.coreutils "true"}";
|
||||
"Mod+Shift+Q".close-window = { };
|
||||
"Mod+S".spawn-sh = "${lib.getExe noctaliaPkg} ipc call launcher toggle";
|
||||
|
||||
"Mod+1".focus-workspace = 1;
|
||||
"Mod+2".focus-workspace = 2;
|
||||
"Mod+3".focus-workspace = 3;
|
||||
"Mod+4".focus-workspace = 4;
|
||||
"Mod+5".focus-workspace = 5;
|
||||
"Mod+6".focus-workspace = 6;
|
||||
"Mod+7".focus-workspace = 7;
|
||||
"Mod+8".focus-workspace = 8;
|
||||
"Mod+9".focus-workspace = 9;
|
||||
|
||||
"Mod+Tab".focus-workspace-previous = { };
|
||||
|
||||
# "Mod+WheelScrollDown".focus-workspace-down = { };
|
||||
# "Mod+WheelScrollUp".focus-workspace-up = { };
|
||||
|
||||
# "Mod+Up".focus-window-up = { };
|
||||
# "Mod+Down".focus-window-down = { };
|
||||
"Mod+Up".focus-workspace-up = { };
|
||||
"Mod+Down".focus-workspace-down = { };
|
||||
"Mod+Left".focus-column-left = { };
|
||||
"Mod+Right".focus-column-right = { };
|
||||
"Mod+K".focus-window-up = { };
|
||||
"Mod+J".focus-window-down = { };
|
||||
"Mod+H".focus-column-left = { };
|
||||
"Mod+L".focus-column-right = { };
|
||||
"Mod+Home".focus-column-first = { };
|
||||
"Mod+End".focus-column-last = { };
|
||||
|
||||
# "Mod+Shift+Left".focus-monitor-left = { };
|
||||
# "Mod+Shift+Right".focus-monitor-right = { };
|
||||
# "Mod+Shift+Up".focus-monitor-up = { };
|
||||
# "Mod+Shift+Down".focus-monitor-down = { };
|
||||
|
||||
"Mod+Shift+1".move-column-to-workspace = 1;
|
||||
"Mod+Shift+2".move-column-to-workspace = 2;
|
||||
"Mod+Shift+3".move-column-to-workspace = 3;
|
||||
"Mod+Shift+4".move-column-to-workspace = 4;
|
||||
"Mod+Shift+5".move-column-to-workspace = 5;
|
||||
"Mod+Shift+6".move-column-to-workspace = 6;
|
||||
"Mod+Shift+7".move-column-to-workspace = 7;
|
||||
"Mod+Shift+8".move-column-to-workspace = 8;
|
||||
"Mod+Shift+9".move-column-to-workspace = 9;
|
||||
|
||||
"Mod+Ctrl+Up".move-window-up = { };
|
||||
"Mod+Ctrl+Down".move-window-down = { };
|
||||
"Mod+Ctrl+Left".move-column-left = { };
|
||||
"Mod+Ctrl+Right".move-column-right = { };
|
||||
"Mod+Ctrl+K".move-window-up = { };
|
||||
"Mod+Ctrl+J".move-window-down = { };
|
||||
"Mod+Ctrl+H".move-column-left = { };
|
||||
"Mod+Ctrl+L".move-column-right = { };
|
||||
"Mod+Ctrl+Home".move-column-to-first = { };
|
||||
"Mod+Ctrl+End".move-column-to-last = { };
|
||||
|
||||
"Mod+Shift+Ctrl+Left".move-column-to-monitor-left = { };
|
||||
"Mod+Shift+Ctrl+Right".move-column-to-monitor-right = { };
|
||||
"Mod+Shift+Ctrl+Up".move-column-to-monitor-up = { };
|
||||
"Mod+Shift+Ctrl+Down".move-column-to-monitor-down = { };
|
||||
|
||||
"Mod+Ctrl+F".expand-column-to-available-width = { };
|
||||
"Mod+C".center-column = { };
|
||||
"Mod+Ctrl+C".center-visible-columns = { };
|
||||
"Mod+Minus".set-column-width = "-10%";
|
||||
"Mod+Equal".set-column-width = "+10%";
|
||||
"Mod+Shift+Minus".set-window-height = "-10%";
|
||||
"Mod+Shift+Equal".set-window-height = "+10%";
|
||||
|
||||
"Mod+T".toggle-window-floating = { };
|
||||
"Mod+F".fullscreen-window = { };
|
||||
"Mod+W".toggle-column-tabbed-display = { };
|
||||
"Mod+O".toggle-overview = { };
|
||||
|
||||
"Ctrl+Shift+1".screenshot = { };
|
||||
"Ctrl+Shift+2".screenshot-screen = { };
|
||||
"Ctrl+Shift+3".screenshot-window = { };
|
||||
|
||||
"Mod+Shift+Slash".show-hotkey-overlay = { };
|
||||
"Mod+Escape".toggle-keyboard-shortcuts-inhibit = { };
|
||||
"Mod+Shift+P".power-off-monitors = { };
|
||||
"Ctrl+Alt+Delete".quit = { };
|
||||
|
||||
"Mod+WheelScrollRight".focus-column-right = { };
|
||||
"Mod+WheelScrollLeft".focus-column-left = { };
|
||||
"Mod+Ctrl+WheelScrollRight".move-column-right = { };
|
||||
"Mod+Ctrl+WheelScrollLeft".move-column-left = { };
|
||||
"Mod+Shift+WheelScrollDown".focus-column-right = { };
|
||||
"Mod+Shift+WheelScrollUp".focus-column-left = { };
|
||||
# "Mod+Ctrl+Shift+WheelScrollDown".move-column-right = { };
|
||||
# "Mod+Ctrl+Shift+WheelScrollUp".move-column-left = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
in {
|
||||
packages.myNiri = myNiriUnwrapped;
|
||||
|
||||
packages.myNiriNixGL = pkgs.symlinkJoin {
|
||||
name = "my-niri-nixgl";
|
||||
paths = [ myNiriUnwrapped ];
|
||||
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||
meta.mainProgram = "niri";
|
||||
postBuild = ''
|
||||
for exe in "$out"/bin/*; do
|
||||
if [[ -f "$exe" ]]; then
|
||||
base="$(basename "$exe")"
|
||||
mv "$exe" "$out/bin/.''${base}-real"
|
||||
makeWrapper ${lib.getExe nixGLPackage} "$exe" \
|
||||
--add-flags "$out/bin/.''${base}-real"
|
||||
fi
|
||||
done
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.modules.nixos.base = { config, pkgs, lib, ... }: {
|
||||
imports = [
|
||||
self.modules.nixos.audio
|
||||
];
|
||||
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
nixpkgs.config = {
|
||||
permittedInsecurePackages = [ "openssl-1.1.1w" ];
|
||||
allowUnfree = true;
|
||||
problems.handlers.sublimetext4.broken = "ignore";
|
||||
};
|
||||
|
||||
# Set your time zone.
|
||||
time.timeZone = "US/Central";
|
||||
|
||||
# Select internationalisation properties.
|
||||
i18n = {
|
||||
defaultLocale = "en_US.UTF-8";
|
||||
extraLocaleSettings = {
|
||||
LC_ADDRESS = "en_US.UTF-8";
|
||||
LC_IDENTIFICATION = "en_US.UTF-8";
|
||||
LC_MEASUREMENT = "en_US.UTF-8";
|
||||
LC_MONETARY = "en_US.UTF-8";
|
||||
LC_NAME = "en_US.UTF-8";
|
||||
LC_NUMERIC = "en_US.UTF-8";
|
||||
LC_PAPER = "en_US.UTF-8";
|
||||
LC_TELEPHONE = "en_US.UTF-8";
|
||||
LC_TIME = "en_US.UTF-8";
|
||||
};
|
||||
};
|
||||
|
||||
fonts.packages = with pkgs; [
|
||||
nerd-fonts.hack
|
||||
nerd-fonts.sauce-code-pro
|
||||
];
|
||||
|
||||
# Need for less pain-in-the-ass for doing normal-ish stuff
|
||||
# This is needed for VSCode remote support. Read: https://nixos.wiki/wiki/Visual_Studio_Code
|
||||
programs.nix-ld.enable = true;
|
||||
|
||||
# Configure network connections interactively with nmcli or nmtui.
|
||||
networking.networkmanager.enable = true;
|
||||
|
||||
services.openssh.enable = true;
|
||||
|
||||
security.polkit.enable = true; # polkit
|
||||
services.gnome.gnome-keyring.enable = true; # secret service
|
||||
security.pam.services.swaylock = {};
|
||||
|
||||
# Enable sound with pipewire.
|
||||
services.pulseaudio.enable = false;
|
||||
security.rtkit.enable = true; # PulseAudio server uses this to acquire realtime priority.
|
||||
services.pipewire = {
|
||||
enable = true;
|
||||
alsa.enable = true;
|
||||
alsa.support32Bit = true;
|
||||
pulse.enable = true;
|
||||
# If you want to use JACK applications, uncomment this
|
||||
#jack.enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{ self, inputs, ... }: {
|
||||
perSystem = { pkgs, lib, ... }:
|
||||
let
|
||||
nixGLPackage = inputs.nixgl.packages.${pkgs.stdenv.hostPlatform.system}.nixGLIntel;
|
||||
myNoctaliaUnwrapped = inputs.wrapper-modules.wrappers.noctalia-shell.wrap {
|
||||
inherit pkgs;
|
||||
runtimePkgs = [
|
||||
pkgs.qt6.qtwebsockets
|
||||
];
|
||||
# settings =
|
||||
# (builtins.fromJSON
|
||||
# (builtins.readFile ./noctalia.json)).settings;
|
||||
};
|
||||
in {
|
||||
packages.myNoctalia = myNoctaliaUnwrapped;
|
||||
|
||||
packages.myNoctaliaNixGL = pkgs.symlinkJoin {
|
||||
name = "my-noctalia-nixgl";
|
||||
paths = [ myNoctaliaUnwrapped ];
|
||||
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||
meta.mainProgram = "noctalia-shell";
|
||||
postBuild = ''
|
||||
for exe in "$out"/bin/*; do
|
||||
if [[ -f "$exe" ]]; then
|
||||
base="$(basename "$exe")"
|
||||
mv "$exe" "$out/bin/.''${base}-real"
|
||||
makeWrapper ${lib.getExe nixGLPackage} "$exe" \
|
||||
--add-flags "$out/bin/.''${base}-real"
|
||||
fi
|
||||
done
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,10 +1,65 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.modules.nixos.restic-server = { config, pkgs, lib, ... }: {
|
||||
flake.modules.nixos.restic-server = { config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config.resticServer;
|
||||
port = builtins.fromJSON (lib.last (lib.splitString ":" cfg.listenAddress));
|
||||
in {
|
||||
options.resticServer = {
|
||||
enable = lib.mkEnableOption "Enable the RESTic REST server";
|
||||
|
||||
dataDir = lib.mkOption {
|
||||
description = "Storage directory for RESTic repositories served by this host.";
|
||||
type = lib.types.str;
|
||||
default = "/mnt/restic";
|
||||
};
|
||||
|
||||
listenAddress = lib.mkOption {
|
||||
description = "Listen address for the RESTic REST server.";
|
||||
type = lib.types.str;
|
||||
default = "0.0.0.0:8080";
|
||||
};
|
||||
|
||||
privateRepos = lib.mkOption {
|
||||
description = "Whether the RESTic server should use private repository mode.";
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
};
|
||||
|
||||
extraFlags = lib.mkOption {
|
||||
description = "Additional flags to pass to the RESTic REST server before TLS flags are appended.";
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ "--no-auth" ];
|
||||
};
|
||||
|
||||
tls = {
|
||||
certFile = lib.mkOption {
|
||||
description = "Path to the TLS certificate file for the RESTic REST server, or null to disable TLS.";
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
keyFile = lib.mkOption {
|
||||
description = "Path to the TLS private key file for the RESTic REST server, or null to disable TLS.";
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
networking.firewall.allowedTCPPorts = [ port ];
|
||||
|
||||
services.restic.server = {
|
||||
enable = true;
|
||||
dataDir = "/mnt/restic";
|
||||
listenAddress = "0.0.0.0:8080";
|
||||
extraFlags = [ "--no-auth" ];
|
||||
inherit (cfg) dataDir listenAddress privateRepos;
|
||||
extraFlags =
|
||||
cfg.extraFlags
|
||||
++ lib.optionals (cfg.tls.certFile != null && cfg.tls.keyFile != null) [
|
||||
"--tls"
|
||||
"--tls-cert=${cfg.tls.certFile}"
|
||||
"--tls-key=${cfg.tls.keyFile}"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,40 +1,82 @@
|
||||
# This module provides all the shell options
|
||||
{ inputs, lib, ... }:
|
||||
{ self, inputs, ... }:
|
||||
{
|
||||
flake.modules.homeManager.shell-tools =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
flake.modules.homeManager.shell-tools = { config, pkgs, ... }: {
|
||||
options.shell.program = lib.mkOption {
|
||||
type = lib.types.enum [ "bash" "zsh" ];
|
||||
default = "zsh";
|
||||
description = "Which interactive shell configuration to enable.";
|
||||
};
|
||||
|
||||
imports = with inputs.self.modules.homeManager; [
|
||||
bash
|
||||
# bash
|
||||
zsh
|
||||
|
||||
# Tools
|
||||
eza
|
||||
files
|
||||
];
|
||||
|
||||
config = {
|
||||
home.shell.enableShellIntegration = true;
|
||||
programs.zsh.enable = lib.mkForce (config.shell.program == "zsh");
|
||||
home.packages = with pkgs; [
|
||||
nh
|
||||
nvd
|
||||
nix-output-monitor
|
||||
wget
|
||||
curl
|
||||
busybox
|
||||
gnugrep
|
||||
dig
|
||||
btop
|
||||
uv
|
||||
xclip
|
||||
jq
|
||||
inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.shell-tools
|
||||
];
|
||||
|
||||
home.shell.enableShellIntegration = true;
|
||||
};
|
||||
|
||||
perSystem =
|
||||
{
|
||||
system,
|
||||
pkgs,
|
||||
self',
|
||||
...
|
||||
}:
|
||||
{
|
||||
packages.shell-tools = inputs.wrappers.lib.wrapPackage {
|
||||
inherit pkgs;
|
||||
# binName = "show-tools";
|
||||
package = (
|
||||
pkgs.symlinkJoin {
|
||||
name = "show-tools";
|
||||
meta.mainProgram = "show-tools";
|
||||
paths = with pkgs; [
|
||||
nh
|
||||
ripgrep
|
||||
(writeShellScriptBin "ds" ''${lib.getExe pkgs.gdu} -x -I /snap /'')
|
||||
fd
|
||||
jq
|
||||
wget
|
||||
curl
|
||||
dig
|
||||
bat
|
||||
self'.packages.gdu
|
||||
self'.packages.my-eza
|
||||
self'.packages.yazi
|
||||
hostname
|
||||
iproute2
|
||||
direnv
|
||||
(writeShellApplication {
|
||||
name = "show-tools";
|
||||
text = ''
|
||||
IFS=':' read -r -a path_dirs <<< "''${PATH:-}"
|
||||
|
||||
for dir in "''${path_dirs[@]}"; do
|
||||
[[ "$dir" == */bin ]] || continue
|
||||
[[ -d "$dir" ]] || continue
|
||||
|
||||
printf '%s\n' "$dir"/*
|
||||
done
|
||||
'';
|
||||
})
|
||||
];
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
packages.gdu = inputs.wrappers.lib.wrapPackage {
|
||||
inherit pkgs;
|
||||
package = pkgs.gdu;
|
||||
args = [
|
||||
"-x"
|
||||
"--si"
|
||||
"--collapse-path"
|
||||
"--mouse"
|
||||
"$@"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
# New attempt at consolidating SSH config that wraps the step client, SSH certs, and all that in a top-level module
|
||||
{ self, inputs, ... }:
|
||||
let
|
||||
caPatterns = [ "*.john-stream.com" "192.168.1.*" "fded:fb16:653e:25da:be24:11ff:*" ];
|
||||
sshHostCAPath = ../../hosts/janus/public/ssh_host_ca_key.pub;
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.ssh-new = { config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config.ssh-new;
|
||||
hostKeyFile = "${cfg.host.configDir}/${cfg.host.keyFile}";
|
||||
hostCertFile = "${hostKeyFile}-cert.pub";
|
||||
userCAFile = "${cfg.host.configDir}/${cfg.certificates.user.CAFile}";
|
||||
CAknownHosts = (lib.genAttrs caPatterns (_: {
|
||||
certAuthority = true;
|
||||
publicKey = lib.removeSuffix "\n" (builtins.readFile sshHostCAPath);
|
||||
}));
|
||||
|
||||
wrappers = inputs.self.wrappers;
|
||||
provisionerPasswordFile = config.sops.secrets."janus/admin_jwk".path;
|
||||
sshHostCertSign = (wrappers.signHostWrapper.apply {
|
||||
inherit pkgs provisionerPasswordFile;
|
||||
inherit (cfg.certificates) provisioner;
|
||||
inherit (cfg.certificates.host) extraPrincipals;
|
||||
}).wrapper;
|
||||
sshHostCertRenew = (wrappers.renewHostWrapper.apply {
|
||||
inherit pkgs;
|
||||
sshHostKeyFile = hostKeyFile;
|
||||
overwrite = true;
|
||||
}).wrapper;
|
||||
sshHostCertCheck = (wrappers.hostCheckWrapper.apply {
|
||||
inherit pkgs;
|
||||
certPath = hostCertFile;
|
||||
}).wrapper;
|
||||
sshHostRenewalCheck = (wrappers.renewalCheck.apply {
|
||||
inherit pkgs;
|
||||
certPath = hostCertFile;
|
||||
expires-in = "4h";
|
||||
}).wrapper;
|
||||
sshUserCertSign = (wrappers.signUserWrapper.apply {
|
||||
inherit pkgs provisionerPasswordFile;
|
||||
inherit (cfg.certificates) provisioner;
|
||||
validUsers = [ "root" "john" "appdaemon" ];
|
||||
}).wrapper;
|
||||
sshUserCertCheck = (wrappers.userCheckWrapper.apply {
|
||||
inherit pkgs;
|
||||
certPath = "${cfg.user.keyFile}-cert.pub";
|
||||
}).wrapper;
|
||||
in
|
||||
{
|
||||
options.ssh-new = {
|
||||
user = {
|
||||
keyFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "$HOME/.ssh/id_ed25519";
|
||||
};
|
||||
};
|
||||
host = {
|
||||
configDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/etc/ssh";
|
||||
};
|
||||
keyFile = lib.mkOption {
|
||||
description = "String path to the host private key file";
|
||||
type = lib.types.str;
|
||||
default = "ssh_host_ed25519_key";
|
||||
};
|
||||
keyType = lib.mkOption {
|
||||
description = "OpenSSH host key type for ssh.hostKey.";
|
||||
type = lib.types.enum [ "ed25519" "rsa" "ecdsa" ];
|
||||
default = "ed25519";
|
||||
};
|
||||
extraSettings = lib.mkOption {
|
||||
description = "Extra settings to merge";
|
||||
type = lib.types.attrs;
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
certificates = {
|
||||
provisioner = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
};
|
||||
host = {
|
||||
enable = lib.mkEnableOption "Enable SSH host certs";
|
||||
autoRenew = lib.mkEnableOption "Auto-renew the SSH host certs with a systemd service/timer";
|
||||
extraPrincipals = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
user = {
|
||||
enable = lib.mkEnableOption "Enable SSH user certs";
|
||||
CAFile = lib.mkOption {
|
||||
description = "Filename of the SSH user CA with the config directory";
|
||||
type = lib.types.str;
|
||||
default = "ssh_user_ca_key.pub";
|
||||
};
|
||||
extraPrincipals = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
hostKeys = [
|
||||
{
|
||||
path = hostKeyFile;
|
||||
type = cfg.host.keyType;
|
||||
}
|
||||
];
|
||||
settings = lib.mkMerge [
|
||||
{
|
||||
PasswordAuthentication = false;
|
||||
KbdInteractiveAuthentication = false;
|
||||
}
|
||||
cfg.host.extraSettings
|
||||
(lib.mkIf cfg.certificates.host.enable {
|
||||
HostCertificate = "${hostKeyFile}-cert.pub";
|
||||
})
|
||||
(lib.mkIf cfg.certificates.user.enable {
|
||||
TrustedUserCAKeys = "${cfg.host.configDir}/${cfg.certificates.user.CAFile}";
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
environment.etc."ssh/${cfg.certificates.user.CAFile}" = lib.mkIf cfg.certificates.user.enable {
|
||||
source = ../../hosts/janus/public/ssh_user_ca_key.pub;
|
||||
};
|
||||
|
||||
environment.systemPackages = (
|
||||
lib.optionals cfg.certificates.host.enable [
|
||||
sshHostCertSign
|
||||
sshHostCertRenew
|
||||
sshHostRenewalCheck
|
||||
sshHostCertCheck
|
||||
]
|
||||
);
|
||||
|
||||
sops = lib.mkIf cfg.certificates.host.autoRenew {
|
||||
secrets."janus/admin_jwk" = {
|
||||
sopsFile = ../../../keys/secrets.yaml;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
mode = "0400";
|
||||
};
|
||||
};
|
||||
|
||||
systemd = lib.mkIf cfg.certificates.host.autoRenew {
|
||||
services.ssh-certs-renew = {
|
||||
description = "SSH host certificate renewal";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
path = with pkgs; [ step-cli systemd ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "root";
|
||||
Group = "root";
|
||||
ExecCondition = lib.getExe sshHostRenewalCheck;
|
||||
ExecStart = lib.getExe sshHostCertRenew;
|
||||
};
|
||||
};
|
||||
timers.ssh-certs-renew = {
|
||||
description = "Periodic Step SSH host certificate renewal";
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnBootSec = "5m";
|
||||
OnUnitActiveSec = "4h";
|
||||
RandomizedDelaySec = "15m";
|
||||
Persistent = true;
|
||||
Unit = "ssh-certs-renew.service";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# This winds up in /etc/ssh/ssh_known_hosts, which applies to all users
|
||||
programs.ssh.knownHosts = lib.mkIf cfg.certificates.user.enable CAknownHosts;
|
||||
|
||||
home-manager.users.root = lib.mkIf cfg.certificates.user.enable {
|
||||
home.stateVersion = lib.mkDefault config.system.stateVersion;
|
||||
imports = with inputs.self.modules.homeManager; [
|
||||
ssh-new
|
||||
];
|
||||
home.packages = [
|
||||
sshUserCertSign
|
||||
sshUserCertCheck
|
||||
];
|
||||
ssh-new.certificates.enable = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
flake.modules.homeManager.ssh-new = { config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config.ssh-new;
|
||||
sshHostCAContent = lib.removeSuffix "\n" (builtins.readFile sshHostCAPath);
|
||||
knownHostsText = lib.concatMapStrings
|
||||
(pattern: "@cert-authority ${pattern} ${sshHostCAContent}\n")
|
||||
caPatterns;
|
||||
in
|
||||
{
|
||||
options.ssh-new = {
|
||||
keyFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "${config.home.homeDirectory}/.ssh/id_ed25519";
|
||||
};
|
||||
certificates = {
|
||||
enable = lib.mkEnableOption "Enable SSH client certificates";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
home.file.".ssh/known_hosts" = lib.mkIf cfg.certificates.enable {
|
||||
text = knownHostsText;
|
||||
};
|
||||
programs.ssh = {
|
||||
enable = true;
|
||||
enableDefaultConfig = false;
|
||||
settings = {
|
||||
# These settings apply to all connections
|
||||
"*" = lib.mkMerge (
|
||||
[
|
||||
# Default settings
|
||||
{
|
||||
Compression = false;
|
||||
ServerAliveInterval = 60;
|
||||
ServerAliveCountMax = 3;
|
||||
TCPKeepAlive = "yes";
|
||||
ConnectTimeout = 3;
|
||||
|
||||
PubkeyAuthentication = "yes";
|
||||
PasswordAuthentication = "no";
|
||||
PreferredAuthentications = "publickey";
|
||||
IdentitiesOnly = true;
|
||||
IdentityFile = cfg.keyFile;
|
||||
|
||||
StrictHostKeyChecking = "accept-new";
|
||||
UserKnownHostsFile = "${config.home.homeDirectory}/.ssh/known_hosts";
|
||||
|
||||
HashKnownHosts = lib.mkDefault false;
|
||||
AddKeysToAgent = lib.mkDefault "yes";
|
||||
ForwardAgent = lib.mkDefault false;
|
||||
|
||||
RequestTTY = lib.mkDefault "auto";
|
||||
SetEnv.TERM = "xterm-256color";
|
||||
}
|
||||
]
|
||||
# SSH certificate settings
|
||||
++ lib.optionals cfg.certificates.enable [
|
||||
{
|
||||
CertificateFile = "${cfg.keyFile}-cert.pub";
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
"gitea" = {
|
||||
HostName = "192.168.1.104";
|
||||
User = "john";
|
||||
};
|
||||
"janus" = {
|
||||
HostName = "fded:fb16:653e:25da:be24:11ff:fe6b:4d57";
|
||||
User = "root";
|
||||
};
|
||||
"soteria" = {
|
||||
HostName = "fded:fb16:653e:25da:be24:11ff:fe54:aa39";
|
||||
User = "root";
|
||||
};
|
||||
"hermes" = {
|
||||
HostName = "192.168.1.150";
|
||||
User = "root";
|
||||
};
|
||||
"jdl-docker" = {
|
||||
HostName = "jdl-docker.tailcf205.ts.net";
|
||||
User = "john";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
{ self, inputs, ... }:
|
||||
let
|
||||
mkPrincipalArgs = principals:
|
||||
builtins.concatLists (map (principal: [ "--principal" principal ]) principals);
|
||||
in
|
||||
{
|
||||
flake.wrappers.signHostWrapper = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
|
||||
options = {
|
||||
provisioner = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = "admin";
|
||||
};
|
||||
provisionerPasswordFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
};
|
||||
extraPrincipals = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
};
|
||||
overwrite = lib.mkEnableOption "Overwrite existing cert file?";
|
||||
};
|
||||
|
||||
config = {
|
||||
binName = "ssh-host-cert-sign";
|
||||
package = config.pkgs.step-cli;
|
||||
extraPackages = with config.pkgs; [ hostname iproute2 systemd ];
|
||||
preHook = ''
|
||||
HOSTNAME=$(hostname -s)
|
||||
IP_ADDRESS=$(ip -4 -o addr show scope global | while read -r _ _ _ addr _; do
|
||||
case "$addr" in
|
||||
192.168.1.*/*)
|
||||
printf '%s\n' "''${addr%%/*}"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done)
|
||||
echo "Signing SSH host cert for $HOSTNAME at $IP_ADDRESS"
|
||||
'';
|
||||
args =
|
||||
[
|
||||
"ssh" "certificate"
|
||||
"--host" "--sign"
|
||||
"--principal" "$HOSTNAME"
|
||||
"--principal" "$IP_ADDRESS"
|
||||
]
|
||||
++ lib.optionals (config.provisioner != null) [ "--provisioner" "${config.provisioner}" ]
|
||||
++ lib.optionals (config.provisionerPasswordFile != null) [
|
||||
"--provisioner-password-file" "${config.provisionerPasswordFile}"
|
||||
]
|
||||
++ lib.optionals config.overwrite [ "-f" ]
|
||||
++ mkPrincipalArgs config.extraPrincipals;
|
||||
};
|
||||
});
|
||||
|
||||
flake.wrappers.renewHostWrapper = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
|
||||
options = {
|
||||
sshHostKeyFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/etc/ssh/ssh_host_ed25519_key";
|
||||
};
|
||||
overwrite = lib.mkEnableOption "Overwrite existing cert file?";
|
||||
};
|
||||
|
||||
config = {
|
||||
binName = "ssh-host-cert-renew";
|
||||
package = config.pkgs.step-cli;
|
||||
extraPackages = with config.pkgs; [ systemd ];
|
||||
args =
|
||||
[ "ssh" "renew" "${config.sshHostKeyFile}-cert.pub" "${config.sshHostKeyFile}" ]
|
||||
++ lib.optionals config.overwrite [ "-f" ];
|
||||
};
|
||||
});
|
||||
|
||||
flake.wrappers.hostCheckWrapper = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
|
||||
options = {
|
||||
certPath = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = "/etc/ssh/ssh_host_ed25519_key-cert.pub";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
binName = "ssh-host-cert-check";
|
||||
package = config.pkgs.openssh;
|
||||
exePath = lib.getExe' config.pkgs.openssh "ssh-keygen";
|
||||
args = [ "-Lf" "${config.certPath}" ];
|
||||
};
|
||||
});
|
||||
|
||||
flake.wrappers.signUserWrapper = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
|
||||
options = {
|
||||
provisioner = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = "admin";
|
||||
};
|
||||
provisionerPasswordFile = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
};
|
||||
validUsers = lib.mkOption {
|
||||
description = "A list of the user names that this cert will be valid for";
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
};
|
||||
overwrite = lib.mkEnableOption "Overwrite existing cert file?";
|
||||
};
|
||||
|
||||
config = {
|
||||
binName = "ssh-user-cert-sign";
|
||||
package = config.pkgs.step-cli;
|
||||
args = [ "ssh" "certificate" "--sign" ]
|
||||
++ lib.optionals (config.provisioner != null) [ "--provisioner" "${config.provisioner}" ]
|
||||
++ lib.optionals (config.provisionerPasswordFile != null) [
|
||||
"--provisioner-password-file" "${config.provisionerPasswordFile}"
|
||||
]
|
||||
++ lib.optionals config.overwrite [ "-f" ]
|
||||
++ mkPrincipalArgs config.validUsers;
|
||||
};
|
||||
});
|
||||
|
||||
flake.wrappers.userCheckWrapper = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
|
||||
options = {
|
||||
certPath = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = "$HOME/.ssh/id_ed25519-cert.pub";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
binName = "ssh-user-cert-check";
|
||||
package = config.pkgs.openssh;
|
||||
exePath = lib.getExe' config.pkgs.openssh "ssh-keygen";
|
||||
args = [ "-Lf" "${config.certPath}" ];
|
||||
};
|
||||
});
|
||||
|
||||
flake.wrappers.renewalCheck = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
|
||||
options = {
|
||||
certPath = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = "$HOME/.ssh/id_ed25519-cert.pub";
|
||||
};
|
||||
expires-in = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "4h";
|
||||
};
|
||||
};
|
||||
config = {
|
||||
binName = "ssh-renewal-check";
|
||||
package = config.pkgs.step-cli;
|
||||
preHook = ''
|
||||
echo "Checking SSH cert at ${config.certPath}"
|
||||
'';
|
||||
args = [
|
||||
"ssh" "needs-renewal" "${config.certPath}"
|
||||
"--expires-in" "${config.expires-in}"
|
||||
];
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
{ self, inputs, ... }:
|
||||
let
|
||||
defaultCaUrl = "https://janus.john-stream.com/";
|
||||
defaultFingerprint = "2036c44f7b5901566ff7611ea6c927291ecc6d2dd00779c0eead70ec77fa10d6";
|
||||
defaultRoot = ../hosts/janus/public/root_ca.crt;
|
||||
|
||||
mkStepBootstrap = { pkgs, caUrl, fingerprint, install ? false }:
|
||||
(inputs.self.wrappers.stepBootstrap.apply {
|
||||
inherit pkgs install;
|
||||
ca-url = caUrl;
|
||||
inherit fingerprint;
|
||||
}).wrapper;
|
||||
|
||||
mkDefaultsText = cfg: builtins.toJSON {
|
||||
ca-url = cfg.caUrl;
|
||||
fingerprint = cfg.fingerprint;
|
||||
root = cfg.root;
|
||||
};
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.step-client = { config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config."step-client";
|
||||
in
|
||||
{
|
||||
options."step-client" = {
|
||||
enable = lib.mkOption {
|
||||
description = "Enable step-ca client bootstrap";
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
};
|
||||
|
||||
caUrl = lib.mkOption {
|
||||
description = "The step-ca URL used for bootstrap and renewal.";
|
||||
type = lib.types.str;
|
||||
default = defaultCaUrl;
|
||||
};
|
||||
|
||||
fingerprint = lib.mkOption {
|
||||
description = "The SHA256 fingerprint of the step-ca root certificate.";
|
||||
type = lib.types.str;
|
||||
default = defaultFingerprint;
|
||||
};
|
||||
|
||||
root = lib.mkOption {
|
||||
description = "The step-ca root certificate used for bootstrap.";
|
||||
type = lib.types.path;
|
||||
default = defaultRoot;
|
||||
};
|
||||
|
||||
certDir = lib.mkOption {
|
||||
description = "Directory used to store root CA material for mTLS and step bootstrap.";
|
||||
type = lib.types.str;
|
||||
default = "/etc/step-ca/certs";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment.systemPackages = [
|
||||
(mkStepBootstrap {
|
||||
inherit pkgs;
|
||||
caUrl = cfg.caUrl;
|
||||
fingerprint = cfg.fingerprint;
|
||||
})
|
||||
];
|
||||
|
||||
environment.etc."step-ca/defaults.json".text = mkDefaultsText cfg;
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.certDir} 0750 root root -"
|
||||
"L+ ${cfg.certDir}/root_ca.crt - - - - ${cfg.root}"
|
||||
"d /root/.step 0700 root root -"
|
||||
"d /root/.step/config 0700 root root -"
|
||||
"d /root/.step/certs 0700 root root -"
|
||||
"L+ /root/.step/config/defaults.json - - - - /etc/step-ca/defaults.json"
|
||||
"L+ /root/.step/certs/root_ca.crt - - - - ${cfg.certDir}/root_ca.crt"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
flake.modules.homeManager.step-client = { config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config."step-client";
|
||||
in
|
||||
{
|
||||
options."step-client" = {
|
||||
enable = lib.mkOption {
|
||||
description = "Enable step-ca client bootstrap";
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
};
|
||||
|
||||
caUrl = lib.mkOption {
|
||||
description = "The step-ca URL used for bootstrap and renewal.";
|
||||
type = lib.types.str;
|
||||
default = defaultCaUrl;
|
||||
};
|
||||
|
||||
fingerprint = lib.mkOption {
|
||||
description = "The SHA256 fingerprint of the step-ca root certificate.";
|
||||
type = lib.types.str;
|
||||
default = defaultFingerprint;
|
||||
};
|
||||
|
||||
root = lib.mkOption {
|
||||
description = "The step-ca root certificate used for bootstrap.";
|
||||
type = lib.types.path;
|
||||
default = defaultRoot;
|
||||
};
|
||||
|
||||
certDir = lib.mkOption {
|
||||
description = "Directory used to store root CA material for mTLS and step bootstrap.";
|
||||
type = lib.types.str;
|
||||
default = "${config.home.homeDirectory}/.step/certs";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (
|
||||
let
|
||||
certDirPath = lib.removePrefix "${config.home.homeDirectory}/" cfg.certDir;
|
||||
in
|
||||
{
|
||||
home.packages = [
|
||||
(mkStepBootstrap {
|
||||
inherit pkgs;
|
||||
caUrl = cfg.caUrl;
|
||||
fingerprint = cfg.fingerprint;
|
||||
})
|
||||
];
|
||||
|
||||
home.file.".step/config/defaults.json".text = mkDefaultsText cfg;
|
||||
home.file."${certDirPath}/root_ca.crt".source = cfg.root;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
perSystem = { system, pkgs, lib, ... }: {
|
||||
packages.step-bootstrap = mkStepBootstrap {
|
||||
inherit pkgs;
|
||||
caUrl = defaultCaUrl;
|
||||
fingerprint = defaultFingerprint;
|
||||
install = true;
|
||||
};
|
||||
};
|
||||
|
||||
flake.wrappers.stepBootstrap = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
|
||||
options = {
|
||||
ca-url = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
fingerprint = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
install = lib.mkEnableOption "Install the cert to the system trust store";
|
||||
};
|
||||
|
||||
config = {
|
||||
binName = "step-bootstrap";
|
||||
package = config.pkgs.step-cli;
|
||||
args = [
|
||||
"ca" "bootstrap"
|
||||
"--ca-url" config.ca-url
|
||||
"--fingerprint" config.fingerprint
|
||||
]
|
||||
++ lib.optional config.install "--install";
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
# Janus
|
||||
|
||||
Generate passwords:
|
||||
```shell
|
||||
mkdir -p /tmp/janus-step-ca-bootstrap && chmod 700 /tmp/janus-step-ca-bootstrap && cd /tmp/janus-step-ca-bootstrap && umask 077 && openssl rand -base64 48 > ca_password.txt && openssl rand -base64 48 > admin_jwk_password.txt
|
||||
```
|
||||
|
||||
Generate the Janus OpenSSH host key for reference (Janus now uses whatever key
|
||||
already exists on the target at `/etc/ssh/ssh_host_ed25519_key`):
|
||||
|
||||
```shell
|
||||
ssh-keygen -t ed25519 -N '' -C janus@john-stream.com -f /tmp/janus-step-ca-bootstrap/ssh_host_ed25519_key
|
||||
```
|
||||
|
||||
Bootstrap CA materials with SSH enabled:
|
||||
```shell
|
||||
STEPPATH=/tmp/janus-step-ca-bootstrap/step step ca init --name Janus --dns janus.john-stream.com --dns 192.168.1.244 --address :443 --provisioner admin --password-file /tmp/janus-step-ca-bootstrap/ca_password.txt --provisioner-password-file /tmp/janus-step-ca-bootstrap/admin_jwk_password.txt --ssh --deployment-type standalone --with-ca-url https://janus.john-stream.com
|
||||
```
|
||||
|
||||
Insert generated runtime CA material into `modules/hosts/janus/secrets.yaml` under `janus`:
|
||||
|
||||
- `/tmp/janus-step-ca-bootstrap/ca_password.txt` -> `ca_password`
|
||||
- `/tmp/janus-step-ca-bootstrap/step/secrets/intermediate_ca_key` -> `intermediate_ca_key`
|
||||
- `/tmp/janus-step-ca-bootstrap/step/secrets/ssh_host_ca_key` -> `ssh_host_ca_key`
|
||||
- `/tmp/janus-step-ca-bootstrap/step/secrets/ssh_user_ca_key` -> `ssh_user_ca_key`
|
||||
- `/tmp/janus-step-ca-bootstrap/step/config/ca.json` -> `admin_provisioner_encrypted_key` (copy `authority.provisioners[].encryptedKey` for the `admin` JWK provisioner)
|
||||
|
||||
If you are only validating wiring first, `admin_provisioner_encrypted_key` can be an encrypted placeholder and replaced later.
|
||||
|
||||
If rotating provisioner password, also set:
|
||||
|
||||
- `/tmp/janus-step-ca-bootstrap/admin_jwk_password.txt` -> `janus.admin_jwk` in `keys/secrets.yaml`
|
||||
|
||||
Secret source-of-truth after this split:
|
||||
|
||||
- `modules/hosts/janus/secrets.yaml`: Janus runtime CA secrets (`ca_password`, `intermediate_ca_key`, `ssh_host_ca_key`, `ssh_user_ca_key`, `admin_provisioner_encrypted_key`)
|
||||
- `keys/secrets.yaml`: shared Janus provisioner secret (`janus.admin_jwk`) consumed across hosts
|
||||
|
||||
Then update public artifacts in repo from generated output:
|
||||
|
||||
- `modules/hosts/janus/public/root_ca.crt` from `/tmp/janus-step-ca-bootstrap/step/certs/root_ca.crt`
|
||||
- `modules/hosts/janus/public/intermediate_ca.crt` from `/tmp/janus-step-ca-bootstrap/step/certs/intermediate_ca.crt` (public certificate; intentionally committed, not stored in SOPS)
|
||||
- `modules/hosts/janus/fingerprint` from:
|
||||
```shell
|
||||
step certificate fingerprint /tmp/janus-step-ca-bootstrap/step/certs/root_ca.crt
|
||||
```
|
||||
- `modules/hosts/janus/public/ssh_user_ca_key.pub` from `/tmp/janus-step-ca-bootstrap/step/certs/ssh_user_ca_key.pub`
|
||||
- `modules/hosts/janus/public/ssh_host_ca_key.pub` from `/tmp/janus-step-ca-bootstrap/step/certs/ssh_host_ca_key.pub`
|
||||
|
||||
## First boot checks
|
||||
|
||||
After switching Janus, verify the declarative bootstrap units instead of running
|
||||
ad hoc issuance commands first:
|
||||
|
||||
```shell
|
||||
systemctl status step-ca.service
|
||||
systemctl status ssh-certs-renew.service
|
||||
systemctl status ssh-certs-renew.timer
|
||||
systemctl status mtls-bootstrap.service
|
||||
ssh-host-cert-check
|
||||
mtls-check
|
||||
```
|
||||
|
||||
`ssh-certs-renew.service` issues the SSH host certificate when it is missing
|
||||
or expiring. `mtls-bootstrap.service` issues the first Janus mTLS bundle
|
||||
only when the configured certificate files are absent or invalid; recurring mTLS
|
||||
renewal remains handled by `mtls-renew.timer`.
|
||||
|
||||
## Back up the root CA key offline
|
||||
|
||||
The root CA private key is **not** deployed and is never needed by the running CA.
|
||||
It is only used to sign/rotate intermediates. Store it offline (e.g. 1Password)
|
||||
before wiping the bootstrap directory, otherwise intermediate rotation becomes
|
||||
impossible and any future rotation forces a full root rotation (redistributing
|
||||
`root_ca.crt` + `fingerprint` to every client).
|
||||
|
||||
Back up, then wipe the bootstrap material:
|
||||
|
||||
- `/tmp/janus-step-ca-bootstrap/step/secrets/root_ca_key` -> offline secret store
|
||||
- `/tmp/janus-step-ca-bootstrap/ca_password.txt` -> offline secret store (root/intermediate key password)
|
||||
|
||||
```shell
|
||||
shred -u /tmp/janus-step-ca-bootstrap/step/secrets/root_ca_key 2>/dev/null || true
|
||||
rm -rf /tmp/janus-step-ca-bootstrap
|
||||
```
|
||||
@@ -2,108 +2,63 @@
|
||||
let
|
||||
username = "john";
|
||||
hostname = "janus";
|
||||
ca-url = "https://janus.john-stream.com/";
|
||||
fingerprint = builtins.readFile ./fingerprint;
|
||||
ipv4 = "192.168.1.32";
|
||||
ipv6 = "fded:fb16:653e:25da:be24:11ff:fe6b:4d57";
|
||||
names = [ "${hostname}.john-stream.com" ipv4 ipv6 ];
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.janus-ca =
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.janus-ca;
|
||||
johnHome = lib.attrByPath [ "users" "users" username "home" ] "/home/${username}" config;
|
||||
johnGroup = lib.attrByPath [ "users" "users" username "group" ] username config;
|
||||
cfgInEtc = lib.hasPrefix "/etc/" cfg.certDir;
|
||||
certDirEtcPath =
|
||||
if cfgInEtc then
|
||||
lib.removePrefix "/etc/" cfg.certDir
|
||||
else
|
||||
cfg.certDir;
|
||||
certRootEtcPath = "${certDirEtcPath}/root_ca.crt";
|
||||
mkStepRules = home: user: group: [
|
||||
"d ${home}/.step 0700 ${user} ${group} -"
|
||||
"d ${home}/.step/config 0700 ${user} ${group} -"
|
||||
"d ${home}/.step/certs 0700 ${user} ${group} -"
|
||||
"L+ ${home}/.step/config/defaults.json - - - - /etc/step-ca/defaults.json"
|
||||
"L+ ${home}/.step/certs/root_ca.crt - - - - ${cfg.certDir}/root_ca.crt"
|
||||
];
|
||||
in
|
||||
{
|
||||
options.janus-ca = {
|
||||
certDir = lib.mkOption {
|
||||
description = "String path to where the mtls certs will be stored.";
|
||||
type = lib.types.str;
|
||||
default = "/etc/step-ca/certs";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
environment.etc = lib.mkIf cfgInEtc {
|
||||
"step-ca/defaults.json".text = builtins.toJSON {
|
||||
inherit ca-url fingerprint;
|
||||
root = "/etc/${certRootEtcPath}";
|
||||
};
|
||||
"${certRootEtcPath}".source = ./root_ca.crt;
|
||||
};
|
||||
systemd.tmpfiles.rules =
|
||||
mkStepRules johnHome username johnGroup
|
||||
++ mkStepRules "/root" "root" "root";
|
||||
};
|
||||
};
|
||||
|
||||
flake.modules.homeManager.janus-ca = { config, ... }: {
|
||||
home.file.".step/config/defaults.json".text = builtins.toJSON {
|
||||
inherit ca-url fingerprint;
|
||||
root = "${config.home.homeDirectory}/.step/certs/root_ca.crt";
|
||||
};
|
||||
home.file.".step/certs/root_ca.crt".source = ./root_ca.crt;
|
||||
};
|
||||
|
||||
flake.nixosConfigurations."${hostname}" = inputs.nixpkgs.lib.nixosSystem {
|
||||
modules = with inputs.self.modules; [
|
||||
nixos.lxc
|
||||
nixos.mysops
|
||||
nixos.step-ssh-host
|
||||
nixos.janus-ca
|
||||
nixos.login-text
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
nixos."${username}"
|
||||
nixos.docker
|
||||
nixos.login-text
|
||||
nixos.ssh-new
|
||||
nixos.mysops
|
||||
nixos.step-ca # Runs the step-ca server
|
||||
nixos.step-client # Uses the step-ca server as a client
|
||||
nixos.mtls
|
||||
{
|
||||
({ config, lib, pkgs, ... }: {
|
||||
networking.hostName = hostname;
|
||||
step-ssh-host = {
|
||||
hostname = hostname;
|
||||
loginText.extraServiceStatus = {
|
||||
"Step-CA" = "step-ca";
|
||||
};
|
||||
sops.defaultSopsFile = ./secrets.yaml;
|
||||
ssh-new.certificates = {
|
||||
provisioner = "admin";
|
||||
host = {
|
||||
enable = true;
|
||||
extraPrincipals = names;
|
||||
autoRenew = true;
|
||||
};
|
||||
user.enable = true;
|
||||
};
|
||||
step-ca = {
|
||||
rootCertPath = ./public/root_ca.crt;
|
||||
intermediateCertPath = ./public/intermediate_ca.crt;
|
||||
dnsNames = names;
|
||||
secrets = {
|
||||
sopsFile = ./secrets.yaml;
|
||||
caPassword = "janus/ca_password";
|
||||
intermediateKey = "janus/intermediate_ca_key";
|
||||
sshHostCaKey = "janus/ssh_host_ca_key";
|
||||
sshUserCaKey = "janus/ssh_user_ca_key";
|
||||
adminProvisionerEncryptedKey = "janus/admin_provisioner_encrypted_key";
|
||||
};
|
||||
};
|
||||
step-client.caUrl = "https://${ipv4}/";
|
||||
mtls = {
|
||||
enable = true;
|
||||
subject = hostname;
|
||||
san = [
|
||||
"${hostname}.john-stream.com"
|
||||
"192.168.1.244"
|
||||
];
|
||||
san = names;
|
||||
bootstrap = {
|
||||
enable = true;
|
||||
after = [ "sops-nix.service" "step-ca.service" ];
|
||||
wants = [ "step-ca.service" ];
|
||||
provisionerPasswordFile = config.sops.secrets."janus/admin_jwk".path;
|
||||
};
|
||||
|
||||
home-manager.users."${username}" = {
|
||||
imports = with inputs.self.modules.homeManager; [
|
||||
mysops
|
||||
step-ssh-user
|
||||
};
|
||||
})
|
||||
];
|
||||
shell.program = "zsh";
|
||||
docker.enable = true;
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
perSystem = { system, pkgs, lib, ... }: {
|
||||
packages.janus-ca = inputs.wrappers.lib.wrapPackage {
|
||||
inherit pkgs;
|
||||
package = pkgs.step-cli;
|
||||
binName = "janus-cert";
|
||||
args = [
|
||||
"ca" "certificate"
|
||||
"--ca-url=${ca-url}"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBuzCCAWKgAwIBAgIQIW2tnzaR3eHv3EzooE+QGjAKBggqhkjOPQQDAjAoMQ4w
|
||||
DAYDVQQKEwVKYW51czEWMBQGA1UEAxMNSmFudXMgUm9vdCBDQTAeFw0yNTEyMTgw
|
||||
NjAyMjlaFw0zNTEyMTYwNjAyMjlaMDAxDjAMBgNVBAoTBUphbnVzMR4wHAYDVQQD
|
||||
ExVKYW51cyBJbnRlcm1lZGlhdGUgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
|
||||
AARyjfmHDMD+aOIESYF9v9Pe68aRgINpjRskGdELz4Y5+x7QNHvbZPEdkox0Qbcp
|
||||
XETmrcrw6fxBnImYJM5/IU0do2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/
|
||||
BAgwBgEB/wIBADAdBgNVHQ4EFgQUZN0zt5HKZLj0YtGTI7ROw8wLCw8wHwYDVR0j
|
||||
BBgwFoAUaOeW8ssqNrHjz/vM8/rjOJlzVfswCgYIKoZIzj0EAwIDRwAwRAIgE6W7
|
||||
yPG7tpLdwAUVF8BtOZV8TEDwmS1gudrcEbgXyXcCIETTUmM4GlN+zxMGYKs6OybN
|
||||
IxTY4WmviiXMsDoGWm0B
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1 @@
|
||||
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNug18oLH0vZxnibXJzMJvTWFPZTnSlhCDDVi+rHhgnIum6ZXQ4SF+VHOOAM5BbzZmMKitNJ5lcrGP15Eur7DzQ=
|
||||
@@ -0,0 +1,40 @@
|
||||
janus:
|
||||
ca_password: ENC[AES256_GCM,data:z6i0ELrn6XwVbJ7gP1GkpWe0Xw==,iv:3pZs/kzPp6bL8iv8jITZnAHuCycbSjhWswgzuJuLvjE=,tag:3gUftdyVXPVWe5dGxh9dvw==,type:str]
|
||||
intermediate_ca_key: ENC[AES256_GCM,data:tHP5V/yyrx7ydebg+cN30sLlRhnn01oWogywCUVm4YpKbWTJASaH4G74aaqPAGqETD1Go0Y5xR4DRkBJHBlTc3qzNj5/pzrSKwqfc537SCSUX79hTzw6MvTvukmbg4c/vF0ts5vZPYyi6mYwiAD2Ouk8HwU+AZOlCSm0VWL6rFjmq++fX4jhV0JRpcViBD8tjXpElpT/btyPAAXjaJgqMMZKALfwnAgXXrdy5E5FsuegQRfBadCDLsv0A+qq14CnstfPRkYAKqAhTL2HlmKKVtRiWTMZ6D14dYteUW+NQ33zKVIDz2a0M8tCp+41bDQWC9KrxrKcNVWc6ajAW11DzufP+lVhyWoZF+jkHZjWuy0lsgAUsTvZggLPU6euXHAjDeO4Z3ZQHvViVM9pVnsMzN6/w0i7gHSBFBw=,iv:actN7L8G7xbh9dYq7zQoOvnDYtcqAodV1V5Yu1q5elY=,tag:vas5E1ywf3sP1knVd7iE7A==,type:str]
|
||||
ssh_host_ca_key: ENC[AES256_GCM,data:eD0aZskYgsN7NWWo98VzrJGfQsbXLPUv9tWz8/GPU9UJcDaDmjEOs96cdW08IvINZXTMJtUscMGmewWX5mg1zevYGBYF8xgfdzT1pao5tw/F079aLc8HNxRn1s+4lL/GINtRXXK13tzYTPeI6Z1g4AE8UqVDZ/65+oLzkzWljE93UM6G9K78+ulBZq27Www0+dqT00n03YSfDCO8LgEJjxo03aShLXth+nT2SGZDfH9sILkZaDOeqOgQpW43L9rU+5yBCcGgCkBuhb8jMu2yStLCRxDewTNcxDYvIsNr1T/7jammRgUn+lYNcKgHeZS81dgNQkXmzRwErYbAx22zCaHdmSlH7RvF0olVCWzgXY4uSjbMFmQqNVVlj7UpYXRIIWnmiiwiBJQlmCJbm/OjS43dTYwZZ0waRIs=,iv:0TTQe2DC6EFiNhFEA3DVv2yu487pr4iX7siooA3EU94=,tag:mlZIquPBWR/5OVU7ZW0Uqg==,type:str]
|
||||
ssh_user_ca_key: ENC[AES256_GCM,data:CrjtkGfaUklNsOfIWhHwJXzBCJN2RvJKZQLueqAaOmY5niMdVRRnQahtMCtyJ7QxONs26VZkdcLmq4mfHWUA8lo4Q5ssFvqWvIcHvj4J0i0eB4RqQev2KGo/lg+GybZK2opBWS5z7msfPI94Fgu12qN46brBdbgVgclxqS23MR+A8d+VTcUYRcrPLhf4VTPH8eNGnhlM47tjzp7PjpMOumpkjbrNnNyPG3rVzvH0zbCc2HhJxtstEdoLT1u+1FxphR6kroRd/F+xQAY82A4UYVuHfaNkc8thHLA+wbGf5ReRZode5vCz4ErEWpJhnltU8YVgMDQSqE5gDuxWFv+P17hUdNXUmVI8QBiyu0sI8oNIBT6xDvK69rgzKaEy8MON/2E31CpQALokEiDB1Xn04W3rdnSVS6m84ck=,iv:lGrSv/0qCXha7rK/nEtZa0cH2wKtGxKigUsxqahk3SE=,tag:ZmxenXfXt9Pa4euZIgt1bw==,type:str]
|
||||
admin_provisioner_encrypted_key: ENC[AES256_GCM,data:lv85vgW77Qiy4UOcHt5SaVvuGmBsDIdjTxbUUaxGeP2H+XghTVuHZ/oLNXaPYqBFKKFqctG7FeL3/Gnm5wD7DXn9z6Cwc9eSZjNHUEiYcQLThBHDUOArk/CHhWvuAhWbuT5yBwVJkUmBpO5ZR3slRAh8RS/EBhb3iOXGW63i2Bj1kFAyf4XucmWY8lkRRpIj11gkWA1gVZqJkQI5AJDf4ikvYI/5p33wtQFpClsKJsJRzaoCee+m3w2Obq8hYMdS1mGnXm1L3tbkAj0130dl4/eOvOZq7le2S5Fo0WAFFyk1nhNz771fRI3WtiA85IHC9Nd3v6jGek+PVclk0D4rfvlQTF1yOpemtcsX2BbAbbB2j7sJ5a5bM9O5lotBoFlGQP/co5EU7Ki0+sTYdQxsHVSQhvsCTv5IhSMSjrzkUVN0RV7EwSkKdLK65Q8bR1pmOdXVDAPsyjB4tUJlwWKisrkAc5oyM+mI0Lalz7nvbTYvgtiS0vcBnGmRwcxDC5q//BTm4RhAB2e/My0MadVxF3aEn6fMaoK+3ef1zAToBRG8bV1vC+trhz3ucPqvMNPxjrSQpOdxzJyqlPgfKFVRI/DEQhO0XWpnU3h7wA1mV/dyjeosFVydfKN5iOLl+xF3VgRpisV2oE0Fgas6JkOGwy/d+MpkgNz/Amr+9phZA/bXl2lpDYIKj6aBduqizI4jV1jpM2FHtv99Jkg24DKIcPSZVvzVYr6j8FMmgaxOsA3Z72GlleLdwBKtuBC0GvU=,iv:kcJEhE1d4vYLHk+W2ay2rid+KCuarAIxUZSl/0wi5W4=,tag:7VARgV4a1nT+beIYWelqyw==,type:str]
|
||||
ssh_host_ed25519_key: ENC[AES256_GCM,data:6NxQm3pHzHmBkGJq/YLswi7uyMXzbzrsBPoqhpFA25PmHJBeIUVi0sIWPsdGDemx8dzEZ3oEB4dGbZ8JSvywpnSdvIzUhWK21BZLpbutUDBFtG68tkFK5N7dJ/dlA000oNEq5JiqrlKdFuVVjtUj7BK887a5xK7gyYfiqvv4E5dWv9IGk7lCHb/tjKkXGpYTRg1MVv2wSoVGRRSW1/97s2gAFMzeG48yJlwfiCQERXpHBte2rW5vZrOyR57J0ienf75wYwz6dLJuM1YZLKP9Ob8hCs00PEyxq7+JZuX/tx2yRiYKlgCYhtVbLbnjriPnSpKB/Xn/5t2FGP3ogfWUx+xkUm8lHDPbzrI86LNpFo016Ge2gDCDLvqLr9o4SDqnwmJfiAdMQokH7rJuQOiqk8gnWQCk8cvKc3vdYE0F6TnjbnZHMU4JbnmCr40xkBXkya/9jxVAC8RYfaW88hmAfQ9mYSdnbbUipC9Hv9Qhwst+Yqe/HxwaWfyimE0CpxOopwUT2qT/+ZO8Qjy/HnK4,iv:jlWTcKt/fvrry/OTamtKHNZAZmlxAj6TgGoPDor4HeY=,tag:btP8xFMEDzd/Nzr4EqIDZg==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRZ1pQQW1rekYzUDFGcXBW
|
||||
M2RHSXUvbCtvemtHYUljNmxXOCt4dEczYUU0CkdFWDRBYWRhQStYb1RtbEFmZVk4
|
||||
bWxMTnhKVXlZWDRlcWZHWWhaSC9KMW8KLS0tIER6VWJGY28ramk2NDhZa0x5RU1s
|
||||
YmU5S1FCVkJyNTNIVXhJSkdQVHYxMVkKEkbFmD4a7FWC8cKpZu2ZVot5ibheTQ1h
|
||||
H6uqwAYT/UIXqnceyZyoT9JRcZyQy7xAUAJzQMe+TxUr+8iHAm7aBg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1qahhlzeanprtykym9jymk2t95uedr7cwx9sdshx46q2m6u66fucsqua8l3
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqWjBiVDJSZFRQYis3dSt5
|
||||
OFFMbGhyaHgxSDBjdnVMSlZ5d3B0eEhVM0VVCmw4TXk2a0s3ZDRCeHhEVFpGRkpl
|
||||
b1lSQUJPL0hNRk50UEhHejNkL2VRd1EKLS0tICtXaWVMN0FSejNsNGNTOGh1WG13
|
||||
aFJINlNKOVNhc3VBOU00VXYyMTRFdVkKV251o5CegRdDxvhpOJjK5ITp/+DVBE9o
|
||||
R3eAsTXG5SSN50rcZYjwoEgYUeiRB4dQ/NYw6lVDUFrOf/a7sAr32g==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYYjlQRGVmRnRGbjNNT3dC
|
||||
cWJCWmtrM1BnbE80QnpTWXdHMytqSmFHOVhjCm9LYzd4R2s1TENIVkFYK3g4STRk
|
||||
cGIxMS9RQ2Y1dmR6RjZYeVF5LzRoelkKLS0tIEdjaGk2WnNCRVA0S1dxRzJyWFc3
|
||||
Yzh0MlFSZTR0Z0RhUnhHQzRSbFRXaUEK+5jadT4PMWPrljNcXVW3S1yJ1nI0iei8
|
||||
htf09S2S5jraIFkl2x6Rv8IqOnKVPPgTZkvZqPMQoM4zcs+o1/lnug==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
|
||||
lastmodified: "2026-07-05T01:59:20Z"
|
||||
mac: ENC[AES256_GCM,data:s4HAH/T9iqHooXXswMoeC1ttJwpyXQgnzy4Op2WPP55nowM//itnxpBzaqlMQUuuqMi4kd8wD/AakrlPRsbsBjgxnPYqmBKQA5eg+3a8QSf31HcLEbhtOsi38jOG2L2VMkdVfihvbe8NdFw4AD1tPviDhs7t9zjWEH1r/eCu+as=,iv:+FmOEuFM0V1ID3zkvNXF8m0ukQpq0Nmbdp0s4/WR/PY=,tag:/e4xkd2bdfC5fdHXJB4NAA==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.13.1
|
||||
@@ -0,0 +1,75 @@
|
||||
{ withSystem, self, inputs, ... }:
|
||||
let
|
||||
username = "john";
|
||||
hostname = "john-kde";
|
||||
in
|
||||
{
|
||||
flake.modules.homeManager."${hostname}" = { config, pkgs, lib, ... }:
|
||||
let
|
||||
selfPkgs = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||
flakeDir = "${config.xdg.configHome}/home-manager";
|
||||
in
|
||||
{
|
||||
imports = with inputs.self.modules.homeManager; [
|
||||
rebuild
|
||||
john
|
||||
# mtls
|
||||
# restic
|
||||
docker
|
||||
desktop
|
||||
step-client
|
||||
# mysops
|
||||
# myPackage
|
||||
# myStepClient
|
||||
];
|
||||
# TODO: make this more restrictive, rather than allowing all unfree packages
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
nixpkgs.config.permittedInsecurePackages = [ "openssl-1.1.1w" ];
|
||||
|
||||
targets.genericLinux.enable = true;
|
||||
|
||||
home.username = "${username}";
|
||||
home.homeDirectory = "/home/${username}";
|
||||
home.packages = with pkgs; [
|
||||
selfPkgs.jsl-zsh
|
||||
selfPkgs.my-neovim
|
||||
# selfPkgs.step-bootstrap
|
||||
# selfPkgs.wg-platform
|
||||
# self'.packages.myWrappedPackage
|
||||
# (inputs.self.wrappers.test-push.apply {
|
||||
# inherit pkgs flakeDir;
|
||||
# host = testHost;
|
||||
# target = testTarget;
|
||||
# }).wrapper
|
||||
];
|
||||
|
||||
homeManagerFlakeDir = flakeDir;
|
||||
docker.enable = true;
|
||||
|
||||
ssh = {
|
||||
certificates.enable = true;
|
||||
knownHosts = [
|
||||
"fded:fb16:653e:25da:be24:11ff:fea0:753f ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ9ZqiWPrCwHjxFCiu0lT4rlQs7KyMapxKJQQ5PJP1eh"
|
||||
];
|
||||
matchSets = {
|
||||
certs = true;
|
||||
appdaemon = true;
|
||||
homelab = true;
|
||||
dev = true;
|
||||
tailscale = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
flake.homeConfigurations."john@john-kde" = inputs.home-manager.lib.homeManagerConfiguration {
|
||||
pkgs = import inputs.nixpkgs.outPath {
|
||||
localSystem.system = "x86_64-linux";
|
||||
config = {
|
||||
allowUnfree = true;
|
||||
permittedInsecurePackages = [ "openssl-1.1.1w" ];
|
||||
problems.handlers.sublimetext4.broken = "ignore";
|
||||
};
|
||||
};
|
||||
modules = [ inputs.self.modules.homeManager."${hostname}" ]; # Uses the module defined above
|
||||
};
|
||||
}
|
||||
@@ -5,60 +5,34 @@
|
||||
hostname = "john-p14s";
|
||||
homeDirectory = config.home-manager.users.john.home.homeDirectory;
|
||||
flakeDir = "${homeDirectory}/Documents/dendritic";
|
||||
my-neovim = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.my-neovim;
|
||||
selfPkgs = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
self.modules.nixos.base
|
||||
self.modules.nixos.p14sHardware
|
||||
self.modules.nixos.onepassword
|
||||
];
|
||||
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
nixpkgs.config = {
|
||||
permittedInsecurePackages = [ "openssl-1.1.1w" ];
|
||||
allowUnfree = true;
|
||||
};
|
||||
|
||||
rebuild.flakeDir = flakeDir;
|
||||
|
||||
networking = {
|
||||
hostName = hostname;
|
||||
networkmanager.enable = true;
|
||||
};
|
||||
|
||||
# Enable automatic login for the user.
|
||||
# services.displayManager.autoLogin.enable = true;
|
||||
# services.displayManager.autoLogin.user = "john";
|
||||
networking.hostName = hostname;
|
||||
|
||||
programs.zsh.enable = true;
|
||||
services.openssh.enable = true;
|
||||
services.tailscale.enable = true;
|
||||
|
||||
# List packages installed in system profile. To search, run:
|
||||
# $ nix search wget
|
||||
environment.systemPackages = with pkgs; [
|
||||
wget
|
||||
cacert
|
||||
busybox
|
||||
dig
|
||||
samba
|
||||
my-neovim
|
||||
selfPkgs.my-neovim
|
||||
selfPkgs.wg-platform
|
||||
selfPkgs.jsl-zsh
|
||||
];
|
||||
|
||||
security.pam.services.swaylock = {};
|
||||
security.pam.services.swaylock.fprintAuth = true;
|
||||
|
||||
programs._1password.enable = true;
|
||||
programs._1password-gui = {
|
||||
enable = true;
|
||||
# Certain features, including CLI integration and system authentication support,
|
||||
# require enabling PolKit integration on some desktop environments (e.g. Plasma).
|
||||
polkitPolicyOwners = [ "john" ];
|
||||
# TODO this should not be a hardcoded username
|
||||
};
|
||||
|
||||
# This is needed for VSCode remote support. Read: https://nixos.wiki/wiki/Visual_Studio_Code
|
||||
programs.nix-ld.enable = true;
|
||||
|
||||
# This value determines the NixOS release from which the default
|
||||
# settings for stateful data, like file locations and database versions
|
||||
# on your system were taken. It's perfectly fine and recommended to leave
|
||||
@@ -67,43 +41,10 @@
|
||||
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
|
||||
system.stateVersion = "24.05"; # Did you read the comment?
|
||||
|
||||
# Set your time zone.
|
||||
time.timeZone = "America/Chicago";
|
||||
|
||||
# Select internationalisation properties.
|
||||
i18n = {
|
||||
defaultLocale = "en_US.UTF-8";
|
||||
extraLocaleSettings = {
|
||||
LC_ADDRESS = "en_US.UTF-8";
|
||||
LC_IDENTIFICATION = "en_US.UTF-8";
|
||||
LC_MEASUREMENT = "en_US.UTF-8";
|
||||
LC_MONETARY = "en_US.UTF-8";
|
||||
LC_NAME = "en_US.UTF-8";
|
||||
LC_NUMERIC = "en_US.UTF-8";
|
||||
LC_PAPER = "en_US.UTF-8";
|
||||
LC_TELEPHONE = "en_US.UTF-8";
|
||||
LC_TIME = "en_US.UTF-8";
|
||||
};
|
||||
};
|
||||
|
||||
services.libinput.enable = true; # Enable touchpad support (enabled default in most desktopManager).
|
||||
services.fprintd.enable = true; # Enables fingerprint sensor
|
||||
|
||||
# Enable sound with pipewire.
|
||||
services.pulseaudio.enable = false;
|
||||
security.rtkit.enable = true; # PulseAudio server uses this to acquire realtime priority.
|
||||
services.pipewire = {
|
||||
enable = true;
|
||||
alsa.enable = true;
|
||||
alsa.support32Bit = true;
|
||||
pulse.enable = true;
|
||||
# If you want to use JACK applications, uncomment this
|
||||
#jack.enable = true;
|
||||
|
||||
# use the example session manager (no others are packaged yet so this is enabled by default,
|
||||
# no need to redefine it in your config for now)
|
||||
# media-session.enable = true;
|
||||
};
|
||||
|
||||
home-manager.users.root = {
|
||||
imports = with inputs.self.modules.homeManager; [
|
||||
@@ -115,19 +56,12 @@
|
||||
gnome
|
||||
desktop
|
||||
mysops
|
||||
step-client
|
||||
rebuild
|
||||
{
|
||||
my-vscode.enable = true;
|
||||
mysops.hostSecretFile = "${flakeDir}/modules/hosts/john-p14s/secrets.yaml";
|
||||
homeManagerFlakeDir = "${flakeDir}";
|
||||
shell.program = "zsh";
|
||||
home.packages = with pkgs; [
|
||||
bash
|
||||
discord
|
||||
my-neovim
|
||||
proton-vpn
|
||||
joplin-desktop
|
||||
];
|
||||
ssh.certificates.enable = true;
|
||||
ssh.matchSets = {
|
||||
appdaemon = true;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
"${inputs.nixos-hardware}/lenovo/thinkpad/p14s/amd/gen4"
|
||||
] ++ (with self.modules.nixos; [
|
||||
p14sConfiguration
|
||||
janus-ca
|
||||
rebuild
|
||||
sudo
|
||||
john
|
||||
@@ -20,6 +19,7 @@
|
||||
steam
|
||||
wireguard
|
||||
mtls
|
||||
step-client
|
||||
# greetd
|
||||
# niri
|
||||
]);
|
||||
|
||||
@@ -3,11 +3,8 @@ let
|
||||
username = "john";
|
||||
hostname = "john-pc-ubuntu";
|
||||
|
||||
# testTarget = "fded:fb16:653e:25da:be24:11ff:fe89:1cc3"; # soteria
|
||||
# testTarget = "fded:fb16:653e:25da:be24:11ff:fea0:753f"; # test-nix
|
||||
testHost = "soteria"; # which host to test build
|
||||
testTarget = "test-nix";
|
||||
|
||||
in
|
||||
{
|
||||
flake.modules.homeManager."${hostname}" = { config, pkgs, lib, ... }:
|
||||
@@ -15,23 +12,20 @@ in
|
||||
selfPkgs = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||
resticPasswordFile = "${config.xdg.configHome}/restic/password.txt";
|
||||
flakeDir = "${config.xdg.configHome}/home-manager/jsl-dendritic";
|
||||
test-push = with pkgs; writeShellApplication {
|
||||
name = "test-push";
|
||||
runtimeInputs = [ nh ];
|
||||
text = ''nh os switch ${flakeDir}#${testHost} --target-host root@${testTarget} -e none'';
|
||||
};
|
||||
in
|
||||
{
|
||||
imports = with inputs.self.modules.homeManager; [
|
||||
rebuild
|
||||
john
|
||||
mysops
|
||||
janus-ca
|
||||
step-ssh-user
|
||||
mtls
|
||||
restic
|
||||
docker
|
||||
desktop
|
||||
step-client
|
||||
mysops
|
||||
# zed-editor
|
||||
# myPackage
|
||||
# myStepClient
|
||||
];
|
||||
# TODO: make this more restrictive, rather than allowing all unfree packages
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
@@ -42,36 +36,29 @@ in
|
||||
home.username = "${username}";
|
||||
home.homeDirectory = "/home/${username}";
|
||||
home.packages = with pkgs; [
|
||||
nixos-rebuild
|
||||
test-push
|
||||
selfPkgs.neovim-min
|
||||
# ${selfPkgs}.my-neovim
|
||||
# selfPkgs.richPrinter
|
||||
selfPkgs.janus-ca
|
||||
nil # Nix language server
|
||||
selfPkgs.jsl-zsh
|
||||
(inputs.self.wrappers.test-push.apply {
|
||||
inherit pkgs flakeDir;
|
||||
host = testHost;
|
||||
target = testTarget;
|
||||
}).wrapper
|
||||
];
|
||||
|
||||
shell.program = "zsh";
|
||||
|
||||
homeManagerFlakeDir = flakeDir;
|
||||
docker.enable = true;
|
||||
|
||||
step-ssh-user = {
|
||||
enable = true;
|
||||
principals = ["root" "${username}" "appdaemon"];
|
||||
provisioner = "admin";
|
||||
};
|
||||
ssh = {
|
||||
ssh-new = {
|
||||
certificates.enable = true;
|
||||
knownHosts = [
|
||||
"fded:fb16:653e:25da:be24:11ff:fea0:753f ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ9ZqiWPrCwHjxFCiu0lT4rlQs7KyMapxKJQQ5PJP1eh"
|
||||
];
|
||||
matchSets = {
|
||||
certs = true;
|
||||
appdaemon = true;
|
||||
homelab = true;
|
||||
dev = true;
|
||||
};
|
||||
};
|
||||
# ssh = {
|
||||
# matchSets = {
|
||||
# certs = true;
|
||||
# appdaemon = true;
|
||||
# homelab = true;
|
||||
# dev = true;
|
||||
# tailscale = true;
|
||||
# };
|
||||
# };
|
||||
|
||||
# This provides the keys at build time and will be included in the nix store
|
||||
sops.defaultSopsFile = ../../../keys/secrets.yaml;
|
||||
@@ -96,22 +83,28 @@ in
|
||||
"/home/john/john-nas"
|
||||
];
|
||||
};
|
||||
mtls = {
|
||||
enable = true;
|
||||
subject = hostname;
|
||||
san = [
|
||||
"${hostname}"
|
||||
"192.168.1.85"
|
||||
"spiffe://john-stream.com/ubuntu"
|
||||
];
|
||||
lifetime = "1h";
|
||||
renew.onCalendar = "*:1/10";
|
||||
};
|
||||
# mtls = {
|
||||
# enable = true;
|
||||
# subject = hostname;
|
||||
# san = [
|
||||
# "${hostname}"
|
||||
# "192.168.1.85"
|
||||
# "spiffe://john-stream.com/ubuntu"
|
||||
# ];
|
||||
# lifetime = "1h";
|
||||
# renew.onCalendar = "*:1/10";
|
||||
# };
|
||||
};
|
||||
|
||||
flake.homeConfigurations."john@john-pc-ubuntu" = withSystem "x86_64-linux" (ctx@{ config, inputs', ...}:
|
||||
inputs.home-manager.lib.homeManagerConfiguration {
|
||||
pkgs = inputs'.nixpkgs.legacyPackages;
|
||||
flake.homeConfigurations."john@john-pc-ubuntu" = inputs.home-manager.lib.homeManagerConfiguration {
|
||||
pkgs = import inputs.nixpkgs.outPath {
|
||||
localSystem.system = "x86_64-linux";
|
||||
config = {
|
||||
allowUnfree = true;
|
||||
permittedInsecurePackages = [ "openssl-1.1.1w" ];
|
||||
problems.handlers.sublimetext4.broken = "ignore";
|
||||
};
|
||||
};
|
||||
modules = [ inputs.self.modules.homeManager."${hostname}" ];
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.wrappers.test-push = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
|
||||
options = {
|
||||
flakeDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
host = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
target = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
sshUser = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "root";
|
||||
};
|
||||
elevationStrategy = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "none";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
binName = "test-push";
|
||||
package = config.pkgs.nh;
|
||||
args = [
|
||||
"os" "switch" "${config.flakeDir}#${config.host}"
|
||||
"--target-host" "${config.sshUser}@${config.target}"
|
||||
"--elevation-strategy" "${config.elevationStrategy}"
|
||||
];
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
{ self, inputs, ... }: {
|
||||
|
||||
flake.modules.nixos.omen-nixos = { pkgs, lib, ... }: {
|
||||
# import any other modules from here
|
||||
imports = [
|
||||
self.modules.nixos.omen-nixos-hardware
|
||||
self.modules.nixos.base
|
||||
self.modules.nixos.greetd
|
||||
self.modules.nixos.niri
|
||||
self.modules.nixos.onepassword
|
||||
self.modules.nixos.steam
|
||||
];
|
||||
|
||||
# Use the systemd-boot EFI boot loader.
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
|
||||
# Use latest kernel.
|
||||
boot.kernelPackages = pkgs.linuxPackages_latest;
|
||||
|
||||
networking.hostName = "omen-nixos"; # Define your hostname.
|
||||
|
||||
# Configure network proxy if necessary
|
||||
# networking.proxy.default = "http://user:password@proxy:port/";
|
||||
# networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
|
||||
|
||||
# Select internationalisation properties.
|
||||
# i18n.defaultLocale = "en_US.UTF-8";
|
||||
# console = {
|
||||
# font = "Lat2-Terminus16";
|
||||
# keyMap = "us";
|
||||
# useXkbConfig = true; # use xkb.options in tty.
|
||||
# };
|
||||
|
||||
# Enable CUPS to print documents.
|
||||
# services.printing.enable = true;
|
||||
|
||||
# Enable sound.
|
||||
# services.pulseaudio.enable = true;
|
||||
# OR
|
||||
# services.pipewire = {
|
||||
# enable = true;
|
||||
# pulse.enable = true;
|
||||
# };
|
||||
|
||||
# Enable touchpad support (enabled default in most desktopManager).
|
||||
# services.libinput.enable = true;
|
||||
|
||||
# Define a user account. Don't forget to set a password with ‘passwd’.
|
||||
# users.users.alice = {
|
||||
# isNormalUser = true;
|
||||
# extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
|
||||
# packages = with pkgs; [
|
||||
# tree
|
||||
# ];
|
||||
# };
|
||||
|
||||
# programs.firefox.enable = true;
|
||||
|
||||
# List packages installed in system profile.
|
||||
# You can use https://search.nixos.org/ to find more packages (and options).
|
||||
environment.systemPackages = with pkgs; [
|
||||
# vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
|
||||
wget
|
||||
git
|
||||
micro
|
||||
nh
|
||||
];
|
||||
|
||||
# Some programs need SUID wrappers, can be configured further or are
|
||||
# started in user sessions.
|
||||
# programs.mtr.enable = true;
|
||||
# programs.gnupg.agent = {
|
||||
# enable = true;
|
||||
# enableSSHSupport = true;
|
||||
# };
|
||||
|
||||
# List services that you want to enable:
|
||||
|
||||
# Enable the OpenSSH daemon.
|
||||
services.openssh.enable = true;
|
||||
|
||||
# Open ports in the firewall.
|
||||
# networking.firewall.allowedTCPPorts = [ ... ];
|
||||
# networking.firewall.allowedUDPPorts = [ ... ];
|
||||
# Or disable the firewall altogether.
|
||||
# networking.firewall.enable = false;
|
||||
|
||||
# Copy the NixOS configuration file and link it from the resulting system
|
||||
# (/run/current-system/configuration.nix). This is useful in case you
|
||||
# accidentally delete configuration.nix.
|
||||
# system.copySystemConfiguration = true;
|
||||
|
||||
# This option defines the first version of NixOS you have installed on this particular machine,
|
||||
# and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
|
||||
#
|
||||
# Most users should NEVER change this value after the initial install, for any reason,
|
||||
# even if you've upgraded your system to a new NixOS release.
|
||||
#
|
||||
# This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
|
||||
# so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how
|
||||
# to actually do that.
|
||||
#
|
||||
# This value being lower than the current NixOS release does NOT mean your system is
|
||||
# out of date, out of support, or vulnerable.
|
||||
#
|
||||
# Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
|
||||
# and migrated your data accordingly.
|
||||
#
|
||||
# For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
|
||||
system.stateVersion = "26.05"; # Did you read the comment?
|
||||
# ...
|
||||
|
||||
home-manager.users.john.imports = with inputs.self.modules.homeManager; [
|
||||
desktop
|
||||
# rebuild
|
||||
{
|
||||
my-vscode.enable = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.nixosConfigurations.omen-nixos = inputs.nixpkgs.lib.nixosSystem {
|
||||
modules = [
|
||||
self.modules.nixos.omen-nixos
|
||||
self.modules.nixos.john
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.modules.nixos.omen-nixos-hardware = { config, lib, pkgs, modulesPath, ... }: {
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
|
||||
boot.initrd.kernelModules = [ ];
|
||||
boot.kernelModules = [ "kvm-amd" ];
|
||||
boot.extraModulePackages = [ ];
|
||||
|
||||
fileSystems = {
|
||||
"/" = {
|
||||
device = "/dev/disk/by-uuid/35f77d1a-346c-4c52-83b2-7d25e2ac9fe1";
|
||||
fsType = "ext4";
|
||||
};
|
||||
"/mnt/shared" = {
|
||||
device = "/dev/disk/by-uuid/216e8dca-170d-4377-bf1a-69b574e1778c";
|
||||
fsType = "ext4";
|
||||
};
|
||||
};
|
||||
|
||||
swapDevices = [ ];
|
||||
|
||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
{ withSystem, self, inputs, lib, ... }:
|
||||
# Intent: NixOS service node for Forgejo + restic with mTLS, host-local secrets, and a paired Home Manager profile for john.
|
||||
let
|
||||
username = "john";
|
||||
hostname = "soteria";
|
||||
ipv4 = "192.168.1.233";
|
||||
ipv6 = "fded:fb16:653e:25da:be24:11ff:fe54:aa39";
|
||||
names = [ "${hostname}.john-stream.com" ipv4 ipv6 ];
|
||||
in
|
||||
{
|
||||
flake.nixosConfigurations."${hostname}" = inputs.nixpkgs.lib.nixosSystem {
|
||||
modules = with inputs.self.modules; [
|
||||
nixos.lxc
|
||||
nixos.login-text
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
nixos."${username}"
|
||||
nixos.ssh-new
|
||||
# nixos.mtls
|
||||
nixos.mysops
|
||||
nixos.docker
|
||||
nixos.step-client
|
||||
nixos.forgejo
|
||||
nixos.restic-server
|
||||
# nixos.restic-envoy
|
||||
({ config, pkgs, ... }: {
|
||||
networking.hostName = hostname;
|
||||
|
||||
# Removes password for sudo
|
||||
security.sudo-rs.extraRules = lib.mkAfter [
|
||||
{
|
||||
users = [ username ];
|
||||
commands = [
|
||||
{
|
||||
command = "ALL";
|
||||
options = [ "NOPASSWD" ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
# users.users."${username}".extraGroups = [ "mtls" ];
|
||||
# mtls = {
|
||||
# enable = true;
|
||||
# subject = hostname;
|
||||
# san = names;
|
||||
# lifetime = "12h";
|
||||
# renew.onCalendar = "*:3/15";
|
||||
# renew.reloadUnits = [ "forgejo.service" "restic-rest-server.service" ];
|
||||
# certReaders = [ config.services.forgejo.user "restic" ];
|
||||
# };
|
||||
|
||||
# forgejo = {
|
||||
# enable = true;
|
||||
# root_url = "https://forgejo.john-stream.com";
|
||||
# https = true;
|
||||
# port = 443;
|
||||
# };
|
||||
|
||||
# resticServer = {
|
||||
# enable = true;
|
||||
# dataDir = "/mnt/restic";
|
||||
# privateRepos = true;
|
||||
# listenAddress = "0.0.0.0:8000";
|
||||
# tls = {
|
||||
# certFile = config.mtls.certFile;
|
||||
# keyFile = config.mtls.keyFile;
|
||||
# };
|
||||
# };
|
||||
|
||||
loginText.extraServiceStatus = {
|
||||
Docker = "docker";
|
||||
"mTLS Renewal" = "mtls-renew.timer";
|
||||
"Forgejo" = "forgejo.service";
|
||||
"Forgejo Backup" = "forgejo-dump.timer";
|
||||
"Restic REST Server" = "restic-rest-server.service";
|
||||
};
|
||||
|
||||
ssh-new.certificates = {
|
||||
provisioner = "admin";
|
||||
host = {
|
||||
enable = true;
|
||||
extraPrincipals = names;
|
||||
autoRenew = true;
|
||||
};
|
||||
user.enable = true;
|
||||
};
|
||||
|
||||
# This provides the secrets at install time
|
||||
sops.defaultSopsFile = ./secrets.yaml;
|
||||
|
||||
home-manager.users."${username}".imports = [ inputs.self.modules.homeManager.soteria ];
|
||||
|
||||
environment.systemPackages = [
|
||||
inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.my-neovim
|
||||
inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.jsl-zsh
|
||||
];
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
flake.modules.homeManager.soteria = { config, pkgs, lib, ... }: {
|
||||
imports = with inputs.self.modules.homeManager; [
|
||||
rebuild
|
||||
mysops
|
||||
step-client
|
||||
({ config, pkgs, lib, ... }: {
|
||||
homeManagerFlakeDir = "${config.xdg.configHome}/home-manager";
|
||||
docker.enable = true;
|
||||
|
||||
# This will provide the edit-secrets script targeting this file
|
||||
mysops.hostSecretFile = "${config.homeManagerFlakeDir}/modules/hosts/soteria/secrets.yaml";
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
# flake.homeConfigurations.soteria = withSystem "x86_64-linux" (ctx@{ config, inputs', ...}:
|
||||
# inputs.home-manager.lib.homeManagerConfiguration {
|
||||
# pkgs = inputs'.nixpkgs.legacyPackages;
|
||||
# modules = [
|
||||
# inputs.self.modules.homeManager."${username}"
|
||||
# inputs.self.modules.homeManager.soteria
|
||||
# ];
|
||||
# });
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
janus:
|
||||
admin_jwk: ENC[AES256_GCM,data:2XcN5X77wTQ+OUXa4C9xAErGvrwJKseHjCcgoj6jt+c=,iv:9x+M1wM0dYND1JYkJjM4N8pSOok2OF+P9vmm9NCumTI=,tag:dTCfh+KB1iRJBVoNsGqvuw==,type:str]
|
||||
forgejo:
|
||||
#ENC[AES256_GCM,data:/wtm0uXbiWFoGNWtlTzVuNxBR7CPm2FMB98t3AxSj5V5rltLvzF9BjgWoJCiX3ltzmU=,iv:xaZXbUIGJHxPrLRQzEQI7hgRgc0y061jIhoE3zlcMaA=,tag:fQGheCIdBKm6wE+vtj7g6A==,type:comment]
|
||||
secret_key: ENC[AES256_GCM,data:/jcyeDcsryLqu9Q3VnNaENb71/Tl5JUr0zDzxt8L5UCtnJ/YHA6MKItrQ9ZHFv1XROtnSfZl9D5kKomeM8EVqA==,iv:HxMKAMVQ08gkq6SWENj0/d8i9PhcgPCp5eqbztj9bSg=,tag:nO2dFWT/vfgGc/9JIDZrGw==,type:str]
|
||||
@@ -11,33 +9,33 @@ forgejo:
|
||||
restic_password: ENC[AES256_GCM,data:u7QOZXJkxVG4J75K5nphb2uJGdz6jbWuVSsKKu+41fshp7cVoRijtr/Cs02LjVse,iv:bt1W2FeBTG6ypBFYzMPXPIkYTSn0uHURY2ui6MRgYY8=,tag:DObAMws/zQcM+UKUe9EECA==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
|
||||
enc: |
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5QThHOFdVQ0F6ZDE3QXdL
|
||||
QVF0WW5yRkNtNXIyRm1LeEhwY2dRRWo0WENZCmZoWmlXZDh4ZGlUR2JYOUxuaVAx
|
||||
WjhXVHBMNWdrdkgvTENJR2RpWldkNTAKLS0tIEF1bS9JSnFTbytBa1U5RjVzWkd3
|
||||
S0pKVVI2RFN5a1BMYXBEY3VOUUM5QTAKNarYZm9DKRQhosSJBn9yryFxDkmFTV/o
|
||||
i8b/tZ1ZybjEXX+X1EsgylM5u8iKkbEyUzqKO0E0gpg4qXSsJaMMYQ==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLM3NML2ozWExSTlY3ZXJH
|
||||
ZEpaN1RIOWVjNXRRQTlLQVZKTUUvZnBxREd3Cmp6b29aRDlUdDFwMVhwS2thODNO
|
||||
QzNWSVIrak1uMGRuUlBONkR2aW1nbXMKLS0tIDh2YmFaRXBvZ0Q2blhjTVphL3J3
|
||||
TkROaHdwWWN4YnkyczZLbDJYRTNrQTQKvHSXjP9EZkq9DrPZZHTIlFrPUhez6jZT
|
||||
LJvkmQgKluer7+lWr8XuuqtQsvhtef1di82SBG6C6YK0zWIxrMjFkA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1h0prahyukq4l564yqwgcpg3g6gdrjflk0suklussjjrjstxd9uesws8633
|
||||
enc: |
|
||||
recipient: age1p4gyh5260ewp7t2ctzv5ewj4a06szl389fm4gzy6vltxhjj73ers87u33g
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhdEN2M1lkMFoyQzg3bCsz
|
||||
cVB0NUlkV1JPRjQ1TW5RSVFHUjdNMERoRXlzCk9tc25iMG00TkZjWjk2S2cyVE1J
|
||||
cUFITDh2SkZGN0lpeWJVcWM3V3JrTTQKLS0tIENOWVBzTFYrdHRsMERwV0RKS01j
|
||||
QVI5TVl4L0dwOWpoQ1I2THZ6cWovWGMKIo1x1ZbdTyr/dNlPhvuomfk2MoPLsHyU
|
||||
N3CP3Scu3aZ7vqVua7uwtv4xQqyQI/yOnFjwxrVYPJ+N5Y/b4wsC6A==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwWThXZGtFSjFZVU1UWG9v
|
||||
Wk9sVEVWemtVWTJ6TGlOd3ZhVUZBYnJ1OFFRClpFNmEwTG1PR0pQUFJuTTVVZGRD
|
||||
bnpPdjZGVUJvS0NyWGJQbEV4cUlHZncKLS0tIDFNU1VxNjZYSks1OTMrblJPbDI1
|
||||
bHIrUnhsMWNhWVpVc3hCazlKTlV5YncKgR/AbgCE9JU8U8PH3ouYTZJOqL7rRODr
|
||||
5D2zT9pcTC3LQGZrhNtgjAi6IucU4yDAU7UhT3D7cZawwFTKVaoplw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
|
||||
enc: |
|
||||
recipient: age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGSGQvL2ZYM2dtV1JWaDR1
|
||||
RjJnN0YwWFM4d1IwTElqUnZQQkVDTmdydUJjCjdCUWxCVDNXUHVLd2ZHcDA5L0Mw
|
||||
cEVxVE84L2xmQnRoYmsxVEdMSEZjdGcKLS0tIHViRVJyTWt4Q3JrMWtYaS9QdHlS
|
||||
Yjd0MUcxcExvWVpCOUR3MkdZdGQyWUkKnru0Y2A98+0Mps7EtVK7ct3vPqIGveUt
|
||||
E5fzpcKvdefzObrx7BPTwJ19t2fZg/dSi7HKwx3vmKZSzyQaqJOzsg==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3K054SHE0V1RCYlRlTXJT
|
||||
bXo4ZnE1cFozY3R2dmg5S1BacC9xb2oweWpBClJJVnkrSytFaG8xR1l4SU5FNGxi
|
||||
UndnNzYwMUlXSjRYK3c3MzI4NmRVRVEKLS0tIFQ4NFdPeGI4LytWK0JPcUV4VjM2
|
||||
b1dtYWdFTTFQcW5PQml2OWs1Z0VZOFUKrDX7y3cU8KPRLKlO/HI/jdynqwO1TAS0
|
||||
WY4F0qFKmWwsJJjbnjdpYYP3O1/8RNeXjnUg8fGYY75sf7iAYWSe/w==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
|
||||
lastmodified: "2026-04-04T23:18:43Z"
|
||||
mac: ENC[AES256_GCM,data:qBgeli5lHb4pyA8nAADBuRBAaq8VbAIsFI37OZtgnbnoHW2crxo3YC+EknaIYnZpZ48kwVhQS5lGRjI6JsWWhTH3+LVAhTmS2Qj/pZTD/JDLK6XJGXS4U9nB7m9aGYyW8gFCy9/DfoJWGsS//+ZmUikKPfd5kMZgh1zGoYCIGug=,iv:h+2fA+bO2SMCNrEslP36x3BPRaIy25cU/DNX8CYSC6A=,tag:RvVyUZ4ONRaKaqGiT31eUQ==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
{ self, inputs, lib, ... }:
|
||||
let
|
||||
username = "john";
|
||||
hostname = "soteria";
|
||||
in
|
||||
{
|
||||
flake.nixosConfigurations."${hostname}" = inputs.nixpkgs.lib.nixosSystem {
|
||||
modules = with inputs.self.modules; [
|
||||
nixos.lxc
|
||||
nixos."${username}"
|
||||
nixos.mysops
|
||||
nixos.step-ssh-host
|
||||
nixos.login-text
|
||||
nixos.docker
|
||||
nixos.mtls
|
||||
nixos.janus-ca
|
||||
nixos.forgejo
|
||||
# nixos.restic-server
|
||||
# nixos.restic-envoy
|
||||
({ config, pkgs, ... }: {
|
||||
networking.hostName = hostname;
|
||||
time.timeZone = "America/Chicago";
|
||||
|
||||
# Removes password for sudo
|
||||
security.sudo-rs.extraRules = lib.mkAfter [
|
||||
{
|
||||
users = [ username ];
|
||||
commands = [
|
||||
{
|
||||
command = "ALL";
|
||||
options = [ "NOPASSWD" ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
users.users."${username}".extraGroups = [ "mtls" ];
|
||||
mtls = {
|
||||
enable = true;
|
||||
certDir = config.janus-ca.certDir;
|
||||
subject = hostname;
|
||||
san = [
|
||||
"${hostname}.john-stream.com"
|
||||
# "192.168.1.142"
|
||||
"forgejo.john-stream.com"
|
||||
"192.168.1.244"
|
||||
];
|
||||
lifetime = "12h";
|
||||
renew.onCalendar = "*:3/15";
|
||||
renew.reloadUnits = [ "forgejo.service" "restic-rest-server.service" ];
|
||||
certReaders = [ config.services.forgejo.user "restic" ];
|
||||
};
|
||||
forgejo = {
|
||||
enable = true;
|
||||
root_url = "https://forgejo.john-stream.com";
|
||||
https = true;
|
||||
port = 443;
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 8000 ];
|
||||
services.restic.server = {
|
||||
enable = true;
|
||||
privateRepos = true;
|
||||
listenAddress = "0.0.0.0:8000";
|
||||
extraFlags = [
|
||||
"--no-auth"
|
||||
"--tls"
|
||||
"--tls-cert=${config.mtls.certFile}"
|
||||
"--tls-key=${config.mtls.keyFile}"
|
||||
];
|
||||
};
|
||||
|
||||
loginText.extraServiceStatus = {
|
||||
Docker = "docker";
|
||||
"mTLS Renewal" = "mtls-renew.timer";
|
||||
Forgejo = "forgejo.service";
|
||||
"Forgejo Backup" = "forgejo-dump.timer";
|
||||
"Restic REST Server" = "restic-rest-server.service";
|
||||
};
|
||||
|
||||
step-ssh-host.hostname = hostname;
|
||||
|
||||
# This provides the secrets at install time
|
||||
sops.defaultSopsFile = ./secrets.yaml;
|
||||
|
||||
programs.zsh.enable = true;
|
||||
home-manager.users."${username}" = {
|
||||
imports = with inputs.self.modules; [
|
||||
homeManager."${hostname}"
|
||||
];
|
||||
};
|
||||
|
||||
environment.systemPackages = [
|
||||
inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.janus-ca
|
||||
inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.my-neovim
|
||||
inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.jsl-zsh
|
||||
];
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
flake.modules.homeManager."${hostname}" = { config, pkgs, lib, ... }: {
|
||||
imports = with inputs.self.modules; [
|
||||
homeManager.rebuild
|
||||
homeManager.mysops
|
||||
];
|
||||
|
||||
homeManagerFlakeDir = "${config.xdg.configHome}/home-manager";
|
||||
shell.program = "zsh";
|
||||
docker.enable = true;
|
||||
|
||||
# This will provide the edit-secrets script targeting this file
|
||||
mysops.hostSecretFile = "${config.homeManagerFlakeDir}/modules/hosts/soteria/secrets.yaml";
|
||||
};
|
||||
|
||||
flake.homeConfigurations."${hostname}" = inputs.home-manager.lib.homeManagerConfiguration {
|
||||
pkgs = import inputs.nixpkgs { system = "x86_64-linux"; };
|
||||
modules = with inputs.self.modules; [
|
||||
homeManager."${username}"
|
||||
homeManager."${hostname}"
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
{ inputs, ... }:
|
||||
let
|
||||
username = "john";
|
||||
hostname = "test-nix";
|
||||
in
|
||||
{
|
||||
flake.nixosConfigurations."${hostname}" = inputs.nixpkgs.lib.nixosSystem {
|
||||
modules = with inputs.self.modules; [
|
||||
nixos.lxc
|
||||
nixos.mysops
|
||||
nixos.step-ssh-host
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
nixos."${username}"
|
||||
nixos.docker
|
||||
{
|
||||
home-manager.users."${username}" = {
|
||||
imports = with inputs.self.modules.homeManager; [
|
||||
mysops
|
||||
];
|
||||
shell.program = "zsh";
|
||||
docker.enable = true;
|
||||
ssh.matchSets = {
|
||||
certs = true;
|
||||
homelab = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -40,7 +40,6 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
let
|
||||
flakeDir = config.homeManagerFlakeDir;
|
||||
hostnameCmd = "$(${lib.getExe pkgs.hostname} -s)";
|
||||
|
||||
flake-parts-check = with pkgs; writeShellApplication {
|
||||
name = "flake-parts-check";
|
||||
@@ -52,22 +51,6 @@
|
||||
'';
|
||||
};
|
||||
|
||||
nhms = with pkgs; writeShellApplication {
|
||||
name = "nhms";
|
||||
runtimeInputs = [ coreutils hostname nh ];
|
||||
text = ''
|
||||
HOSTNAME=$(hostname -s)
|
||||
echo "Switching to the $HOSTNAME home-manager profile"
|
||||
nh home switch ${flakeDir} -c "$HOSTNAME" "$@"
|
||||
'';
|
||||
};
|
||||
|
||||
nhmu = with pkgs; writeShellApplication {
|
||||
name = "nhmu";
|
||||
runtimeInputs = [ nhms ];
|
||||
text = ''nhms --update'';
|
||||
};
|
||||
|
||||
test-build = with pkgs; writeShellApplication {
|
||||
name = "test-build";
|
||||
runtimeInputs = [ coreutils nix hostname ];
|
||||
@@ -81,24 +64,6 @@
|
||||
nix eval "${flakeDir}#nixosConfigurations.$HOSTNAME.config.system.build.toplevel.drvPath"
|
||||
'';
|
||||
};
|
||||
|
||||
cleanup = with pkgs; writeShellApplication {
|
||||
name = "cleanup";
|
||||
runtimeInputs = [ coreutils home-manager nix ];
|
||||
text = ''
|
||||
set -e
|
||||
DAYS=$1
|
||||
if [ -z "$DAYS" ]; then
|
||||
echo "usage: cleanup <days>"
|
||||
exit 1
|
||||
fi
|
||||
home-manager expire-generations "-$DAYS days"
|
||||
nix profile wipe-history --older-than "''${DAYS}d"
|
||||
nix store gc
|
||||
nix store optimise
|
||||
'';
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
options = {
|
||||
@@ -120,13 +85,64 @@
|
||||
name = "build-tools";
|
||||
paths = [
|
||||
flake-parts-check
|
||||
nhms
|
||||
nhmu
|
||||
test-build
|
||||
cleanup
|
||||
(inputs.self.wrappers.home-switch.apply {
|
||||
inherit pkgs flakeDir;
|
||||
}).wrapper
|
||||
(inputs.self.wrappers.home-switch.apply {
|
||||
binName = lib.mkForce "nhmu";
|
||||
inherit pkgs flakeDir;
|
||||
extraOptions = [ "--update" ];
|
||||
}).wrapper
|
||||
(inputs.wrappers.lib.wrapPackage {
|
||||
binName = "cleanup";
|
||||
inherit pkgs;
|
||||
package = nh;
|
||||
args = [ "clean" "user" "--keep-since" "3days" ];
|
||||
})
|
||||
];
|
||||
})
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
flake.wrappers.home-switch = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
|
||||
options = {
|
||||
flakeDir = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
};
|
||||
user = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "$(whoami)";
|
||||
};
|
||||
hostname = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "$(hostname -s)";
|
||||
};
|
||||
configuration = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "${config.user}@${config.hostname}";
|
||||
};
|
||||
extraOptions = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
binName = "nhms";
|
||||
extraPackages = with config.pkgs; [ coreutils hostname nh ];
|
||||
preHook = ''
|
||||
CONFIG=${config.configuration}
|
||||
echo "Switching to $CONFIG from ${config.flakeDir}"
|
||||
'';
|
||||
package = config.pkgs.nh;
|
||||
args = [
|
||||
"home" "switch"
|
||||
"--configuration" "${config.configuration}"
|
||||
"$@"
|
||||
"${config.flakeDir}"
|
||||
] ++ config.extraOptions;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,17 +14,27 @@
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
];
|
||||
|
||||
environment.shells = [
|
||||
"${lib.getExe pkgs.zsh}"
|
||||
"${lib.getExe inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.jsl-zsh}"
|
||||
];
|
||||
|
||||
users.groups."${username}" = {};
|
||||
|
||||
users.users."${username}" = {
|
||||
isNormalUser = true;
|
||||
group = username;
|
||||
home = "/home/${username}";
|
||||
shell = lib.mkIf config.programs.zsh.enable pkgs.zsh;
|
||||
extraGroups = [ "input" "networkmanager" ]
|
||||
shell = pkgs.zsh;
|
||||
extraGroups = [ "input" "networkmanager" "video" "render" ]
|
||||
++ lib.optional isAdmin "wheel"
|
||||
++ lib.optional config.virtualisation.docker.enable "docker"
|
||||
++ lib.optional (isAdmin && config.services.forgejo.enable) config.services.forgejo.group
|
||||
++ lib.optional (isAdmin && config.services.postgresql.enable) "postgres";
|
||||
};
|
||||
|
||||
programs.zsh.enable = true;
|
||||
|
||||
security.sudo-rs.enable = lib.mkIf isAdmin true;
|
||||
|
||||
home-manager.useGlobalPkgs = true;
|
||||
@@ -33,7 +43,9 @@
|
||||
imports = [ self.modules.homeManager."${username}" ];
|
||||
home.username = "${username}";
|
||||
home.homeDirectory = "/home/${username}";
|
||||
# home.packages = homePackages;
|
||||
home.packages = with pkgs; [
|
||||
# fzf zoxide starship
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake-file.inputs = {
|
||||
config.flake-file.inputs = {
|
||||
wrapper-modules = {
|
||||
url = "github:BirdeeHub/nix-wrapper-modules";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
@@ -9,4 +9,13 @@
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
options = {
|
||||
# This is what allows wrappers to be defined in flake.wrappers.<wrapper-name> throughout different flake-parts modules
|
||||
flake = inputs.flake-parts.lib.mkSubmoduleOptions {
|
||||
wrappers = inputs.nixpkgs.lib.mkOption {
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
flake.modules.nixos.login-text = { config, lib, ... }:
|
||||
let
|
||||
defaultServiceStatus = {
|
||||
SSH = "sshd.socket";
|
||||
"SSH Cert Renewal" = "step-ssh-host-renew.timer";
|
||||
"SSH Socket" = "sshd.socket";
|
||||
"SSH Cert Renewal" = "ssh-certs-renew.timer";
|
||||
};
|
||||
in {
|
||||
options.loginText.extraServiceStatus = lib.mkOption {
|
||||
|
||||
+16
-2
@@ -1,13 +1,27 @@
|
||||
{ inputs, ... }:
|
||||
{
|
||||
flake.modules.nixos.lxc = { pkgs, lib, ...}: {
|
||||
flake.modules.nixos.lxc = { pkgs, lib, ...}:
|
||||
let
|
||||
selfPackages = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||
rootShellPath = lib.getExe' selfPackages.jsl-zsh-tty "jsl-zsh-tty";
|
||||
in
|
||||
{
|
||||
imports = with inputs.self.modules.nixos; [
|
||||
({ modulesPath, ... }: { imports = [ "${modulesPath}/virtualisation/proxmox-lxc.nix" ]; })
|
||||
];
|
||||
nixpkgs.hostPlatform = lib.mkForce "x86_64-linux";
|
||||
system.stateVersion = "25.11";
|
||||
time.timeZone = "US/Central";
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
environment.systemPackages = with pkgs; [ git zsh ];
|
||||
environment.systemPackages = with pkgs; [
|
||||
git
|
||||
selfPackages.jsl-zsh-tty
|
||||
];
|
||||
environment.shells = lib.mkAfter [ rootShellPath ];
|
||||
users.users.root.shell = lib.mkForce rootShellPath;
|
||||
|
||||
networking.nameservers = [ "192.168.1.150" ];
|
||||
networking.dhcpcd.extraConfig = "nohook resolv.conf";
|
||||
|
||||
# security.sudo-rs.enable = true;
|
||||
programs.nix-ld.enable = true;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
{
|
||||
flake.modules.homeManager.bash = { pkgs, lib, config, ... }:
|
||||
{
|
||||
programs.bash = lib.mkIf (config.shell.program == "bash") {
|
||||
programs.bash = {
|
||||
enable = true;
|
||||
enableCompletion = true;
|
||||
package = pkgs.bash;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -5,19 +5,27 @@
|
||||
enable = true;
|
||||
enableBashIntegration = true;
|
||||
enableZshIntegration = true;
|
||||
git = true;
|
||||
icons = "auto";
|
||||
colors = "auto";
|
||||
extraOptions = [
|
||||
package = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.my-eza;
|
||||
};
|
||||
};
|
||||
|
||||
perSystem = { system, pkgs, ... }: {
|
||||
packages.my-eza = inputs.wrappers.lib.wrapPackage {
|
||||
inherit pkgs;
|
||||
package = pkgs.eza;
|
||||
args = [
|
||||
"--all"
|
||||
"--long"
|
||||
"--group-directories-first"
|
||||
"--icons=auto"
|
||||
"--color=auto"
|
||||
"--sort=type"
|
||||
"--dereference"
|
||||
"--octal-permissions"
|
||||
"--smart-group"
|
||||
"--no-time"
|
||||
"--git"
|
||||
"$@"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,12 +14,5 @@
|
||||
ignorecase = true;
|
||||
};
|
||||
};
|
||||
|
||||
home.packages = with pkgs; [
|
||||
gdu
|
||||
# (writeShellScriptBin "lfcd" ''
|
||||
# . <(${lib.getExe pkgs.lf} -print-last-dir | sed 's/^/cd /')
|
||||
# '')
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
flake.modules.homeManager.ghostty = { config, pkgs, lib, ... }:
|
||||
let
|
||||
ghosttyPackage = pkgs.symlinkJoin {
|
||||
ghosttyX11 = pkgs.symlinkJoin {
|
||||
name = "ghostty-x11";
|
||||
paths = [ (config.lib.nixGL.wrap pkgs.ghostty) ];
|
||||
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||
@@ -24,6 +24,10 @@
|
||||
TERMINAL = "ghostty";
|
||||
};
|
||||
|
||||
home.packages = with pkgs; [
|
||||
inputs.self.packages.${system}.ghosttyGPUCheck
|
||||
];
|
||||
|
||||
targets.genericLinux.nixGL = {
|
||||
packages = inputs.nixgl.packages.${pkgs.stdenv.hostPlatform.system};
|
||||
defaultWrapper = "mesa";
|
||||
@@ -33,9 +37,9 @@
|
||||
programs.ghostty = {
|
||||
enable = true;
|
||||
enableZshIntegration = true;
|
||||
package = ghosttyPackage;
|
||||
package = ghosttyX11;
|
||||
settings = {
|
||||
command = "TERM=xterm-256color ${lib.getExe pkgs.zsh}";
|
||||
command = "TERM=xterm-256color ${lib.getExe inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.jsl-zsh}";
|
||||
font-size = 12;
|
||||
font-family = "Source Code Pro";
|
||||
theme = "Catppuccin Mocha";
|
||||
@@ -72,7 +76,7 @@
|
||||
# https://github.com/ghostty-org/ghostty/discussions/3763#discussioncomment-11699970
|
||||
xdg.desktopEntries."com.mitchellh.ghostty" =
|
||||
let
|
||||
ghosttyCmd = lib.getExe ghosttyPackage;
|
||||
ghosttyCmd = lib.getExe ghosttyX11;
|
||||
in
|
||||
{
|
||||
name = "Ghostty";
|
||||
@@ -100,4 +104,16 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
perSystem = { system, pkgs, lib, ... }: {
|
||||
packages.ghosttyGPUCheck = pkgs.writeShellApplication {
|
||||
name = "ghostty-gpu-check";
|
||||
runtimeInputs = with pkgs; [
|
||||
bash
|
||||
gnugrep
|
||||
procps # for pgrep, pidof, etc.
|
||||
];
|
||||
text = builtins.readFile ../../scripts/ghostty-gpu-check.sh;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+104
-79
@@ -6,12 +6,9 @@
|
||||
};
|
||||
};
|
||||
|
||||
perSystem = { system, pkgs, config, ... }: {
|
||||
packages.my-neovim = ((inputs.nvf.lib.neovimConfiguration {
|
||||
inherit pkgs;
|
||||
modules = [
|
||||
{
|
||||
# https://nvf.notashelf.dev/search.html
|
||||
perSystem = { system, pkgs, config, ... }:
|
||||
let
|
||||
commonNeovimModule = {
|
||||
config.vim = {
|
||||
options = {
|
||||
number = true;
|
||||
@@ -27,7 +24,6 @@
|
||||
|
||||
syntaxHighlighting = true;
|
||||
|
||||
# Enable custom theming options
|
||||
theme.enable = true;
|
||||
theme.name = "catppuccin";
|
||||
theme.style = "mocha";
|
||||
@@ -36,81 +32,63 @@
|
||||
# git.neogit.enable = true;
|
||||
|
||||
# https://github.com/akinsho/toggleterm.nvim
|
||||
terminal.toggleterm.enable = true;
|
||||
terminal.toggleterm.lazygit.enable = true;
|
||||
terminal.toggleterm.lazygit.direction = "float";
|
||||
terminal.toggleterm.lazygit.mappings.open = "<C-g>";
|
||||
|
||||
|
||||
utility.nix-develop.enable = true;
|
||||
|
||||
utility.oil-nvim.enable = true;
|
||||
utility.oil-nvim.gitStatus.enable = true;
|
||||
|
||||
filetree.neo-tree = {
|
||||
terminal.toggleterm = {
|
||||
enable = true;
|
||||
lazygit = {
|
||||
enable = true;
|
||||
package = pkgs.lazygit;
|
||||
direction = "float";
|
||||
mappings.open = "<C-g>";
|
||||
};
|
||||
};
|
||||
|
||||
# lazy = {
|
||||
# enable = true;
|
||||
# };
|
||||
languages = {
|
||||
enableTreesitter = true;
|
||||
enableFormat = true;
|
||||
|
||||
# globals = {
|
||||
# SimpylFold_docstring_preview = 1;
|
||||
# SimpylFold_fold_blank = 0;
|
||||
# };
|
||||
|
||||
# extraPlugins = with pkgs.vimPlugins; {
|
||||
# SimpylFold.package = SimpylFold;
|
||||
# };
|
||||
nix.enable = true;
|
||||
yaml.enable = true;
|
||||
toml.enable = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
telescopeModule = {
|
||||
config.vim = {
|
||||
telescope = {
|
||||
enable = true;
|
||||
extensions = [
|
||||
{
|
||||
name = "fzf";
|
||||
packages = [ pkgs.vimPlugins.telescope-fzf-native-nvim ];
|
||||
setup = {fzf = {fuzzy = true;};};
|
||||
setup = {
|
||||
fzf.fuzzy = true;
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# Enable Treesitter
|
||||
treesitter = {
|
||||
enable = true;
|
||||
grammars = with pkgs.vimPlugins.nvim-treesitter-parsers; [ python ];
|
||||
};
|
||||
|
||||
lsp.enable = true;
|
||||
|
||||
languages = {
|
||||
enableTreesitter = true;
|
||||
enableFormat = true;
|
||||
|
||||
markdown = {
|
||||
enable = true;
|
||||
extensions = {
|
||||
# render-markdown-nvim.enable = true;
|
||||
markview-nvim.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
bash.enable = true;
|
||||
css.enable = true;
|
||||
yaml.enable = true;
|
||||
toml.enable = true;
|
||||
|
||||
nix = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
python = {
|
||||
enable = true;
|
||||
dap.enable = true;
|
||||
format.type = [ "ruff" ];
|
||||
keymaps = [
|
||||
{
|
||||
desc = "Key Maps [Telescope]";
|
||||
key = "<leader>fkm";
|
||||
mode = "n";
|
||||
silent = false;
|
||||
action = "<cmd>:Telescope keymaps<CR>";
|
||||
}
|
||||
{
|
||||
desc = "Toggle Filesystem Tree [NeoTree]";
|
||||
key = "<C-b>";
|
||||
mode = [ "n" "v" "t" ];
|
||||
silent = false;
|
||||
action = "<cmd>:Neotree toggle filesystem left action=show<CR>";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
keymapsModule = {
|
||||
config.vim = {
|
||||
keymaps = [
|
||||
{
|
||||
desc = "Edit key mappings";
|
||||
@@ -126,20 +104,6 @@
|
||||
silent = false;
|
||||
action = "<cmd>:TermExec cmd='clear && nhms && exit' name='Nix Home Manager Switch' direction=float<CR>";
|
||||
}
|
||||
{
|
||||
desc = "Key Maps [Telescope]";
|
||||
key = "<leader>fkm";
|
||||
mode = "n";
|
||||
silent = false;
|
||||
action = "<cmd>:Telescope keymaps<CR>";
|
||||
}
|
||||
{
|
||||
desc = "Toggle Filesystem Tree [NeoTree]";
|
||||
key = "<C-b>";
|
||||
mode = [ "n" "v" "t" ];
|
||||
silent = false;
|
||||
action = "<cmd>:Neotree toggle filesystem left action=show<CR>";
|
||||
}
|
||||
{
|
||||
key = "<C-`>";
|
||||
mode = [ "n" "v" "t" ];
|
||||
@@ -148,11 +112,72 @@
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
in {
|
||||
packages.my-neovim = ((inputs.nvf.lib.neovimConfiguration {
|
||||
inherit pkgs;
|
||||
modules = [
|
||||
commonNeovimModule
|
||||
telescopeModule
|
||||
keymapsModule
|
||||
{
|
||||
# https://nvf.notashelf.dev/search.html
|
||||
config.vim = {
|
||||
utility.nix-develop.enable = true;
|
||||
|
||||
utility.oil-nvim.enable = true;
|
||||
utility.oil-nvim.gitStatus.enable = true;
|
||||
|
||||
filetree.neo-tree = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
# Enable Treesitter
|
||||
treesitter = {
|
||||
enable = true;
|
||||
grammars = with pkgs.vimPlugins.nvim-treesitter-parsers; [ python ];
|
||||
};
|
||||
|
||||
lsp.enable = true;
|
||||
|
||||
languages = {
|
||||
markdown = {
|
||||
enable = true;
|
||||
extensions = {
|
||||
# render-markdown-nvim.enable = true;
|
||||
markview-nvim.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
bash.enable = true;
|
||||
css.enable = true;
|
||||
yaml.enable = true;
|
||||
toml.enable = true;
|
||||
|
||||
python = {
|
||||
enable = true;
|
||||
dap.enable = true;
|
||||
format.type = [ "ruff" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
}).neovim).overrideAttrs (old: {
|
||||
pname = "my-neovim";
|
||||
version = "custom";
|
||||
version = "1.0.0";
|
||||
});
|
||||
|
||||
packages.neovim-min = ((inputs.nvf.lib.neovimConfiguration {
|
||||
inherit pkgs;
|
||||
modules = [
|
||||
commonNeovimModule
|
||||
telescopeModule
|
||||
keymapsModule
|
||||
];
|
||||
}).neovim).overrideAttrs (old: {
|
||||
pname = "neovim-min";
|
||||
version = "1.0.0";
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.modules.nixos.niri = { pkgs, lib, ... }: {
|
||||
programs.niri = {
|
||||
enable = true;
|
||||
package = self.packages.${pkgs.stdenv.hostPlatform.system}.myNiri;
|
||||
};
|
||||
};
|
||||
|
||||
perSystem = { pkgs, lib, self', ... }: {
|
||||
packages.myNiri = inputs.wrapper-modules.wrappers.niri.wrap {
|
||||
inherit pkgs;
|
||||
env.RUST_BACKTRACE = "full";
|
||||
settings = {
|
||||
spawn-at-startup = [
|
||||
"${lib.getExe self'.packages.myNoctalia}"
|
||||
];
|
||||
xwayland-satellite.path = lib.getExe pkgs.xwayland-satellite;
|
||||
input.keyboard.xkb.layout = "us,ua";
|
||||
layout.gaps = 5;
|
||||
binds = {
|
||||
"Mod+Return".spawn-sh = lib.getExe pkgs.ghostty;
|
||||
"Mod+Q".close-window = null;
|
||||
"Mod+S".spawn-sh = "${lib.getExe self'.packages.myNoctalia} ipc call launcher toggle";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{ self, inputs, ... }: {
|
||||
perSystem = { pkgs, ... }: {
|
||||
packages.myNoctalia = inputs.wrapper-modules.wrappers.noctalia-shell.wrap {
|
||||
inherit pkgs;
|
||||
# settings = (builtins.fromJSON (builtins.readFile ./noctalia.json)).settings;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,10 +1,24 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.modules.nixos.onepassword = { config, ... }: {
|
||||
programs._1password.enable = true;
|
||||
programs._1password-gui = {
|
||||
enable = true;
|
||||
# Certain features, including CLI integration and system authentication support,
|
||||
# require enabling PolKit integration on some desktop environments (e.g. Plasma).
|
||||
polkitPolicyOwners = [ "john" ];
|
||||
# TODO this should not be a hardcoded username
|
||||
};
|
||||
};
|
||||
|
||||
flake.modules.homeManager.onepassword = { config, ... }: {
|
||||
home.file.".config/1Password/ssh/agent.toml".text = ''
|
||||
# https://developer.1password.com/docs/ssh/agent/config
|
||||
[[ssh-keys]]
|
||||
vault = "Private"
|
||||
'';
|
||||
programs.ssh.matchBlocks."*".identityAgent = "${config.home.homeDirectory}/.1password/agent.sock";
|
||||
|
||||
programs.ssh.settings."*" = {
|
||||
IdentityAgent = "${config.home.homeDirectory}/.1password/agent.sock";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+54
-53
@@ -1,4 +1,4 @@
|
||||
{ inputs, ... }:
|
||||
{ self, inputs, ... }:
|
||||
let
|
||||
inputs' = inputs; # save a reference before it's shadowed
|
||||
in
|
||||
@@ -14,18 +14,10 @@ in
|
||||
imports = [ inputs.sops-nix.nixosModules.sops ];
|
||||
};
|
||||
|
||||
# Define the homeModules that are used by flake-parts
|
||||
# https://flake.parts/options/home-manager.html#opt-flake.modules.homeManager
|
||||
flake.modules.homeManager.mysops = { inputs, config, pkgs, lib, ... }:
|
||||
flake.modules.homeManager.mysops =
|
||||
{ config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config.mysops;
|
||||
sopsBin = lib.getExe pkgs.sops;
|
||||
sopsConfigPath = ../../.sops.yaml;
|
||||
sopsSecretsPath = ../../keys/secrets.yaml;
|
||||
|
||||
editScript = lib.optional (cfg.hostSecretFile != null) (pkgs.writeShellScriptBin "edit-secrets" ''
|
||||
${sopsBin} --config ${sopsConfigPath} ${cfg.hostSecretFile}
|
||||
'');
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
@@ -34,11 +26,6 @@ in
|
||||
];
|
||||
|
||||
options.mysops = {
|
||||
ageKeyFile = lib.mkOption {
|
||||
description = "Default location for the age key";
|
||||
type = lib.types.str;
|
||||
default = "${config.xdg.configHome}/sops/age/keys.txt";
|
||||
};
|
||||
hostSecretFile = lib.mkOption {
|
||||
description = "Path to the secrets file for this host. Used to create the edit-secrets script";
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
@@ -48,50 +35,64 @@ in
|
||||
|
||||
config =
|
||||
let
|
||||
echo = lib.getExe' pkgs.coreutils "echo";
|
||||
dirname = lib.getExe' pkgs.coreutils "dirname";
|
||||
mkdir = lib.getExe' pkgs.coreutils "mkdir";
|
||||
show-age-key = (pkgs.writeShellScriptBin "show-age-key" ''
|
||||
${lib.getExe' pkgs.age "age-keygen"} -y ${cfg.ageKeyFile}
|
||||
'');
|
||||
identityFile = config.ssh-new.keyFile;
|
||||
my-sops = (inputs.self.wrappers.mySops.apply {
|
||||
inherit pkgs;
|
||||
sshKey = identityFile;
|
||||
}).wrapper;
|
||||
in
|
||||
{
|
||||
home.packages = with pkgs; [
|
||||
eza
|
||||
age
|
||||
sops # This is necessary to make the sops binary available
|
||||
ssh-to-age
|
||||
(writeShellScriptBin "gen-age-key" ''
|
||||
set -eu
|
||||
|
||||
if [ ! -f "${config.ssh.identityFile}" ]; then
|
||||
${echo} "SSH identity file not found: ${config.ssh.identityFile}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -e "${cfg.ageKeyFile}" ]; then
|
||||
${echo} "Refusing to overwrite existing age key file: ${cfg.ageKeyFile}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
${mkdir} -p "$(${dirname} "${cfg.ageKeyFile}")"
|
||||
${lib.getExe pkgs.ssh-to-age} -i ${config.ssh.identityFile} -private-key > ${cfg.ageKeyFile}
|
||||
${echo} -n "Created ${cfg.ageKeyFile}: "
|
||||
${echo} $(${lib.getExe show-age-key})
|
||||
'')
|
||||
show-age-key
|
||||
(writeShellScriptBin "ls-secrets" "${lib.getExe pkgs.eza} -alT --follow-symlinks ~/.config/sops-nix/secrets")
|
||||
] ++ editScript;
|
||||
|
||||
home.shellAliases.sops = "${sopsBin} --config ${sopsConfigPath}";
|
||||
|
||||
# Option definitions for the sops home-manager module:
|
||||
# https://github.com/Mic92/sops-nix/blob/master/modules/home-manager/sops.nix
|
||||
sops = {
|
||||
defaultSopsFile = sopsSecretsPath;
|
||||
defaultSopsFile = ../../keys/secrets.yaml;
|
||||
defaultSopsFormat = "yaml";
|
||||
age.sshKeyPaths = [ "${config.ssh.identityFile}" ];
|
||||
age.sshKeyPaths = [ identityFile ];
|
||||
};
|
||||
|
||||
home.packages = with pkgs; [
|
||||
my-sops
|
||||
(inputs.wrappers.lib.wrapPackage {
|
||||
binName = "ls-secrets";
|
||||
inherit pkgs;
|
||||
package = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.my-eza;
|
||||
args = [
|
||||
"-T" "--follow-symlinks"
|
||||
"${config.xdg.configHome}/sops-nix/secrets"
|
||||
];
|
||||
})
|
||||
|
||||
]
|
||||
++ lib.optional (cfg.hostSecretFile != null) (inputs.wrappers.lib.wrapPackage {
|
||||
binName = "edit-secrets";
|
||||
inherit pkgs;
|
||||
package = my-sops;
|
||||
args = [ cfg.hostSecretFile ];
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
flake.wrappers.mySops = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
|
||||
options = {
|
||||
sshKey = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "String path to the SSH key to use for creating an AGE key at runtime";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# binName = "my-sops";
|
||||
package = config.pkgs.sops;
|
||||
extraPackages = with config.pkgs; [ coreutils ssh-to-age ];
|
||||
preHook = ''
|
||||
AGE_KEY=$(umask 077; mktemp)
|
||||
ssh-to-age -private-key -i ${config.sshKey} > "$AGE_KEY"
|
||||
'';
|
||||
flags."--config" = "${../../.sops.yaml}";
|
||||
postHook = ''
|
||||
rm "$AGE_KEY"
|
||||
echo "Removed $AGE_KEY"
|
||||
'';
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
{ self, inputs, ... }: {
|
||||
perSystem = { system, pkgs, lib, ... }: {
|
||||
packages.starship = (inputs.wrappers.wrapperModules.starship.apply {
|
||||
inherit pkgs;
|
||||
settings = lib.recursiveUpdate (lib.importTOML (pkgs.fetchurl {
|
||||
url = https://starship.rs/presets/toml/catppuccin-powerline.toml;
|
||||
sha256 = "1jzbbzm3nwdlldq43aj3csp0ps23fsa6x2225z85l0s9qbj4cdy2";
|
||||
})) {
|
||||
palette = "catppuccin_mocha";
|
||||
add_newline = true;
|
||||
line_break.disabled = false;
|
||||
git_status.diverged = "⇕⇡\${ahead_count}⇣\${behind_count}";
|
||||
cmd_duration.format = " $duration";
|
||||
cmd_duration.show_notifications = false;
|
||||
hostname = {
|
||||
disabled = false;
|
||||
ssh_symbol = "🌐";
|
||||
format = "[$ssh_symbol$hostname]($style)";
|
||||
style = "bg:red fg:crust";
|
||||
};
|
||||
os.symbols.NixOS = " ";
|
||||
format = lib.replaceStrings ["\n"] [""] ''
|
||||
[](red)
|
||||
$os
|
||||
$username
|
||||
$hostname
|
||||
[](bg:peach fg:red)
|
||||
$directory
|
||||
[](bg:yellow fg:peach)
|
||||
$git_branch
|
||||
$git_status
|
||||
[](fg:yellow bg:green)
|
||||
$c
|
||||
$rust
|
||||
$golang
|
||||
$nodejs
|
||||
$php
|
||||
$java
|
||||
$kotlin
|
||||
$haskell
|
||||
$python
|
||||
[](fg:green bg:sapphire)
|
||||
$conda
|
||||
[](fg:sapphire bg:lavender)
|
||||
$time
|
||||
[ ](fg:lavender)
|
||||
$cmd_duration
|
||||
$line_break
|
||||
$character
|
||||
'';
|
||||
};
|
||||
}).wrapper;
|
||||
|
||||
packages.starship-ascii = (inputs.wrappers.wrapperModules.starship.apply {
|
||||
inherit pkgs;
|
||||
settings = {
|
||||
add_newline = false;
|
||||
|
||||
format = "$username$hostname$directory$git_branch$git_status$cmd_duration$line_break$character";
|
||||
|
||||
username = {
|
||||
show_always = true;
|
||||
format = "[$user]($style)";
|
||||
style_user = "bold blue";
|
||||
style_root = "bold red";
|
||||
};
|
||||
|
||||
hostname = {
|
||||
disabled = false;
|
||||
ssh_only = false;
|
||||
format = "[@$hostname]($style) ";
|
||||
style = "bold blue";
|
||||
};
|
||||
|
||||
directory = {
|
||||
truncation_length = 3;
|
||||
truncate_to_repo = true;
|
||||
format = "[ in $path]($style) ";
|
||||
style = "bold cyan";
|
||||
};
|
||||
|
||||
git_branch = {
|
||||
symbol = "on ";
|
||||
format = "[$symbol$branch]($style) ";
|
||||
style = "bold yellow";
|
||||
};
|
||||
|
||||
git_status = {
|
||||
format = "([$all_status$ahead_behind]($style) )";
|
||||
style = "bold red";
|
||||
ahead = "ahead:$count ";
|
||||
behind = "behind:$count ";
|
||||
diverged = "diverged:$ahead_count/$behind_count ";
|
||||
up_to_date = "";
|
||||
};
|
||||
|
||||
cmd_duration = {
|
||||
min_time = 2000;
|
||||
show_notifications = false;
|
||||
format = "took [$duration]($style) ";
|
||||
style = "bold green";
|
||||
};
|
||||
|
||||
line_break.disabled = false;
|
||||
|
||||
character = {
|
||||
success_symbol = "[\\$](bold green) ";
|
||||
error_symbol = "[\\$](bold red) ";
|
||||
vicmd_symbol = "[\\$](bold yellow) ";
|
||||
};
|
||||
};
|
||||
}).wrapper;
|
||||
};
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
{ self, inputs, ... }: {
|
||||
#
|
||||
# Home Manager Module
|
||||
#
|
||||
flake.modules.homeManager.step-ssh-user = { config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config.step-ssh-user;
|
||||
firstPrincipal = lib.head cfg.principals;
|
||||
principalArgs = lib.concatMapStringsSep " "
|
||||
(principal: "--principal \"${principal}\"") cfg.principals;
|
||||
in
|
||||
{
|
||||
options.step-ssh-user = {
|
||||
enable = lib.mkEnableOption "opionated step client config for SSH certs";
|
||||
provisioner = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "admin";
|
||||
};
|
||||
principals = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
};
|
||||
};
|
||||
config = lib.mkIf cfg.enable {
|
||||
sops.secrets."janus/admin_jwk".mode = "0400";
|
||||
home.packages = with pkgs; [
|
||||
(writeShellScriptBin "sign-ssh-cert" ''
|
||||
${lib.getExe pkgs.step-cli} ssh certificate \
|
||||
--sign \
|
||||
${principalArgs} \
|
||||
--provisioner "${cfg.provisioner}" \
|
||||
--provisioner-password-file "${config.sops.secrets."janus/admin_jwk".path}" \
|
||||
"${firstPrincipal}" "${config.ssh.identityFile}.pub"
|
||||
'')
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -2,39 +2,13 @@
|
||||
flake.modules.nixos.wireguard = { config, pkgs, lib, ... }:
|
||||
let
|
||||
wgInterface = "platform";
|
||||
systemctl = lib.getExe' pkgs.systemd "systemctl";
|
||||
journalctl = lib.getExe' pkgs.systemd "journalctl";
|
||||
|
||||
mkConnect = interface:
|
||||
let
|
||||
serviceName = "wg-quick-${interface}";
|
||||
service = "${serviceName}.service";
|
||||
in
|
||||
pkgs.writeShellScriptBin "wg-connect-${interface}" ''
|
||||
${systemctl} start ${service}
|
||||
start_time=$(${systemctl} show -p ActiveEnterTimestamp ${serviceName} | cut -d= -f2)
|
||||
${journalctl} -u ${service} --since "$start_time" --no-pager
|
||||
'';
|
||||
mkDisconnect = interface:
|
||||
let
|
||||
serviceName = "wg-quick-${interface}";
|
||||
service = "${serviceName}.service";
|
||||
in
|
||||
pkgs.writeShellScriptBin "wg-disconnect-${interface}" ''
|
||||
STOPTIME=$(${lib.getExe' pkgs.coreutils "date"} '+%Y-%m-%d %H:%M:%S')
|
||||
${systemctl} stop ${service}
|
||||
start_time=$(${systemctl} show -p ActiveEnterTimestamp ${serviceName} | cut -d= -f2)
|
||||
${journalctl} -u ${service} --since "$STOPTIME" --no-pager
|
||||
'';
|
||||
in
|
||||
{
|
||||
imports = [ inputs.sops-nix.nixosModules.sops ];
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
wireguard-tools
|
||||
wg-netmanager
|
||||
(mkConnect "platform")
|
||||
(mkDisconnect "platform")
|
||||
wireguard-tools # https://github.com/WireGuard/wireguard-tools
|
||||
# wg-netmanager # https://github.com/gin66/wg_netmanager
|
||||
];
|
||||
|
||||
sops.secrets.wireguard_private_key = { };
|
||||
@@ -56,4 +30,39 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
perSystem = { system, pkgs, lib, ... }:
|
||||
let
|
||||
connect = pkgs.writeShellApplication {
|
||||
name = "wg-platform-connect";
|
||||
text = ''
|
||||
sudo systemctl start wg-quick-platform.service
|
||||
START_TIME=$(sudo systemctl show -p ActiveEnterTimestamp wg-quick-platform | cut -d= -f2)
|
||||
journalctl -u wg-quick-platform --since "$START_TIME" --no-pager
|
||||
'';
|
||||
};
|
||||
|
||||
disconnect = pkgs.writeShellApplication {
|
||||
name = "wg-platform-disconnect";
|
||||
text = ''
|
||||
STOP_TIME=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
systemctl stop wg-quick-platform.service
|
||||
journalctl -u wg-quick-platform.service --since "$STOP_TIME" --no-pager
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
packages.wg-platform = inputs.wrappers.lib.wrapPackage {
|
||||
inherit pkgs;
|
||||
runtimeInputs = with pkgs; [ coreutils systemd wireguard-tools ];
|
||||
package = pkgs.symlinkJoin {
|
||||
name = "wg-platform";
|
||||
meta.mainProgram = "wg-platform-connect";
|
||||
paths = [
|
||||
connect
|
||||
disconnect
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{ self, inputs, ... }: {
|
||||
# https://github.com/Lassulus/wrappers/blob/main/modules/yazi/module.nix
|
||||
perSystem = { system, pkgs, lib, ... }: {
|
||||
packages.yazi = (inputs.wrappers.wrapperModules.yazi.apply {
|
||||
inherit pkgs;
|
||||
settings = {
|
||||
mgr.ratio = [ 1 4 3 ];
|
||||
};
|
||||
}).wrapper;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{ self, ... }:
|
||||
let
|
||||
packageName = "zed-editor";
|
||||
vulkanIcd = "/usr/share/vulkan/icd.d/nvidia_icd.json";
|
||||
eglVendor = "/usr/share/glvnd/egl_vendor.d/10_nvidia.json";
|
||||
in
|
||||
{
|
||||
perSystem = { pkgs, ... }: {
|
||||
packages."${packageName}" = pkgs.symlinkJoin {
|
||||
name = "zed-editor-host-gpu";
|
||||
paths = [ pkgs.zed-editor ];
|
||||
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||
meta = pkgs.zed-editor.meta // {
|
||||
mainProgram = "zeditor";
|
||||
};
|
||||
postBuild = ''
|
||||
for exe in $out/bin/*; do
|
||||
wrapProgram "$exe" \
|
||||
--unset WAYLAND_DISPLAY \
|
||||
--unset GDK_BACKEND \
|
||||
--set VK_DRIVER_FILES ${vulkanIcd} \
|
||||
--set VK_ICD_FILENAMES ${vulkanIcd} \
|
||||
--set __EGL_VENDOR_LIBRARY_FILENAMES ${eglVendor} \
|
||||
--set __GLX_VENDOR_LIBRARY_NAME nvidia
|
||||
done
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
flake.modules.homeManager.zed-editor = { pkgs, ... }: {
|
||||
home.packages = [ pkgs.vulkan-tools ];
|
||||
|
||||
programs.zed-editor = {
|
||||
enable = true;
|
||||
package = self.packages.${pkgs.stdenv.hostPlatform.system}."${packageName}";
|
||||
installRemoteServer = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
+113
-163
@@ -1,188 +1,138 @@
|
||||
{ self, inputs, ... }:
|
||||
let
|
||||
username = "john";
|
||||
historySize = 10000;
|
||||
homeEndKeyBindings = ''
|
||||
# Normalize common Home/End escape sequences across terminal emulators.
|
||||
bindkey "^[[H" beginning-of-line
|
||||
bindkey "^[[F" end-of-line
|
||||
bindkey "^[[1~" beginning-of-line
|
||||
bindkey "^[[4~" end-of-line
|
||||
bindkey "^[[7~" beginning-of-line
|
||||
bindkey "^[[8~" end-of-line
|
||||
bindkey "^[OH" beginning-of-line
|
||||
bindkey "^[OF" end-of-line
|
||||
bindkey "^[[3~" delete-char
|
||||
|
||||
# Normalize common Ctrl+Arrow sequences for word-wise movement.
|
||||
bindkey "^[[1;5D" backward-word
|
||||
bindkey "^[[1;5C" forward-word
|
||||
bindkey "^[[5D" backward-word
|
||||
bindkey "^[[5C" forward-word
|
||||
'';
|
||||
in
|
||||
{
|
||||
flake.modules = {
|
||||
nixos.zsh = { pkgs, ... }: {
|
||||
users.users."${username}".shell = pkgs.zsh;
|
||||
nixos.zsh = { pkgs, ... }:
|
||||
let
|
||||
selfPackages = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||
in
|
||||
{
|
||||
users.users."${username}".shell = selfPackages.jsl-zsh;
|
||||
programs.zsh.enable = true;
|
||||
# Already being imported by the john.nix module
|
||||
# home-manager.sharedModules = [
|
||||
# inputs.self.modules.homeManager.zsh
|
||||
# ];
|
||||
};
|
||||
|
||||
homeManager.zsh = { pkgs, config, ... }: {
|
||||
homeManager.zsh = { pkgs, config, ... }:
|
||||
let
|
||||
selfPackages = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||
in
|
||||
{
|
||||
programs.zsh = {
|
||||
enable = true;
|
||||
package = pkgs.zsh;
|
||||
enableCompletion = true;
|
||||
autosuggestion.enable = true;
|
||||
# syntaxHighlighting.enable = true;
|
||||
initContent = "HOST=$(hostname -s)";
|
||||
package = selfPackages.jsl-zsh;
|
||||
dotDir = "${config.xdg.configHome}/zsh";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
perSystem = { config, self', pkgs, lib, ... }:
|
||||
let
|
||||
ignorePatterns = [
|
||||
"ls" "eza" "history" "clear"
|
||||
];
|
||||
integrationPackages = with pkgs; [
|
||||
fzf
|
||||
zoxide
|
||||
];
|
||||
extraToolPackages = with pkgs; [
|
||||
lazygit
|
||||
lazydocker
|
||||
devenv
|
||||
self'.packages.shell-tools
|
||||
self'.packages.neovim-min
|
||||
];
|
||||
aliasStr = lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (k: v: "alias -- ${lib.escapeShellArg k}=${lib.escapeShellArg v}") {
|
||||
ls = "eza";
|
||||
ll = "eza -l";
|
||||
la = "eza -a";
|
||||
lt = "eza --tree";
|
||||
lla = "eza -la";
|
||||
ds = "gdu -i /snap /";
|
||||
ld = "lazydocker";
|
||||
});
|
||||
|
||||
mkJslZsh = { binName, starshipPackage }:
|
||||
let
|
||||
loginBootstrapPath = lib.makeBinPath (integrationPackages ++ [ starshipPackage ] ++ extraToolPackages);
|
||||
in
|
||||
(inputs.wrappers.wrapperModules.zsh.apply {
|
||||
inherit pkgs binName;
|
||||
env = {
|
||||
LANG = "en_US.UTF-8";
|
||||
COLORTERM = "truecolor";
|
||||
DEVENV_SHELL_TYPE = "zsh";
|
||||
};
|
||||
settings = {
|
||||
completion = {
|
||||
enable = true;
|
||||
extraCompletions = true;
|
||||
caseInsensitive = true;
|
||||
fuzzySearch = true;
|
||||
};
|
||||
autoSuggestions = {
|
||||
enable = true;
|
||||
strategy = [ "history" "completion" ];
|
||||
};
|
||||
history = {
|
||||
append = true;
|
||||
ignoreAllDups = true;
|
||||
ignorePatterns = [
|
||||
"history"
|
||||
"ls"
|
||||
"eza"
|
||||
"clear"
|
||||
];
|
||||
save = 1000;
|
||||
size = 1000;
|
||||
expanded = true;
|
||||
share = true;
|
||||
};
|
||||
oh-my-zsh = {
|
||||
enable = true;
|
||||
# theme = "risto";
|
||||
theme = "agnoster";
|
||||
plugins = [
|
||||
"sudo"
|
||||
"dotenv"
|
||||
"git"
|
||||
"ssh"
|
||||
"ssh-agent"
|
||||
];
|
||||
};
|
||||
};
|
||||
ignoreAllDups = true;
|
||||
ignoreSpace = true;
|
||||
};
|
||||
};
|
||||
extraRC = ''
|
||||
${homeEndKeyBindings}
|
||||
|
||||
perSystem = { system, pkgs, ... }: {
|
||||
packages.jsl-zsh = inputs.wrapper-modules.wrappers.zsh.wrap {
|
||||
inherit pkgs;
|
||||
extraPackages = with pkgs; [
|
||||
inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.neovim-min
|
||||
btop
|
||||
coreutils
|
||||
curl
|
||||
wget
|
||||
yazi
|
||||
zsh
|
||||
];
|
||||
};
|
||||
# Login shells may reset PATH before integrations run.
|
||||
export PATH=${lib.escapeShellArg loginBootstrapPath}:$PATH
|
||||
|
||||
packages.neovim-min = ((inputs.nvf.lib.neovimConfiguration {
|
||||
inherit pkgs;
|
||||
modules = [
|
||||
source <(fzf --zsh)
|
||||
eval "$(zoxide init zsh)"
|
||||
eval "$(starship init zsh)"
|
||||
|
||||
HISTFILE=$HOME/.config/zsh/.zsh_history
|
||||
SAVEHIST=${toString historySize}
|
||||
HISTORY_IGNORE=${lib.escapeShellArg "(${lib.concatStringsSep "|" ignorePatterns})"}
|
||||
|
||||
HOSTNAME=$(hostname -s)
|
||||
${aliasStr}
|
||||
|
||||
eval "$(devenv hook zsh)"
|
||||
'';
|
||||
extraPackages = extraToolPackages;
|
||||
}).wrapper;
|
||||
in
|
||||
{
|
||||
# https://nvf.notashelf.dev/search.html
|
||||
config.vim = {
|
||||
options = {
|
||||
number = true;
|
||||
relativenumber = true;
|
||||
expandtab = true;
|
||||
shiftwidth = 4;
|
||||
tabstop = 4;
|
||||
softtabstop = 4;
|
||||
|
||||
wrap = true;
|
||||
linebreak = true;
|
||||
packages.jsl-zsh = mkJslZsh {
|
||||
binName = "jsl-zsh";
|
||||
starshipPackage = self'.packages.starship;
|
||||
};
|
||||
|
||||
syntaxHighlighting = true;
|
||||
|
||||
# Enable custom theming options
|
||||
theme.enable = true;
|
||||
theme.name = "catppuccin";
|
||||
theme.style = "mocha";
|
||||
|
||||
git.enable = true;
|
||||
# git.neogit.enable = true;
|
||||
extraPlugins = with pkgs.vimPlugins; {
|
||||
icons = {
|
||||
package = nvim-web-devicons;
|
||||
packages.jsl-zsh-tty = mkJslZsh {
|
||||
binName = "jsl-zsh-tty";
|
||||
starshipPackage = self'.packages.starship-ascii;
|
||||
};
|
||||
octo = {
|
||||
package = octo-nvim;
|
||||
setup = "require('octo').setup {}";
|
||||
after = ["telescope" "icons"];
|
||||
};
|
||||
};
|
||||
|
||||
# https://github.com/akinsho/toggleterm.nvim
|
||||
terminal.toggleterm.enable = true;
|
||||
terminal.toggleterm.lazygit.enable = true;
|
||||
terminal.toggleterm.lazygit.direction = "float";
|
||||
terminal.toggleterm.lazygit.mappings.open = "<C-g>";
|
||||
|
||||
utility.nix-develop.enable = true;
|
||||
|
||||
filetree.neo-tree.enable = true;
|
||||
|
||||
telescope = {
|
||||
enable = true;
|
||||
extensions = [
|
||||
{
|
||||
name = "fzf";
|
||||
packages = [pkgs.vimPlugins.telescope-fzf-native-nvim];
|
||||
setup = {fzf = {fuzzy = true;};};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
languages = {
|
||||
enableTreesitter = true;
|
||||
enableFormat = true;
|
||||
|
||||
markdown = {
|
||||
enable = true;
|
||||
extensions = {
|
||||
markview-nvim.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
bash.enable = true;
|
||||
yaml.enable = true;
|
||||
toml.enable = true;
|
||||
nix.enable = true;
|
||||
};
|
||||
|
||||
keymaps = [
|
||||
{
|
||||
desc = "Edit key mappings";
|
||||
key = "<leader>ekm";
|
||||
mode = [ "n" ];
|
||||
silent = false;
|
||||
action = "<cmd>:edit +/keymaps /home/john/.config/home-manager/jsl-dendritic/modules/programs/neovim.nix<CR>";
|
||||
}
|
||||
{
|
||||
desc = "Home Manager Switch";
|
||||
key = "<leader>nhms";
|
||||
mode = [ "n" ];
|
||||
silent = false;
|
||||
action = "<cmd>:TermExec cmd='clear && nhms && exit' name='Nix Home Manager Switch' direction=float<CR>";
|
||||
}
|
||||
{
|
||||
desc = "Key Maps [Telescope]";
|
||||
key = "<leader>fkm";
|
||||
mode = "n";
|
||||
silent = false;
|
||||
action = "<cmd>:Telescope keymaps<CR>";
|
||||
}
|
||||
{
|
||||
desc = "Toggle Filesystem Tree [NeoTree]";
|
||||
key = "<C-b>";
|
||||
mode = [ "n" "v" "t" ];
|
||||
silent = false;
|
||||
action = "<cmd>:Neotree toggle filesystem left action=show<CR>";
|
||||
}
|
||||
{
|
||||
key = "<C-`>";
|
||||
mode = ["n" "v" "t"];
|
||||
silent = false;
|
||||
action = "<cmd>:ToggleTerm<CR>";
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
];
|
||||
}).neovim).overrideAttrs (old: {
|
||||
pname = "neovim-min";
|
||||
version = "custom";
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
{ inputs, ... }:
|
||||
let
|
||||
userName = "john";
|
||||
sshHostCAPubKey = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNug18oLH0vZxnibXJzMJvTWFPZTnSlhCDDVi+rHhgnIum6ZXQ4SF+VHOOAM5BbzZmMKitNJ5lcrGP15Eur7DzQ=";
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.ssh = { config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config.ssh;
|
||||
configDir = "/etc/ssh";
|
||||
in
|
||||
{
|
||||
options.ssh = {
|
||||
hostKey = lib.mkOption {
|
||||
description = "String path to the host private key file";
|
||||
type = lib.types.str;
|
||||
default = "ssh_host_ed25519_key";
|
||||
};
|
||||
certificates = {
|
||||
enable = lib.mkEnableOption "Enable SSH host certificates";
|
||||
userCA = lib.mkOption {
|
||||
description = "Content for the SSH user CA file (public key)";
|
||||
type = lib.types.path;
|
||||
default = ../hosts/janus/ssh_user_ca.pub;
|
||||
};
|
||||
userCAFile = lib.mkOption {
|
||||
description = "String path to the SSh user CA";
|
||||
type = lib.types.str;
|
||||
default = "ssh_user_ca.pub";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
# require public key authentication for better security
|
||||
settings = lib.mkMerge [
|
||||
{
|
||||
PasswordAuthentication = false;
|
||||
KbdInteractiveAuthentication = false;
|
||||
HostKey = "${configDir}/${cfg.hostKey}";
|
||||
}
|
||||
(lib.mkIf cfg.certificates.enable {
|
||||
TrustedUserCAKeys = "${configDir}/${cfg.certificates.userCAFile}";
|
||||
HostCertificate = "${configDir}/${cfg.hostKey}-cert.pub";
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
environment.etc."ssh/${cfg.certificates.userCAFile}" = lib.mkIf cfg.certificates.enable {
|
||||
source = cfg.certificates.userCA;
|
||||
};
|
||||
|
||||
programs.ssh.knownHosts = lib.mkIf cfg.certificates.enable {
|
||||
"192.168.1.*" = {
|
||||
certAuthority = true;
|
||||
publicKey = sshHostCAPubKey;
|
||||
};
|
||||
"*.john-stream.com" = {
|
||||
certAuthority = true;
|
||||
publicKey = sshHostCAPubKey;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
flake.modules.homeManager.ssh = { config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config.ssh;
|
||||
configDir = "${config.home.homeDirectory}/.ssh";
|
||||
identityFile = cfg.identityFile;
|
||||
publicKeyFile = "${identityFile}.pub";
|
||||
certificateFile = "${identityFile}-cert.pub";
|
||||
in
|
||||
{
|
||||
options.ssh = with lib; {
|
||||
identityFile = mkOption {
|
||||
# Intentionally not using a path type here because that will end up with the private key getting copied into the store
|
||||
type = types.str;
|
||||
default = "${config.home.homeDirectory}/.ssh/id_ed25519";
|
||||
description = "Path to the SSH identity file.";
|
||||
};
|
||||
|
||||
certificates = {
|
||||
enable = mkEnableOption "Enable SSH client certificates";
|
||||
};
|
||||
|
||||
knownHostsFile = mkOption {
|
||||
type = types.str;
|
||||
default = "${configDir}/known_hosts";
|
||||
};
|
||||
|
||||
knownHosts = mkOption {
|
||||
description = "";
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
|
||||
matchSets = {
|
||||
appdaemon = mkEnableOption "Enable AppDaemon SSH targets";
|
||||
certs = mkEnableOption "Enable Janus and Soteria SSH targets";
|
||||
homelab = mkEnableOption "Enable various Homelab targets";
|
||||
dev = mkEnableOption "Enable development targets";
|
||||
};
|
||||
};
|
||||
|
||||
# All this stuff has to be wrapped in a config attribute because of the presence of the options here?
|
||||
config = let
|
||||
provisionerPasswordPath = config.sops.secrets."janus/admin_jwk".path;
|
||||
in {
|
||||
home.file.".ssh/known_hosts" = {
|
||||
text = lib.concatStringsSep "\n" (
|
||||
cfg.knownHosts ++ lib.optionals cfg.certificates.enable [
|
||||
"@cert-authority 192.168.1.* ${sshHostCAPubKey}"
|
||||
"@cert-authority *.john-stream.com ${sshHostCAPubKey}"
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
programs.ssh = {
|
||||
enable = true;
|
||||
enableDefaultConfig = false;
|
||||
extraConfig = ''
|
||||
SetEnv TERM="xterm-256color"
|
||||
'';
|
||||
|
||||
matchBlocks = lib.mkMerge [
|
||||
{
|
||||
"john-pc-ubuntu" = {
|
||||
hostname = "192.168.1.85";
|
||||
};
|
||||
|
||||
"*" = lib.mkMerge [
|
||||
{
|
||||
user = "john";
|
||||
|
||||
compression = false;
|
||||
serverAliveInterval = 0;
|
||||
serverAliveCountMax = 3;
|
||||
|
||||
identitiesOnly = true;
|
||||
inherit identityFile;
|
||||
|
||||
hashKnownHosts = false;
|
||||
userKnownHostsFile = cfg.knownHostsFile;
|
||||
|
||||
addKeysToAgent = "yes";
|
||||
forwardAgent = false;
|
||||
}
|
||||
(lib.mkIf cfg.certificates.enable { inherit certificateFile; })
|
||||
];
|
||||
}
|
||||
(lib.mkIf cfg.matchSets.appdaemon {
|
||||
"appdaemon" = {
|
||||
hostname = "192.168.1.242";
|
||||
user = "appdaemon";
|
||||
};
|
||||
"ad-nix" = {
|
||||
hostname = "192.168.1.201";
|
||||
user = "appdaemon";
|
||||
};
|
||||
})
|
||||
(lib.mkIf cfg.matchSets.certs {
|
||||
"janus" = {
|
||||
hostname = "janus.john-stream.com";
|
||||
user = "root";
|
||||
};
|
||||
"soteria" = {
|
||||
hostname = "soteria.john-stream.com";
|
||||
user = "john";
|
||||
};
|
||||
})
|
||||
(lib.mkIf cfg.matchSets.homelab {
|
||||
"docs" = {
|
||||
hostname = "192.168.1.110";
|
||||
user = "root";
|
||||
};
|
||||
"gitea" = {
|
||||
hostname = "192.168.1.104";
|
||||
user = "john";
|
||||
};
|
||||
"hermes" = {
|
||||
hostname = "192.168.1.150";
|
||||
user = "root";
|
||||
};
|
||||
"panoptes" = {
|
||||
hostname = "192.168.1.107";
|
||||
user = "panoptes";
|
||||
};
|
||||
})
|
||||
(lib.mkIf cfg.matchSets.dev {
|
||||
"test-nix" = {
|
||||
hostname = "fded:fb16:653e:25da:be24:11ff:fea0:753f";
|
||||
user = "john";
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
{
|
||||
"root": "/etc/step-ca/certs/root_ca.crt",
|
||||
"federatedRoots": null,
|
||||
"crt": "/etc/step-ca/certs/intermediate_ca.crt",
|
||||
"key": "/etc/step-ca/secrets/intermediate_ca_key",
|
||||
"address": ":443",
|
||||
"insecureAddress": "",
|
||||
"dnsNames": [
|
||||
"janus.john-stream.com",
|
||||
"192.168.1.113"
|
||||
],
|
||||
"ssh": {
|
||||
"hostKey": "/etc/step-ca/secrets/ssh_host_ca_key",
|
||||
"userKey": "/etc/step-ca/secrets/ssh_user_ca_key"
|
||||
},
|
||||
"logger": {
|
||||
"format": "text"
|
||||
},
|
||||
"db": {
|
||||
"type": "badgerv2",
|
||||
"dataSource": "/var/lib/step-ca/db",
|
||||
"badgerFileLoadingMode": ""
|
||||
},
|
||||
"authority": {
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "ACME",
|
||||
"name": "acme"
|
||||
},
|
||||
{
|
||||
"type": "SSHPOP",
|
||||
"name": "sshpop",
|
||||
"claims": {
|
||||
"enableSSHCA": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "JWK",
|
||||
"name": "admin",
|
||||
"key": {
|
||||
"use": "sig",
|
||||
"kty": "EC",
|
||||
"kid": "xoxgOJFbveSLIL2gm1Yu5ZiRb9v8Jxe44F56i3v-Nf8",
|
||||
"crv": "P-256",
|
||||
"alg": "ES256",
|
||||
"x": "zFO8hPx_eH0Iyz7UJI-w8ODMusEKCZ28M76sGWmWYxA",
|
||||
"y": "XIWLLyKDzqxV9UH-2KeAkKPDrgLoPrxxW9-PzkXggME"
|
||||
},
|
||||
"encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjYwMDAwMCwicDJzIjoiUVJnTnJVTF9KcmxJYkJMVTlGNVRPZyJ9.DMu7xBNCq5pr-_--YTxNr5Hrcqy6ZmSVHsWurfVXL7Hk0Q3vyYRxiw.h-CnFiYc-DhxThI3.plx3_Qa_0kU-2TwnqFNfAfGnCpfQ2e0iiCMLruNHbLMnHeXQ1BysHBqps45_02zZXIRdHoDgYGtXRSfcdUYYoS0pLoPzC6m301ZFNSAFdRVlSZ3Q6VmWdixPXXnEB4EgSKTT_wxR33L8t9OpFzD85KfY-b_Un1l99ufjCnfg-EYkcICTn_G4-8bcW3eFIvJ6setzu-l0jHMhLQdIweqncn9on9xBXBD-ANhZfP95P2BJt-APqCi8eqiAvn_vClovdg0PxzRwOVDvWREz66FDw-HTU7xDtGO9hACopT5tfZOXDoykgZw1mJsq9NEq9ZzvKG2hvyk1UXtExxrNtFo.5q1OfGU4Amo4Si-vpeI42g",
|
||||
"claims": {
|
||||
"enableSSHCA": true,
|
||||
"disableRenewal": false,
|
||||
"allowRenewalAfterExpiry": false,
|
||||
"disableSmallstepExtensions": false
|
||||
},
|
||||
"options": {
|
||||
"x509": {},
|
||||
"ssh": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"template": {},
|
||||
"backdate": "1m0s"
|
||||
},
|
||||
"tls": {
|
||||
"cipherSuites": [
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
|
||||
],
|
||||
"minVersion": 1.2,
|
||||
"maxVersion": 1.3,
|
||||
"renegotiation": false
|
||||
},
|
||||
"templates": {
|
||||
"ssh": {
|
||||
"user": [
|
||||
{
|
||||
"name": "config.tpl",
|
||||
"type": "snippet",
|
||||
"template": "templates/ssh/config.tpl",
|
||||
"path": "~/.ssh/config",
|
||||
"comment": "#"
|
||||
},
|
||||
{
|
||||
"name": "step_includes.tpl",
|
||||
"type": "prepend-line",
|
||||
"template": "templates/ssh/step_includes.tpl",
|
||||
"path": "${STEPPATH}/ssh/includes",
|
||||
"comment": "#"
|
||||
},
|
||||
{
|
||||
"name": "step_config.tpl",
|
||||
"type": "file",
|
||||
"template": "templates/ssh/step_config.tpl",
|
||||
"path": "ssh/config",
|
||||
"comment": "#"
|
||||
},
|
||||
{
|
||||
"name": "known_hosts.tpl",
|
||||
"type": "file",
|
||||
"template": "templates/ssh/known_hosts.tpl",
|
||||
"path": "ssh/known_hosts",
|
||||
"comment": "#"
|
||||
}
|
||||
],
|
||||
"host": [
|
||||
{
|
||||
"name": "sshd_config.tpl",
|
||||
"type": "snippet",
|
||||
"template": "templates/ssh/sshd_config.tpl",
|
||||
"path": "/etc/ssh/sshd_config",
|
||||
"comment": "#",
|
||||
"requires": [
|
||||
"Certificate",
|
||||
"Key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ca.tpl",
|
||||
"type": "snippet",
|
||||
"template": "templates/ssh/ca.tpl",
|
||||
"path": "/etc/ssh/ca.pub",
|
||||
"comment": "#"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"commonName": "Step Online CA"
|
||||
}
|
||||
@@ -1,94 +1,91 @@
|
||||
{ inputs, ... }: {
|
||||
flake.modules.nixos.step-ssh-host = { config, pkgs, lib, ... }:
|
||||
flake.modules.nixos.ssh-certs = { config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config.step-ssh-host;
|
||||
provisionerPasswordPath = config.sops.secrets."janus/admin_jwk".path;
|
||||
cfg = config.ssh-certs;
|
||||
wrappers = inputs.self.wrappers;
|
||||
sshKeyPath = "/etc/ssh/ssh_host_ed25519_key";
|
||||
sshCertPath = "${sshKeyPath}-cert.pub";
|
||||
|
||||
sshHostCertSign = (wrappers.signHostWrapper.apply {
|
||||
inherit pkgs;
|
||||
inherit (cfg) provisioner extraPrincipals;
|
||||
provisionerPasswordFile = config.sops.secrets."janus/admin_jwk".path;
|
||||
}).wrapper;
|
||||
sshHostCertRenew = (wrappers.renewHostWrapper.apply {
|
||||
inherit pkgs;
|
||||
sshHostKeyFile = sshKeyPath;
|
||||
overwrite = true;
|
||||
}).wrapper;
|
||||
sshHostCertCheck = (wrappers.hostCheckWrapper.apply {
|
||||
inherit pkgs;
|
||||
certPath = sshCertPath;
|
||||
}).wrapper;
|
||||
sshHostRenewalCheck = (wrappers.renewalCheck.apply {
|
||||
inherit pkgs;
|
||||
certPath = sshCertPath;
|
||||
expires-in = "4h";
|
||||
}).wrapper;
|
||||
in
|
||||
{
|
||||
# NixOS Options
|
||||
options.step-ssh-host = {
|
||||
hostname = lib.mkOption {
|
||||
description = "Networking host name to register with the CA";
|
||||
type = lib.types.str;
|
||||
};
|
||||
options.ssh-certs = {
|
||||
provisioner = lib.mkOption {
|
||||
description = "Provisioner inside Step CA to use for the SSH certificates";
|
||||
type = lib.types.str;
|
||||
default = "admin";
|
||||
};
|
||||
extraPrincipals = lib.mkOption {
|
||||
description = "Additional SSH host certificate principals to include per host";
|
||||
type = with lib.types; listOf str;
|
||||
default = [ ];
|
||||
};
|
||||
};
|
||||
|
||||
imports = with inputs.self.modules.nixos; [ ssh ];
|
||||
# imports = with inputs.self.modules.nixos; [ ssh ];
|
||||
|
||||
# NixOS Config
|
||||
config = {
|
||||
ssh.certificates.enable = true;
|
||||
# ssh.certificates.enable = true;
|
||||
sops.secrets."janus/admin_jwk" = {
|
||||
# Shared provisioner credential is intentionally centralized.
|
||||
sopsFile = ../../../keys/secrets.yaml;
|
||||
owner = "root";
|
||||
group = "root";
|
||||
mode = "0400";
|
||||
};
|
||||
networking.nameservers = [ "192.168.1.150" ];
|
||||
networking.dhcpcd.extraConfig = "nohook resolv.conf";
|
||||
environment.systemPackages = with pkgs; [
|
||||
# step-cli
|
||||
(writeShellScriptBin "ssh-host-cert-renew" ''
|
||||
${lib.getExe pkgs.step-cli} ssh certificate \
|
||||
--host --sign \
|
||||
--provisioner "${cfg.provisioner}" \
|
||||
--provisioner-password-file "${provisionerPasswordPath}" \
|
||||
--principal "${cfg.hostname}" \
|
||||
--principal "${cfg.hostname}.john-stream.com" \
|
||||
"${cfg.hostname}" "${sshKeyPath}.pub"
|
||||
'')
|
||||
(writeShellScriptBin "ssh-host-cert-check" "${lib.getExe' pkgs.openssh "ssh-keygen"} -Lf ${sshCertPath}")
|
||||
environment.systemPackages = [
|
||||
sshHostCertSign
|
||||
sshHostCertRenew
|
||||
sshHostRenewalCheck
|
||||
sshHostCertCheck
|
||||
];
|
||||
|
||||
systemd.services.step-ssh-host-renew = {
|
||||
description = "Renew Step SSH host certificate if needed";
|
||||
wantedBy = [ ];
|
||||
systemd.services.ssh-certs-renew = {
|
||||
description = "SSH host certificate renewal";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
path = with pkgs; [ coreutils systemd step-cli openssh ];
|
||||
path = with pkgs; [ step-cli systemd ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "root";
|
||||
Group = "root";
|
||||
ExecCondition = lib.getExe sshHostRenewalCheck;
|
||||
ExecStart = lib.getExe sshHostCertRenew;
|
||||
};
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
if ${lib.getExe pkgs.step-cli} ssh needs-renewal "${sshCertPath}" --expires-in "4h"; then
|
||||
echo "Renewing SSH host certificate"
|
||||
else
|
||||
rc=$?
|
||||
if [ "$rc" -eq 1 ]; then
|
||||
echo "SSH host cert does not need renewal"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$rc" -eq 2 ]; then
|
||||
echo "SSH host cert missing: ${sshCertPath}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "step ssh needs-renewal failed with rc=$rc" >&2
|
||||
exit "$rc"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.timers.step-ssh-host-renew = {
|
||||
systemd.timers.ssh-certs-renew = {
|
||||
description = "Periodic Step SSH host certificate renewal";
|
||||
wantedBy = [ "timers.target" ];
|
||||
|
||||
timerConfig = {
|
||||
OnBootSec = "5m";
|
||||
OnUnitActiveSec = "4h";
|
||||
RandomizedDelaySec = "15m";
|
||||
Persistent = true;
|
||||
Unit = "step-ssh-host-renew.service";
|
||||
Unit = "ssh-certs-renew.service";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,25 +1,190 @@
|
||||
{ inputs, ... }:
|
||||
{
|
||||
flake.modules.nixos.step-ca = { config, pkgs, lib, ... }:
|
||||
let
|
||||
ipAddress = "0.0.0.0";
|
||||
cfg = config.step-ca;
|
||||
caAddress = "0.0.0.0";
|
||||
caPort = 443;
|
||||
|
||||
caPasswordPath = (lib.getAttr cfg.secrets.caPassword config.sops.secrets).path;
|
||||
intermediateKeyPath = (lib.getAttr cfg.secrets.intermediateKey config.sops.secrets).path;
|
||||
sshHostCaKeyPath = (lib.getAttr cfg.secrets.sshHostCaKey config.sops.secrets).path;
|
||||
sshUserCaKeyPath = (lib.getAttr cfg.secrets.sshUserCaKey config.sops.secrets).path;
|
||||
adminProvisionerEncryptedKeyValue =
|
||||
(lib.getAttr cfg.secrets.adminProvisionerEncryptedKey config.sops.placeholder);
|
||||
|
||||
renderedStepCaConfig = builtins.toJSON {
|
||||
root = cfg.rootCertPath;
|
||||
crt = cfg.intermediateCertPath;
|
||||
key = intermediateKeyPath;
|
||||
address = "${caAddress}:${toString caPort}";
|
||||
dnsNames = cfg.dnsNames;
|
||||
|
||||
ssh = {
|
||||
hostKey = sshHostCaKeyPath;
|
||||
userKey = sshUserCaKeyPath;
|
||||
};
|
||||
|
||||
db = {
|
||||
type = "badgerv2";
|
||||
dataSource = "/var/lib/step-ca/db";
|
||||
};
|
||||
|
||||
authority = {
|
||||
backdate = "1m0s";
|
||||
provisioners = [
|
||||
{
|
||||
type = "ACME";
|
||||
name = "acme";
|
||||
}
|
||||
{
|
||||
type = "SSHPOP";
|
||||
name = "sshpop";
|
||||
claims.enableSSHCA = true;
|
||||
}
|
||||
{
|
||||
type = "JWK";
|
||||
name = "admin";
|
||||
key = {
|
||||
use = "sig";
|
||||
kty = "EC";
|
||||
kid = "xoxgOJFbveSLIL2gm1Yu5ZiRb9v8Jxe44F56i3v-Nf8";
|
||||
crv = "P-256";
|
||||
alg = "ES256";
|
||||
x = "zFO8hPx_eH0Iyz7UJI-w8ODMusEKCZ28M76sGWmWYxA";
|
||||
y = "XIWLLyKDzqxV9UH-2KeAkKPDrgLoPrxxW9-PzkXggME";
|
||||
};
|
||||
encryptedKey = adminProvisionerEncryptedKeyValue;
|
||||
claims = {
|
||||
enableSSHCA = true;
|
||||
disableRenewal = false;
|
||||
allowRenewalAfterExpiry = false;
|
||||
disableSmallstepExtensions = false;
|
||||
};
|
||||
options = {
|
||||
x509 = { };
|
||||
ssh = { };
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
tls = {
|
||||
cipherSuites = [
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
|
||||
];
|
||||
minVersion = 1.2;
|
||||
maxVersion = 1.3;
|
||||
renegotiation = false;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
flake.modules.nixos.step-ca = { pkgs, ... }: {
|
||||
options.step-ca = {
|
||||
rootCertPath = lib.mkOption {
|
||||
description = "Path to the Step CA root certificate served by this host.";
|
||||
type = lib.types.path;
|
||||
};
|
||||
intermediateCertPath = lib.mkOption {
|
||||
description = "Path to the Step CA intermediate certificate served by this host. This is public material and does not need to be stored in SOPS.";
|
||||
type = lib.types.path;
|
||||
};
|
||||
dnsNames = lib.mkOption {
|
||||
description = "DNS names and IP SANs advertised by this Step CA instance.";
|
||||
type = with lib.types; listOf str;
|
||||
};
|
||||
secrets = {
|
||||
sopsFile = lib.mkOption {
|
||||
description = "Host-local SOPS file that stores Step CA secret material.";
|
||||
type = lib.types.path;
|
||||
};
|
||||
caPassword = lib.mkOption {
|
||||
description = "SOPS key for the Step CA intermediate password.";
|
||||
type = lib.types.str;
|
||||
};
|
||||
intermediateKey = lib.mkOption {
|
||||
description = "SOPS key for the Step CA intermediate private key.";
|
||||
type = lib.types.str;
|
||||
};
|
||||
sshHostCaKey = lib.mkOption {
|
||||
description = "SOPS key for the Step SSH host CA private key.";
|
||||
type = lib.types.str;
|
||||
};
|
||||
sshUserCaKey = lib.mkOption {
|
||||
description = "SOPS key for the Step SSH user CA private key.";
|
||||
type = lib.types.str;
|
||||
};
|
||||
adminProvisionerEncryptedKey = lib.mkOption {
|
||||
description = "SOPS key for the Step CA admin provisioner encrypted key.";
|
||||
type = lib.types.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# Placeholders are expected initially until real material is inserted into sops.
|
||||
sops.secrets."${cfg.secrets.caPassword}" = {
|
||||
sopsFile = cfg.secrets.sopsFile;
|
||||
owner = "step-ca";
|
||||
group = "step-ca";
|
||||
mode = "0400";
|
||||
restartUnits = [ "step-ca.service" ];
|
||||
};
|
||||
sops.secrets."${cfg.secrets.intermediateKey}" = {
|
||||
sopsFile = cfg.secrets.sopsFile;
|
||||
owner = "step-ca";
|
||||
group = "step-ca";
|
||||
mode = "0400";
|
||||
restartUnits = [ "step-ca.service" ];
|
||||
};
|
||||
sops.secrets."${cfg.secrets.sshHostCaKey}" = {
|
||||
sopsFile = cfg.secrets.sopsFile;
|
||||
owner = "step-ca";
|
||||
group = "step-ca";
|
||||
mode = "0400";
|
||||
restartUnits = [ "step-ca.service" ];
|
||||
};
|
||||
sops.secrets."${cfg.secrets.sshUserCaKey}" = {
|
||||
sopsFile = cfg.secrets.sopsFile;
|
||||
owner = "step-ca";
|
||||
group = "step-ca";
|
||||
mode = "0400";
|
||||
restartUnits = [ "step-ca.service" ];
|
||||
};
|
||||
sops.secrets."${cfg.secrets.adminProvisionerEncryptedKey}" = {
|
||||
sopsFile = cfg.secrets.sopsFile;
|
||||
owner = "step-ca";
|
||||
group = "step-ca";
|
||||
mode = "0400";
|
||||
restartUnits = [ "step-ca.service" ];
|
||||
};
|
||||
|
||||
sops.templates."step-ca-config" = {
|
||||
owner = "step-ca";
|
||||
group = "step-ca";
|
||||
mode = "0400";
|
||||
content = renderedStepCaConfig;
|
||||
};
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/blob/nixos-23.05/nixos/modules/services/security/step-ca.nix
|
||||
services.step-ca = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
address = ipAddress;
|
||||
port = 8443;
|
||||
address = caAddress;
|
||||
port = caPort;
|
||||
intermediatePasswordFile = caPasswordPath;
|
||||
};
|
||||
|
||||
environment.etc."smallstep/ca.json".source =
|
||||
lib.mkForce config.sops.templates."step-ca-config".path;
|
||||
systemd.services.step-ca.restartTriggers =
|
||||
lib.mkAfter [ config.sops.templates."step-ca-config".path ];
|
||||
|
||||
# https://smallstep.com/docs/step-ca/configuration/#configuration-options
|
||||
settings = {
|
||||
root = "";
|
||||
crt = "";
|
||||
};
|
||||
};
|
||||
environment.systemPackages = with pkgs; [
|
||||
step-ca
|
||||
step-cli
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
+31
-20
@@ -1,6 +1,10 @@
|
||||
{ self, inputs, lib, ... }:
|
||||
{ self, inputs, ... }:
|
||||
let
|
||||
username = "john";
|
||||
baseUserModules = self.factory.user {
|
||||
username = username;
|
||||
isAdmin = true;
|
||||
};
|
||||
in
|
||||
{
|
||||
flake.meta.users."${username}" = {
|
||||
@@ -8,23 +12,17 @@ in
|
||||
name = "John Lancaster";
|
||||
inherit username;
|
||||
key = "";
|
||||
keygrip = [
|
||||
];
|
||||
keygrip = [ ];
|
||||
authorizedKeys = [
|
||||
# "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIAUa4dcg1TWc4pW++uodyhX4eOqrX/QYIxFWtEP7HFJ john@john-pc-ubuntu"
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMOkGLo4N/L3RYvaIZ1FmePlxa1HK0fMciZxKtRhN58F root@janus"
|
||||
# Shared keys for every host can go here.
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIAUa4dcg1TWc4pW++uodyhX4eOqrX/QYIxFWtEP7HFJ john@john-pc-ubuntu"
|
||||
];
|
||||
};
|
||||
|
||||
flake.modules = lib.mkMerge [
|
||||
(self.factory.user {
|
||||
username = username;
|
||||
isAdmin = true;
|
||||
})
|
||||
{
|
||||
nixos."${username}" = {
|
||||
flake.modules = {
|
||||
nixos."${username}" = { config, pkgs, ... }: {
|
||||
imports = [
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
baseUserModules.nixos."${username}"
|
||||
];
|
||||
users.users."${username}" = {
|
||||
openssh.authorizedKeys.keys = inputs.self.meta.users."${username}".authorizedKeys;
|
||||
@@ -32,17 +30,30 @@ in
|
||||
};
|
||||
|
||||
# This module will be imported by the user factory
|
||||
homeManager."${username}" = with inputs.self.meta.users."${username}"; {
|
||||
homeManager."${username}" = { pkgs, ... }:
|
||||
with inputs.self.meta.users."${username}";
|
||||
let
|
||||
selfPkgs = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||
in {
|
||||
home.stateVersion = "25.11";
|
||||
xdg.enable = true;
|
||||
programs.git.settings.user.name = name;
|
||||
programs.git.settings.user.email = email;
|
||||
imports = with inputs.self.modules.homeManager; [
|
||||
ssh
|
||||
ssh-new
|
||||
shell-tools
|
||||
git
|
||||
];
|
||||
xdg.enable = true;
|
||||
home.packages = [
|
||||
selfPkgs.neovim-min
|
||||
];
|
||||
home.sessionVariables = {
|
||||
EDITOR = "nvim";
|
||||
VISUAL = "nvim";
|
||||
GIT_EDITOR = "nvim";
|
||||
SOPS_EDITOR = "nvim";
|
||||
};
|
||||
programs.git.settings.user.name = name;
|
||||
programs.git.settings.user.email = email;
|
||||
programs.git.settings.core.editor = "nvim";
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
Executable
+212
@@ -0,0 +1,212 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u
|
||||
|
||||
# --- pretty output helpers ---
|
||||
BOLD="\033[1m"
|
||||
DIM="\033[2m"
|
||||
GREEN="\033[32m"
|
||||
YELLOW="\033[33m"
|
||||
RED="\033[31m"
|
||||
CYAN="\033[36m"
|
||||
RESET="\033[0m"
|
||||
|
||||
section() {
|
||||
echo
|
||||
echo -e "${BOLD}${CYAN}==> $1${RESET}"
|
||||
}
|
||||
|
||||
ok() { echo -e "${GREEN}[OK]${RESET} $1"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${RESET} $1"; }
|
||||
err() { echo -e "${RED}[ERR]${RESET} $1"; }
|
||||
info() { echo -e "${DIM}$1${RESET}"; }
|
||||
|
||||
has_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
found_process=0
|
||||
found_render_node=0
|
||||
found_gpu_libs=0
|
||||
software_rasterizer=0
|
||||
|
||||
section "1) Find a running Ghostty process"
|
||||
info "What this does: tries the same two process lookups you ran (pgrep and pidof)."
|
||||
info "Good outcome: at least one PID is returned."
|
||||
|
||||
echo -e "${DIM}\$ pgrep -x ghostty${RESET}"
|
||||
PGREP_OUT="$(pgrep -x ghostty || true)"
|
||||
if [[ -n "${PGREP_OUT}" ]]; then
|
||||
echo "${PGREP_OUT}"
|
||||
else
|
||||
echo "<no output>"
|
||||
fi
|
||||
|
||||
echo -e "${DIM}\$ pidof ghostty${RESET}"
|
||||
PIDOF_OUT="$(pidof ghostty || true)"
|
||||
if [[ -n "${PIDOF_OUT}" ]]; then
|
||||
echo "${PIDOF_OUT}"
|
||||
else
|
||||
echo "<no output>"
|
||||
fi
|
||||
|
||||
if [[ -n "${PGREP_OUT}" || -n "${PIDOF_OUT}" ]]; then
|
||||
found_process=1
|
||||
ok "Ghostty process detected."
|
||||
else
|
||||
warn "No Ghostty process found yet by either command."
|
||||
fi
|
||||
|
||||
section "2) Set GHOSTTY_PID"
|
||||
info "What this does: picks one Ghostty PID for all later checks."
|
||||
info "Good outcome: GHOSTTY_PID is set to a valid running Ghostty process."
|
||||
|
||||
# Prefer step-1 outputs first (avoids re-running lookups that may block on some systems).
|
||||
GHOSTTY_PID=""
|
||||
if [[ -n "${PGREP_OUT}" ]]; then
|
||||
GHOSTTY_PID="$(printf '%s\n' "${PGREP_OUT}" | head -n1)"
|
||||
info "Using PID from step-1 pgrep output."
|
||||
elif [[ -n "${PIDOF_OUT}" ]]; then
|
||||
GHOSTTY_PID="$(printf '%s\n' "${PIDOF_OUT}" | awk '{print $1}')"
|
||||
info "Using PID from step-1 pidof output."
|
||||
else
|
||||
echo -e "${DIM}\$ pgrep -xo ghostty${RESET}"
|
||||
if has_cmd timeout; then
|
||||
PGREP_RAW="$(timeout 3s pgrep -xo ghostty 2>&1)"
|
||||
pgrep_status=$?
|
||||
if (( pgrep_status == 124 )); then
|
||||
warn "pgrep timed out after 3 seconds."
|
||||
fi
|
||||
else
|
||||
PGREP_RAW="$(pgrep -xo ghostty 2>&1)"
|
||||
pgrep_status=$?
|
||||
fi
|
||||
|
||||
if (( pgrep_status == 0 )); then
|
||||
GHOSTTY_PID="$(printf '%s\n' "${PGREP_RAW}" | head -n1)"
|
||||
else
|
||||
if [[ -n "${PGREP_RAW}" ]]; then
|
||||
warn "[pgrep] ${PGREP_RAW}"
|
||||
fi
|
||||
warn "pgrep exit code: ${pgrep_status} (1 usually means no matching process)."
|
||||
|
||||
echo -e "${DIM}\$ pidof ghostty${RESET}"
|
||||
PIDOF_RAW="$(pidof ghostty 2>&1)"
|
||||
pidof_status=$?
|
||||
|
||||
if (( pidof_status == 0 )); then
|
||||
GHOSTTY_PID="$(printf '%s\n' "${PIDOF_RAW}" | awk '{print $1}')"
|
||||
else
|
||||
if [[ -n "${PIDOF_RAW}" ]]; then
|
||||
warn "[pidof] ${PIDOF_RAW}"
|
||||
fi
|
||||
warn "pidof exit code: ${pidof_status}"
|
||||
GHOSTTY_PID=""
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "${GHOSTTY_PID}" ]]; then
|
||||
err "Could not find a running Ghostty process to inspect."
|
||||
echo "Open Ghostty, then run this script again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export GHOSTTY_PID
|
||||
ok "Using GHOSTTY_PID=${GHOSTTY_PID}"
|
||||
|
||||
section "3) Check for an open GPU render device"
|
||||
info "What this does: inspects /proc/<pid>/fd for /dev/dri entries used for rendering."
|
||||
info "Good outcome: see /dev/dri/renderD* (for example renderD128)."
|
||||
|
||||
if has_cmd rg; then
|
||||
echo -e "${DIM}\$ sudo ls -l /proc/\$GHOSTTY_PID/fd | rg dri${RESET}"
|
||||
DRI_LINES="$(sudo ls -l "/proc/${GHOSTTY_PID}/fd" | rg dri || true)"
|
||||
else
|
||||
warn "'rg' not found; using grep instead"
|
||||
echo -e "${DIM}\$ sudo ls -l /proc/\$GHOSTTY_PID/fd | grep dri${RESET}"
|
||||
DRI_LINES="$(sudo ls -l "/proc/${GHOSTTY_PID}/fd" | grep dri || true)"
|
||||
fi
|
||||
|
||||
if [[ -n "${DRI_LINES}" ]]; then
|
||||
echo "${DRI_LINES}"
|
||||
else
|
||||
echo "<no dri-related file descriptors found>"
|
||||
fi
|
||||
|
||||
if echo "${DRI_LINES}" | grep -q '/dev/dri/renderD'; then
|
||||
found_render_node=1
|
||||
ok "Found /dev/dri/renderD*: strong sign Ghostty is using GPU render infrastructure."
|
||||
elif [[ -n "${DRI_LINES}" ]]; then
|
||||
warn "Found /dev/dri entries, but no renderD node in this output."
|
||||
else
|
||||
warn "No /dev/dri entries seen for this process."
|
||||
fi
|
||||
|
||||
section "4) Check mapped graphics libraries"
|
||||
info "What this does: reads process memory mappings for GL/EGL/Vulkan/Mesa libraries."
|
||||
info "Good outcome: matches like libGL, libEGL, libvulkan, mesa, libgbm, libgallium appear."
|
||||
|
||||
echo -e "${DIM}\$ sudo grep -E 'libEGL|libGL|libvulkan|mesa' /proc/\$GHOSTTY_PID/maps | head -n 30${RESET}"
|
||||
LIB_LINES="$(sudo grep -E 'libEGL|libGL|libvulkan|mesa' "/proc/${GHOSTTY_PID}/maps" | head -n 30 || true)"
|
||||
|
||||
if [[ -n "${LIB_LINES}" ]]; then
|
||||
echo "${LIB_LINES}"
|
||||
found_gpu_libs=1
|
||||
ok "Graphics stack libraries are mapped into Ghostty."
|
||||
else
|
||||
echo "<no matches>"
|
||||
warn "No GL/EGL/Vulkan/Mesa mappings matched this filter."
|
||||
fi
|
||||
|
||||
section "5) Optional probe for software rasterizer vs hardware driver hints"
|
||||
info "What this does: scans for common software rasterizer strings (llvmpipe/swrast) and driver names."
|
||||
info "Good outcome: no llvmpipe/swrast matches. Driver-name matches can vary by stack."
|
||||
|
||||
echo -e "${DIM}\$ sudo grep -Ei 'llvmpipe|swrast|iris|radeonsi|nouveau|zink|nvidia|_dri\\.so' /proc/\$GHOSTTY_PID/maps${RESET}"
|
||||
PROBE_LINES="$(sudo grep -Ei 'llvmpipe|swrast|iris|radeonsi|nouveau|zink|nvidia|_dri\.so' "/proc/${GHOSTTY_PID}/maps" || true)"
|
||||
|
||||
if [[ -n "${PROBE_LINES}" ]]; then
|
||||
echo "${PROBE_LINES}"
|
||||
if echo "${PROBE_LINES}" | grep -Eiq 'llvmpipe|swrast'; then
|
||||
software_rasterizer=1
|
||||
warn "Software rasterizer markers detected (llvmpipe/swrast)."
|
||||
else
|
||||
ok "Driver-related markers detected and no software rasterizer markers found."
|
||||
fi
|
||||
else
|
||||
ok "No probe matches found. This is often fine; absence of llvmpipe/swrast is usually good."
|
||||
fi
|
||||
|
||||
section "Final interpretation"
|
||||
if (( found_process == 1 )); then
|
||||
ok "Process check: PASS"
|
||||
else
|
||||
warn "Process check: no process found initially (script still proceeded once PID was resolved)."
|
||||
fi
|
||||
|
||||
if (( found_render_node == 1 )); then
|
||||
ok "Render node check: PASS (/dev/dri/renderD* present)"
|
||||
else
|
||||
warn "Render node check: no explicit renderD node found"
|
||||
fi
|
||||
|
||||
if (( found_gpu_libs == 1 )); then
|
||||
ok "Graphics library check: PASS"
|
||||
else
|
||||
warn "Graphics library check: no matches"
|
||||
fi
|
||||
|
||||
if (( software_rasterizer == 1 )); then
|
||||
warn "Software rasterizer check: POSSIBLE software rendering path"
|
||||
else
|
||||
ok "Software rasterizer check: no llvmpipe/swrast markers detected"
|
||||
fi
|
||||
|
||||
echo
|
||||
if (( found_render_node == 1 && found_gpu_libs == 1 && software_rasterizer == 0 )); then
|
||||
ok "Overall: strong evidence Ghostty is using GPU acceleration."
|
||||
else
|
||||
warn "Overall: mixed signals. Consider running a live GPU activity monitor during terminal redraw stress."
|
||||
fi
|
||||
|
||||
ok "Summary complete for PID ${GHOSTTY_PID}"
|
||||
Reference in New Issue
Block a user