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-p14s age1f6drjusg866yscj8029tk4yfpgecklrvezldm02ankm6h8nnwu5s2u6ahy
|
||||||
- &john-pc age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
|
- &john-pc age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
|
||||||
- &test-nix age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
|
- &test-nix age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
|
||||||
- &soteria age1h0prahyukq4l564yqwgcpg3g6gdrjflk0suklussjjrjstxd9uesws8633
|
- &soteria age1p4gyh5260ewp7t2ctzv5ewj4a06szl389fm4gzy6vltxhjj73ers87u33g
|
||||||
|
- &janus age1qahhlzeanprtykym9jymk2t95uedr7cwx9sdshx46q2m6u66fucsqua8l3
|
||||||
creation_rules:
|
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$
|
- path_regex: soteria/secrets\.yaml$
|
||||||
key_groups:
|
key_groups:
|
||||||
- age:
|
- age:
|
||||||
- *john-pc
|
|
||||||
- *soteria
|
- *soteria
|
||||||
|
- *john-pc
|
||||||
- *test-nix
|
- *test-nix
|
||||||
- path_regex: john-p14s/secrets\.yaml$
|
- path_regex: john-p14s/secrets\.yaml$
|
||||||
key_groups:
|
key_groups:
|
||||||
|
|||||||
@@ -12,6 +12,22 @@ home-manager switch --flake .#desktop
|
|||||||
nix flake show --all-systems
|
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
|
## Layout
|
||||||
|
|
||||||
- Everything under `./modules` gets auto-imported by `import-tree`
|
- Everything under `./modules` gets auto-imported by `import-tree`
|
||||||
|
|||||||
Generated
+57
-97
@@ -3,11 +3,11 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1751685974,
|
"lastModified": 1777699697,
|
||||||
"narHash": "sha256-NKw96t+BgHIYzHUjkTK95FqYRVKB8DHpVhefWSz/kTw=",
|
"narHash": "sha256-Eg9b/rq/ECYwNwEXs5i9wHyhxNI0JrYx2srdI2uZMaQ=",
|
||||||
"ref": "refs/heads/main",
|
"ref": "refs/heads/main",
|
||||||
"rev": "549f2762aebeff29a2e5ece7a7dc0f955281a1d1",
|
"rev": "382052b74656a369c5408822af3f2501e9b1af81",
|
||||||
"revCount": 92,
|
"revCount": 94,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.lix.systems/lix-project/flake-compat.git"
|
"url": "https://git.lix.systems/lix-project/flake-compat.git"
|
||||||
},
|
},
|
||||||
@@ -18,11 +18,11 @@
|
|||||||
},
|
},
|
||||||
"flake-file": {
|
"flake-file": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775848911,
|
"lastModified": 1781217157,
|
||||||
"narHash": "sha256-dqva/tlWxsXj32wYPdt06UrrR4l2QdK9JWyvPooWRi4=",
|
"narHash": "sha256-N3q/SP2Ropk336e9KSgLh7kpROY6P70dprYdbPIfd5c=",
|
||||||
"owner": "vic",
|
"owner": "vic",
|
||||||
"repo": "flake-file",
|
"repo": "flake-file",
|
||||||
"rev": "b36cbd5fc01e9794a001ccb0c58b314efaabae08",
|
"rev": "ce63eaf7ebfe04a176653f66385a7f0a36380cee",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -34,15 +34,15 @@
|
|||||||
"flake-parts": {
|
"flake-parts": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs-lib": [
|
"nixpkgs-lib": [
|
||||||
"nixpkgs-lib"
|
"nixpkgs"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775087534,
|
"lastModified": 1782949081,
|
||||||
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
|
"narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
|
"rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -59,11 +59,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769996383,
|
"lastModified": 1778716662,
|
||||||
"narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=",
|
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "57928607ea566b5db3ad13af0e57e921e6b12381",
|
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -95,11 +95,11 @@
|
|||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776046499,
|
"lastModified": 1783134515,
|
||||||
"narHash": "sha256-Wzc4nn07/0RL21ypPHRzNDQZcjhIC8LaYo7QJQjM5T4=",
|
"narHash": "sha256-qMoZazubXlXUD9k/syJ/aiWC4X4g73mwVmZ7Z4+rdpM=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "287f84846c1eb3b72c986f5f6bebcff0bd67440d",
|
"rev": "b885baad531fa3d3beae2ba9a0712d22974d8016",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -110,11 +110,11 @@
|
|||||||
},
|
},
|
||||||
"import-tree": {
|
"import-tree": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773693634,
|
"lastModified": 1778781969,
|
||||||
"narHash": "sha256-BtZ2dtkBdSUnFPPFc+n0kcMbgaTxzFNPv2iaO326Ffg=",
|
"narHash": "sha256-Jjuz5CmSkur8KvLDoGa+vylEp+RkQtv4mt/qcMznpH0=",
|
||||||
"owner": "vic",
|
"owner": "vic",
|
||||||
"repo": "import-tree",
|
"repo": "import-tree",
|
||||||
"rev": "c41e7d58045f9057880b0d85e1152d6a4430dbf1",
|
"rev": "d321337efd0f23a9eb14a42adb7b2c29313ab274",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -125,11 +125,11 @@
|
|||||||
},
|
},
|
||||||
"mnw": {
|
"mnw": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1770419553,
|
"lastModified": 1780772958,
|
||||||
"narHash": "sha256-b1XqsH7AtVf2dXmq2iyRr2NC1yG7skY7Z6N2MpWHlK4=",
|
"narHash": "sha256-VKKe8r4pwCGWZ3Yr9CPN129R4S3CKLSrlYqdYz3vKpM=",
|
||||||
"owner": "Gerg-L",
|
"owner": "Gerg-L",
|
||||||
"repo": "mnw",
|
"repo": "mnw",
|
||||||
"rev": "2aaffa8030d0b262176146adbb6b0e6374ce2957",
|
"rev": "0871dbf63a53610c95db04439ed8ea4d6ec9c160",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -138,28 +138,6 @@
|
|||||||
"type": "github"
|
"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": {
|
"nixgl": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": "flake-utils",
|
||||||
@@ -184,11 +162,11 @@
|
|||||||
"nixos-hardware": {
|
"nixos-hardware": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775490113,
|
"lastModified": 1782562157,
|
||||||
"narHash": "sha256-2ZBhDNZZwYkRmefK5XLOusCJHnoeKkoN95hoSGgMxWM=",
|
"narHash": "sha256-a7+T6QSeowynwZ1ZJJbP8T8ntAytvrui8kFGJmIZt2c=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixos-hardware",
|
"repo": "nixos-hardware",
|
||||||
"rev": "c775c2772ba56e906cbeb4e0b2db19079ef11ff7",
|
"rev": "a9cf7546a938c737b079e738de73934a13de9784",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -199,40 +177,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775423009,
|
"lastModified": 1782175435,
|
||||||
"narHash": "sha256-vPKLpjhIVWdDrfiUM8atW6YkIggCEKdSAlJPzzhkQlw=",
|
"narHash": "sha256-EMzXKmnOtBQ2MnvpiNOm7E+kOMvdPrIKaeg52Tip2Uk=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "68d8aa3d661f0e6bd5862291b5bb263b2a6595c9",
|
"rev": "89570f24e97e614aa34aa9ab1c927b6578a43775",
|
||||||
"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",
|
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -242,23 +191,35 @@
|
|||||||
"type": "github"
|
"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": {
|
"nvf": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"flake-parts": "flake-parts_2",
|
"flake-parts": "flake-parts_2",
|
||||||
"mnw": "mnw",
|
"mnw": "mnw",
|
||||||
"ndg": "ndg",
|
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"systems": "systems_2"
|
"systems": "systems_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775892726,
|
"lastModified": 1783164413,
|
||||||
"narHash": "sha256-1TK1pe33cEHNvGW41TP5xAzrbG1Gp7LfyFL6c3+xf+I=",
|
"narHash": "sha256-7DMamcvS35/tI+KZKXt45bHAq2Vnucl74Xetvp7DR3I=",
|
||||||
"owner": "notashelf",
|
"owner": "notashelf",
|
||||||
"repo": "nvf",
|
"repo": "nvf",
|
||||||
"rev": "5ab359ee7dfd3fa09a5c6f863efaf810bb9a9436",
|
"rev": "c8386ccfbc7053d454fa2d12d716c6ebb4167d51",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -276,9 +237,6 @@
|
|||||||
"nixgl": "nixgl",
|
"nixgl": "nixgl",
|
||||||
"nixos-hardware": "nixos-hardware",
|
"nixos-hardware": "nixos-hardware",
|
||||||
"nixpkgs": "nixpkgs_2",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"nixpkgs-lib": [
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"nvf": "nvf",
|
"nvf": "nvf",
|
||||||
"sops-nix": "sops-nix",
|
"sops-nix": "sops-nix",
|
||||||
"wrapper-modules": "wrapper-modules",
|
"wrapper-modules": "wrapper-modules",
|
||||||
@@ -292,11 +250,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775971308,
|
"lastModified": 1783174389,
|
||||||
"narHash": "sha256-VKp9bhVSm0bT6JWctFy06ocqxGGnWHi1NfoE90IgIcY=",
|
"narHash": "sha256-aCWC8ngycU7OdJrU2+Je3qf+1a2ykuBvpPhZT/9tXMc=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "31ac5fe5d015f76b54058c69fcaebb66a55871a4",
|
"rev": "f1406619a3884cd5c47992a70b8b35c9c0fcb4c9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -337,14 +295,16 @@
|
|||||||
},
|
},
|
||||||
"wrapper-modules": {
|
"wrapper-modules": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs_3"
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775940860,
|
"lastModified": 1782135443,
|
||||||
"narHash": "sha256-8Jxnn2uoFhm2H579ycVxFWDtrywJ6Mc8RmqWbZwk5So=",
|
"narHash": "sha256-vAmbArdCyjqpVW+37aCy/PMBOLIqukUXLQuEKLwUhA4=",
|
||||||
"owner": "BirdeeHub",
|
"owner": "BirdeeHub",
|
||||||
"repo": "nix-wrapper-modules",
|
"repo": "nix-wrapper-modules",
|
||||||
"rev": "fb62851ffc5f6a4d53ebc00b93743e29b41e6224",
|
"rev": "6e7f66fa2cdf4d63162580b438f7fcf87c28a46f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -360,11 +320,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1775600302,
|
"lastModified": 1782375095,
|
||||||
"narHash": "sha256-2fgKImv78CXIcfo1RsY7EI4uMZ84x/MggA5rrusYc7c=",
|
"narHash": "sha256-bRepNpAluz2i0IIk7Tmzw/4BkyOrwABvc66NEgXPEhA=",
|
||||||
"owner": "lassulus",
|
"owner": "lassulus",
|
||||||
"repo": "wrappers",
|
"repo": "wrappers",
|
||||||
"rev": "9d8397d8ef1ac35763085f3338589f558128f7db",
|
"rev": "cd780e92c0fa66c2e721e1d91894f3db13b9f2b4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
flake-file.url = "github:vic/flake-file";
|
flake-file.url = "github:vic/flake-file";
|
||||||
flake-parts = {
|
flake-parts = {
|
||||||
url = "github:hercules-ci/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";
|
home-manager.url = "github:nix-community/home-manager";
|
||||||
import-tree.url = "github:vic/import-tree";
|
import-tree.url = "github:vic/import-tree";
|
||||||
@@ -20,7 +20,6 @@
|
|||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
|
nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
|
||||||
nixpkgs-lib.follows = "nixpkgs";
|
|
||||||
nvf = {
|
nvf = {
|
||||||
url = "github:notashelf/nvf";
|
url = "github:notashelf/nvf";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
@@ -29,7 +28,10 @@
|
|||||||
url = "github:Mic92/sops-nix";
|
url = "github:Mic92/sops-nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
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 = {
|
wrappers = {
|
||||||
url = "github:lassulus/wrappers";
|
url = "github:lassulus/wrappers";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
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]
|
gmail_client_secret: ENC[AES256_GCM,data:du2gEY5TQIwpUEvJKDWKY3noLRGeiKek4IMwPUusVx8NMys=,iv:hIYi1xQYf6+hDhK0pNprBYu6wXwRH2yOTwQg6pzQa0A=,tag:sqmQ5GCkKbHpIy2R+Y5G/A==,type:str]
|
||||||
sops:
|
sops:
|
||||||
age:
|
age:
|
||||||
- recipient: age1f6drjusg866yscj8029tk4yfpgecklrvezldm02ankm6h8nnwu5s2u6ahy
|
- enc: |
|
||||||
enc: |
|
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0UEpja2kxdThZVWZhOGVP
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5dTVZQ2hFanhqbVJXTmxG
|
||||||
S0NtSi84MjhnN0RORkh2NjZ4YlYvWS9kZDBNClFzYnVxWnhmQkpCRkRFVUx1RDdX
|
YUFFanBiTzgyek1WZW5YajlaL3hVNElORHpJCm1JN2hxell5dmJVczlBbVI2VktH
|
||||||
ZHFqYXRqYXM0cWJzcU5EeEtSR1BUVzAKLS0tIDdEY2pnVTJqWlNZVkZldXVYVmFH
|
Z21lelF6UDBHcXIyUDE0clpxcEV4MGsKLS0tIG0zMktBVEt1SFZ6aGFaNW0wdzlB
|
||||||
dVNBRUVodU5sRnpVcG1GZ1RiZzhjTXMKefqBvvD/qZwcSHmFjUnleukVRLueG36Y
|
QkpQc3lkNmZCd0d6ZVRCc1ZaWjFYaDAK9RXWeW6dCIhYfVGPywsHlRigORVMuVfl
|
||||||
Q81KlwQweF2F8kHl7Bqsi+3hH1dZZbVm3vjuGpWFOoti7fowUV55Kw==
|
d+Evyp72wV5C1MnhV9jv8O4S8aQkcNae1PALacnFw0GJAI1iXn0bqw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
|
recipient: age1f6drjusg866yscj8029tk4yfpgecklrvezldm02ankm6h8nnwu5s2u6ahy
|
||||||
enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqZFZxbDhVUWFEUGhPMlZI
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBTWt3MEFDL3JGOUw0LzNa
|
||||||
SFdBYkpxSnAxTUZXbjVwQnlZQ3l1SWtuZGg0CmVBdnVHbTNUcmwvK01iMnZKZTJh
|
RmNLMmFRRzh5NEtZWENjNTlrcDg1MHpjWWg0Cjl5THh3ckdyMWVqR0M2Q0Y0czUr
|
||||||
ajFla3kzYUl4ZWY3czA0WUdNM2lpVFUKLS0tIHo5Uk1pV296MXdnUTZGQ25haWZG
|
Y0ZNRGVIbS9TNXlCcFo3R3lPZUl4cW8KLS0tIDN4QmhSYjBPMDZLWURhMndsK2hE
|
||||||
QWZDWGRaRDBhY1ZkZk5oTHY0ZVV2RXMKanv+WWRhf5nl+aw/T6QZFVQQmhV1DZfB
|
WVY1T0JnZ3dmSjNIMDNBYmhMellvMWcKp15FwF7lJnHoZ/tLNQTGvwP7Kffj8hms
|
||||||
jkSzOAKOgPx7toYFmpq9E8fAH+zrMzDbxI2z2uyrOFI6v+QE0Ul/iQ==
|
6G5ZG2howsNFiRFFdo/uL08ShaycL2PAL/Kge/0pPL1ygiDaDLjGGg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
|
recipient: age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
|
||||||
enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZcGtMNi9RSG5aTVV5cWdT
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwT1FoTDNmRXM4Tk9HZHNu
|
||||||
NittUXN0Qnpld1YvOU50OUh3Z3ZiSzhHOHdNCnc4TmdYbS9QQnBLbldHSytIdkJl
|
ZSszMEM1R0Z5bzY1ZzRvR3VGR2srVVdXbDBNCmp2c0tacndoQ3RiZUpNTnk1MTNH
|
||||||
R0psQWxkZTgyZTRzckkrTGpyNCsvR2sKLS0tIEdLb05aT2I2S3BKcFRrVmtvTGw5
|
V0Q4ckhWaDBjemYxZEhrSGJHcVlwYzQKLS0tIHNDZkNrNmZvM2plNEp2TTJmVlpT
|
||||||
Z1orRCtkTDVXSktuck5pTmV4K05qZHMKZlHHu07q+GnyDDgdwW2Ic3P23PmoSPwn
|
U1JhN2MrT2NzeURuVzRIZlNEVEd0S0EKBcTG4hWKdXqNfajVZ5DOClOeZsFYktPb
|
||||||
WuNLZdlZQleROaRb+zpD+9P1HGGJ3mWAlNlnmjGrRk453k1PbBQ5Og==
|
dAauLGwx1zkXbVxopUpH5+vCXp02kvvwgO9kiqJvSf0U+l/5VIp6Ww==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----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"
|
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]
|
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
|
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
|
# This module is for programs with GUIs that run in a desktop environment
|
||||||
{ self, inputs, ... }: {
|
{ self, inputs, ... }: {
|
||||||
flake.modules.homeManager.desktop = { config, pkgs, lib, ... }: {
|
flake.modules.homeManager.desktop = { config, pkgs, lib, ... }: {
|
||||||
|
nixpkgs.config.problems.handlers.sublimetext4.broken = "ignore";
|
||||||
imports = with inputs.self.modules.homeManager; [
|
imports = with inputs.self.modules.homeManager; [
|
||||||
brave
|
brave
|
||||||
ghostty
|
ghostty
|
||||||
@@ -10,6 +11,10 @@
|
|||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
mangohud
|
mangohud
|
||||||
sublime4
|
sublime4
|
||||||
|
proton-vpn
|
||||||
|
joplin-desktop
|
||||||
|
discord
|
||||||
|
github-copilot-cli
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
desktopManager.gnome.enable = true;
|
desktopManager.gnome.enable = true;
|
||||||
displayManager.gdm = {
|
displayManager.gdm = {
|
||||||
enable = true;
|
enable = true;
|
||||||
wayland = true;
|
|
||||||
banner = "Welcome to John's NixOS implementation";
|
banner = "Welcome to John's NixOS implementation";
|
||||||
};
|
};
|
||||||
udev.packages = [
|
udev.packages = [
|
||||||
|
|||||||
@@ -1,11 +1,24 @@
|
|||||||
# https://github.com/glabrie/dotfiles/blob/main/modules/system/settings/greetd.nix
|
# https://github.com/glabrie/dotfiles/blob/main/modules/system/settings/greetd.nix
|
||||||
{ inputs, ... }: {
|
{ 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 = {
|
services.greetd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings = {
|
settings = {
|
||||||
default_session = {
|
default_session = {
|
||||||
command = "${lib.getExe pkgs.tuigreet} --time --remember --cmd niri-session";
|
command = "${pkgs.dbus}/bin/dbus-run-session -- ${niriExe} --config ${greeterNiriConfig}";
|
||||||
user = "greeter";
|
user = "greeter";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+316
-164
@@ -48,7 +48,7 @@ let
|
|||||||
enable = lib.mkOption {
|
enable = lib.mkOption {
|
||||||
description = "Enable automatic mTLS certificate renewal using a systemd timer.";
|
description = "Enable automatic mTLS certificate renewal using a systemd timer.";
|
||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = true;
|
default = cfg.enable;
|
||||||
};
|
};
|
||||||
onCalendar = lib.mkOption {
|
onCalendar = lib.mkOption {
|
||||||
description = "systemd OnCalendar schedule for mTLS certificate renewal checks.";
|
description = "systemd OnCalendar schedule for mTLS certificate renewal checks.";
|
||||||
@@ -114,155 +114,28 @@ let
|
|||||||
printf '\033[32m✔\033[0m \033[1mmTLS Bundle:\033[0m %s\n' ${lib.escapeShellArg bundleFile}
|
printf '\033[32m✔\033[0m \033[1mmTLS Bundle:\033[0m %s\n' ${lib.escapeShellArg bundleFile}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
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 = [ ];
|
|
||||||
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; });
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
in
|
||||||
{
|
{
|
||||||
flake.modules.nixos.mtls = { config, lib, pkgs, ... }:
|
flake.modules.nixos.mtls = { config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.mtls;
|
cfg = config.mtls;
|
||||||
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) user group reloadUnits postCommands;
|
||||||
|
systemd = {
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = cfg.renew.user;
|
||||||
|
Group = cfg.renew.group;
|
||||||
|
};
|
||||||
|
} // lib.optionalAttrs cfg.renew.enable {
|
||||||
|
startAt = cfg.renew.onCalendar;
|
||||||
|
};
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.mtls = (mkOpts config) // {
|
options.mtls = (mkOpts config) // {
|
||||||
@@ -271,6 +144,33 @@ in
|
|||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "/etc/step-ca/certs";
|
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 {
|
certReaders = lib.mkOption {
|
||||||
description = "";
|
description = "";
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
@@ -291,30 +191,76 @@ in
|
|||||||
inherit (cfg) subject provisioner san certFile keyFile bundleFile lifetime;
|
inherit (cfg) subject provisioner san certFile keyFile bundleFile lifetime;
|
||||||
inherit (cfg.renew) user group;
|
inherit (cfg.renew) user group;
|
||||||
})
|
})
|
||||||
(mkMtlsCheckScript { inherit pkgs; inherit (cfg) bundleFile; })
|
(inputs.self.wrappers.mtlsCheck.apply {
|
||||||
(mkMtlsRenewScript { inherit pkgs cfg; })
|
inherit pkgs;
|
||||||
|
inherit (cfg) bundleFile;
|
||||||
|
}).wrapper
|
||||||
|
mtlsRenewWrapper.wrapper
|
||||||
];
|
];
|
||||||
|
|
||||||
systemd.tmpfiles.rules = [
|
systemd.tmpfiles.rules = [
|
||||||
"d ${cfg.certDir} 0750 ${cfg.renew.user} ${cfg.renew.group} -"
|
"d ${cfg.certDir} 0750 ${cfg.renew.user} ${cfg.renew.group} -"
|
||||||
];
|
];
|
||||||
|
|
||||||
systemd.services.mtls-renew = lib.mkIf cfg.renew.enable
|
systemd.packages = lib.mkIf cfg.renew.enable [
|
||||||
(mkNixosMtlsRenewService { inherit pkgs cfg; });
|
mtlsRenewWrapper.outputs.systemd-system
|
||||||
|
];
|
||||||
|
|
||||||
systemd.timers.mtls-renew = lib.mkIf cfg.renew.enable (mkNixosMtlsRenewTimer {
|
systemd.services.mtls-bootstrap = lib.mkIf cfg.bootstrap.enable {
|
||||||
inherit (cfg.renew) onCalendar randomizedDelaySec;
|
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, ... }:
|
flake.modules.homeManager.mtls = { config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.mtls;
|
cfg = config.mtls;
|
||||||
keyFile = cfg.keyFile;
|
mtlsRenewWrapper = inputs.self.wrappers.mtlsRenew.apply {
|
||||||
certFile = cfg.certFile;
|
inherit pkgs;
|
||||||
bundleFile = cfg.bundleFile;
|
inherit (cfg) certDir keyFile certFile bundleFile;
|
||||||
sanArgs = lib.concatMapStringsSep " " (san: "--san \"${san}\"") cfg.san;
|
inherit (cfg.renew) reloadUnits postCommands;
|
||||||
|
systemctlArgs = [ "--user" ];
|
||||||
|
systemd = lib.optionalAttrs cfg.renew.enable {
|
||||||
|
startAt = cfg.renew.onCalendar;
|
||||||
|
};
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.mtls = (mkOpts config) // {
|
options.mtls = (mkOpts config) // {
|
||||||
@@ -329,24 +275,230 @@ in
|
|||||||
home.packages = with pkgs; lib.optionals cfg.enable [
|
home.packages = with pkgs; lib.optionals cfg.enable [
|
||||||
# step-cli
|
# step-cli
|
||||||
(mkMtlsGenerateScript {
|
(mkMtlsGenerateScript {
|
||||||
|
inherit pkgs;
|
||||||
|
inherit (cfg) keyFile certFile bundleFile;
|
||||||
inherit (cfg) subject provisioner san lifetime;
|
inherit (cfg) subject provisioner san lifetime;
|
||||||
inherit (cfg.renew) user group;
|
inherit (cfg.renew) user group;
|
||||||
inherit pkgs certFile keyFile bundleFile;
|
|
||||||
})
|
})
|
||||||
(mkMtlsCheckScript { inherit pkgs bundleFile; })
|
(inputs.self.wrappers.mtlsCheck.apply {
|
||||||
(mkMtlsRenewScript { inherit pkgs cfg; systemctlArgs = [ "--user" ]; })
|
inherit pkgs;
|
||||||
|
inherit (cfg) bundleFile;
|
||||||
|
}).wrapper
|
||||||
];
|
];
|
||||||
|
|
||||||
systemd.user.tmpfiles.rules = lib.mkIf cfg.enable [
|
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
|
# Create the systemd service files for the user.
|
||||||
(mkHomeManagerMtlsRenewService { inherit pkgs cfg; });
|
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 {
|
# Ensure the timer gets started
|
||||||
inherit (cfg.renew) onCalendar randomizedDelaySec;
|
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,12 +1,67 @@
|
|||||||
{ self, inputs, ... }: {
|
{ self, inputs, ... }: {
|
||||||
flake.modules.nixos.restic-server = { config, pkgs, lib, ... }: {
|
flake.modules.nixos.restic-server = { config, pkgs, lib, ... }:
|
||||||
services.restic.server = {
|
let
|
||||||
enable = true;
|
cfg = config.resticServer;
|
||||||
dataDir = "/mnt/restic";
|
port = builtins.fromJSON (lib.last (lib.splitString ":" cfg.listenAddress));
|
||||||
listenAddress = "0.0.0.0:8080";
|
in {
|
||||||
extraFlags = [ "--no-auth" ];
|
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;
|
||||||
|
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}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
flake.modules.homeManager.restic = { config, pkgs, lib, ... }:
|
flake.modules.homeManager.restic = { config, pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
|
|||||||
@@ -1,41 +1,83 @@
|
|||||||
# This module provides all the shell options
|
# This module provides all the shell options
|
||||||
{ inputs, lib, ... }:
|
{ self, inputs, ... }:
|
||||||
{
|
{
|
||||||
flake.modules.homeManager.shell-tools = { config, pkgs, ... }: {
|
flake.modules.homeManager.shell-tools =
|
||||||
options.shell.program = lib.mkOption {
|
{ config, pkgs, ... }:
|
||||||
type = lib.types.enum [ "bash" "zsh" ];
|
{
|
||||||
default = "zsh";
|
imports = with inputs.self.modules.homeManager; [
|
||||||
description = "Which interactive shell configuration to enable.";
|
# bash
|
||||||
};
|
zsh
|
||||||
|
files
|
||||||
|
];
|
||||||
|
|
||||||
imports = with inputs.self.modules.homeManager; [
|
|
||||||
bash
|
|
||||||
zsh
|
|
||||||
|
|
||||||
# Tools
|
|
||||||
eza
|
|
||||||
files
|
|
||||||
];
|
|
||||||
|
|
||||||
config = {
|
|
||||||
home.shell.enableShellIntegration = true;
|
|
||||||
programs.zsh.enable = lib.mkForce (config.shell.program == "zsh");
|
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
nh
|
|
||||||
nvd
|
|
||||||
nix-output-monitor
|
|
||||||
wget
|
|
||||||
curl
|
|
||||||
busybox
|
|
||||||
gnugrep
|
|
||||||
dig
|
|
||||||
btop
|
btop
|
||||||
uv
|
uv
|
||||||
xclip
|
xclip
|
||||||
jq
|
inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.shell-tools
|
||||||
ripgrep
|
|
||||||
(writeShellScriptBin "ds" ''${lib.getExe pkgs.gdu} -x -I /snap /'')
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
let
|
||||||
username = "john";
|
username = "john";
|
||||||
hostname = "janus";
|
hostname = "janus";
|
||||||
ca-url = "https://janus.john-stream.com/";
|
ipv4 = "192.168.1.32";
|
||||||
fingerprint = builtins.readFile ./fingerprint;
|
ipv6 = "fded:fb16:653e:25da:be24:11ff:fe6b:4d57";
|
||||||
|
names = [ "${hostname}.john-stream.com" ipv4 ipv6 ];
|
||||||
in
|
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 {
|
flake.nixosConfigurations."${hostname}" = inputs.nixpkgs.lib.nixosSystem {
|
||||||
modules = with inputs.self.modules; [
|
modules = with inputs.self.modules; [
|
||||||
nixos.lxc
|
nixos.lxc
|
||||||
nixos.mysops
|
nixos.login-text
|
||||||
nixos.step-ssh-host
|
|
||||||
nixos.janus-ca
|
|
||||||
inputs.home-manager.nixosModules.home-manager
|
inputs.home-manager.nixosModules.home-manager
|
||||||
nixos."${username}"
|
nixos."${username}"
|
||||||
nixos.docker
|
nixos.ssh-new
|
||||||
nixos.login-text
|
nixos.mysops
|
||||||
|
nixos.step-ca # Runs the step-ca server
|
||||||
|
nixos.step-client # Uses the step-ca server as a client
|
||||||
nixos.mtls
|
nixos.mtls
|
||||||
{
|
({ config, lib, pkgs, ... }: {
|
||||||
networking.hostName = hostname;
|
networking.hostName = hostname;
|
||||||
step-ssh-host = {
|
loginText.extraServiceStatus = {
|
||||||
hostname = hostname;
|
"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 = {
|
mtls = {
|
||||||
enable = true;
|
enable = true;
|
||||||
subject = hostname;
|
subject = hostname;
|
||||||
san = [
|
san = names;
|
||||||
"${hostname}.john-stream.com"
|
bootstrap = {
|
||||||
"192.168.1.244"
|
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=
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNpgz9xYE3K+JeQ7vtDA07iWlp0xTXB+G8MBzX6/RluXs8E6v+ahx90M093EYIOImaW6npWQ0JnFtmZaet5l9Ao=
|
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNpgz9xYE3K+JeQ7vtDA07iWlp0xTXB+G8MBzX6/RluXs8E6v+ahx90M093EYIOImaW6npWQ0JnFtmZaet5l9Ao=
|
||||||
@@ -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";
|
hostname = "john-p14s";
|
||||||
homeDirectory = config.home-manager.users.john.home.homeDirectory;
|
homeDirectory = config.home-manager.users.john.home.homeDirectory;
|
||||||
flakeDir = "${homeDirectory}/Documents/dendritic";
|
flakeDir = "${homeDirectory}/Documents/dendritic";
|
||||||
my-neovim = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.my-neovim;
|
selfPkgs = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
self.modules.nixos.base
|
||||||
self.modules.nixos.p14sHardware
|
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;
|
rebuild.flakeDir = flakeDir;
|
||||||
|
|
||||||
networking = {
|
networking.hostName = hostname;
|
||||||
hostName = hostname;
|
|
||||||
networkmanager.enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Enable automatic login for the user.
|
|
||||||
# services.displayManager.autoLogin.enable = true;
|
|
||||||
# services.displayManager.autoLogin.user = "john";
|
|
||||||
|
|
||||||
programs.zsh.enable = true;
|
programs.zsh.enable = true;
|
||||||
services.openssh.enable = true;
|
|
||||||
services.tailscale.enable = true;
|
services.tailscale.enable = true;
|
||||||
|
|
||||||
# List packages installed in system profile. To search, run:
|
# List packages installed in system profile. To search, run:
|
||||||
# $ nix search wget
|
# $ nix search wget
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
wget
|
|
||||||
cacert
|
|
||||||
busybox
|
|
||||||
dig
|
|
||||||
samba
|
samba
|
||||||
my-neovim
|
selfPkgs.my-neovim
|
||||||
|
selfPkgs.wg-platform
|
||||||
|
selfPkgs.jsl-zsh
|
||||||
];
|
];
|
||||||
|
|
||||||
security.pam.services.swaylock = {};
|
security.pam.services.swaylock = {};
|
||||||
security.pam.services.swaylock.fprintAuth = true;
|
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
|
# This value determines the NixOS release from which the default
|
||||||
# settings for stateful data, like file locations and database versions
|
# settings for stateful data, like file locations and database versions
|
||||||
# on your system were taken. It's perfectly fine and recommended to leave
|
# 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).
|
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
|
||||||
system.stateVersion = "24.05"; # Did you read the comment?
|
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.libinput.enable = true; # Enable touchpad support (enabled default in most desktopManager).
|
||||||
services.fprintd.enable = true; # Enables fingerprint sensor
|
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 = {
|
home-manager.users.root = {
|
||||||
imports = with inputs.self.modules.homeManager; [
|
imports = with inputs.self.modules.homeManager; [
|
||||||
@@ -115,19 +56,12 @@
|
|||||||
gnome
|
gnome
|
||||||
desktop
|
desktop
|
||||||
mysops
|
mysops
|
||||||
|
step-client
|
||||||
rebuild
|
rebuild
|
||||||
{
|
{
|
||||||
my-vscode.enable = true;
|
my-vscode.enable = true;
|
||||||
mysops.hostSecretFile = "${flakeDir}/modules/hosts/john-p14s/secrets.yaml";
|
mysops.hostSecretFile = "${flakeDir}/modules/hosts/john-p14s/secrets.yaml";
|
||||||
homeManagerFlakeDir = "${flakeDir}";
|
homeManagerFlakeDir = "${flakeDir}";
|
||||||
shell.program = "zsh";
|
|
||||||
home.packages = with pkgs; [
|
|
||||||
bash
|
|
||||||
discord
|
|
||||||
my-neovim
|
|
||||||
proton-vpn
|
|
||||||
joplin-desktop
|
|
||||||
];
|
|
||||||
ssh.certificates.enable = true;
|
ssh.certificates.enable = true;
|
||||||
ssh.matchSets = {
|
ssh.matchSets = {
|
||||||
appdaemon = true;
|
appdaemon = true;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
"${inputs.nixos-hardware}/lenovo/thinkpad/p14s/amd/gen4"
|
"${inputs.nixos-hardware}/lenovo/thinkpad/p14s/amd/gen4"
|
||||||
] ++ (with self.modules.nixos; [
|
] ++ (with self.modules.nixos; [
|
||||||
p14sConfiguration
|
p14sConfiguration
|
||||||
janus-ca
|
|
||||||
rebuild
|
rebuild
|
||||||
sudo
|
sudo
|
||||||
john
|
john
|
||||||
@@ -20,6 +19,7 @@
|
|||||||
steam
|
steam
|
||||||
wireguard
|
wireguard
|
||||||
mtls
|
mtls
|
||||||
|
step-client
|
||||||
# greetd
|
# greetd
|
||||||
# niri
|
# niri
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -3,11 +3,8 @@ let
|
|||||||
username = "john";
|
username = "john";
|
||||||
hostname = "john-pc-ubuntu";
|
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
|
testHost = "soteria"; # which host to test build
|
||||||
testTarget = "test-nix";
|
testTarget = "test-nix";
|
||||||
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
flake.modules.homeManager."${hostname}" = { config, pkgs, lib, ... }:
|
flake.modules.homeManager."${hostname}" = { config, pkgs, lib, ... }:
|
||||||
@@ -15,23 +12,20 @@ in
|
|||||||
selfPkgs = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
selfPkgs = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||||
resticPasswordFile = "${config.xdg.configHome}/restic/password.txt";
|
resticPasswordFile = "${config.xdg.configHome}/restic/password.txt";
|
||||||
flakeDir = "${config.xdg.configHome}/home-manager/jsl-dendritic";
|
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
|
in
|
||||||
{
|
{
|
||||||
imports = with inputs.self.modules.homeManager; [
|
imports = with inputs.self.modules.homeManager; [
|
||||||
rebuild
|
rebuild
|
||||||
john
|
john
|
||||||
mysops
|
|
||||||
janus-ca
|
|
||||||
step-ssh-user
|
|
||||||
mtls
|
mtls
|
||||||
restic
|
restic
|
||||||
docker
|
docker
|
||||||
desktop
|
desktop
|
||||||
|
step-client
|
||||||
|
mysops
|
||||||
|
# zed-editor
|
||||||
|
# myPackage
|
||||||
|
# myStepClient
|
||||||
];
|
];
|
||||||
# TODO: make this more restrictive, rather than allowing all unfree packages
|
# TODO: make this more restrictive, rather than allowing all unfree packages
|
||||||
nixpkgs.config.allowUnfree = true;
|
nixpkgs.config.allowUnfree = true;
|
||||||
@@ -42,36 +36,29 @@ in
|
|||||||
home.username = "${username}";
|
home.username = "${username}";
|
||||||
home.homeDirectory = "/home/${username}";
|
home.homeDirectory = "/home/${username}";
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
nixos-rebuild
|
nil # Nix language server
|
||||||
test-push
|
selfPkgs.jsl-zsh
|
||||||
selfPkgs.neovim-min
|
(inputs.self.wrappers.test-push.apply {
|
||||||
# ${selfPkgs}.my-neovim
|
inherit pkgs flakeDir;
|
||||||
# selfPkgs.richPrinter
|
host = testHost;
|
||||||
selfPkgs.janus-ca
|
target = testTarget;
|
||||||
|
}).wrapper
|
||||||
];
|
];
|
||||||
|
|
||||||
shell.program = "zsh";
|
|
||||||
|
|
||||||
homeManagerFlakeDir = flakeDir;
|
homeManagerFlakeDir = flakeDir;
|
||||||
docker.enable = true;
|
docker.enable = true;
|
||||||
|
ssh-new = {
|
||||||
step-ssh-user = {
|
|
||||||
enable = true;
|
|
||||||
principals = ["root" "${username}" "appdaemon"];
|
|
||||||
provisioner = "admin";
|
|
||||||
};
|
|
||||||
ssh = {
|
|
||||||
certificates.enable = true;
|
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
|
# This provides the keys at build time and will be included in the nix store
|
||||||
sops.defaultSopsFile = ../../../keys/secrets.yaml;
|
sops.defaultSopsFile = ../../../keys/secrets.yaml;
|
||||||
@@ -96,22 +83,28 @@ in
|
|||||||
"/home/john/john-nas"
|
"/home/john/john-nas"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
mtls = {
|
# mtls = {
|
||||||
enable = true;
|
# enable = true;
|
||||||
subject = hostname;
|
# subject = hostname;
|
||||||
san = [
|
# san = [
|
||||||
"${hostname}"
|
# "${hostname}"
|
||||||
"192.168.1.85"
|
# "192.168.1.85"
|
||||||
"spiffe://john-stream.com/ubuntu"
|
# "spiffe://john-stream.com/ubuntu"
|
||||||
];
|
# ];
|
||||||
lifetime = "1h";
|
# lifetime = "1h";
|
||||||
renew.onCalendar = "*:1/10";
|
# renew.onCalendar = "*:1/10";
|
||||||
};
|
# };
|
||||||
};
|
};
|
||||||
|
|
||||||
flake.homeConfigurations."john@john-pc-ubuntu" = withSystem "x86_64-linux" (ctx@{ config, inputs', ...}:
|
flake.homeConfigurations."john@john-pc-ubuntu" = inputs.home-manager.lib.homeManagerConfiguration {
|
||||||
inputs.home-manager.lib.homeManagerConfiguration {
|
pkgs = import inputs.nixpkgs.outPath {
|
||||||
pkgs = inputs'.nixpkgs.legacyPackages;
|
localSystem.system = "x86_64-linux";
|
||||||
modules = [ inputs.self.modules.homeManager."${hostname}" ];
|
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:
|
forgejo:
|
||||||
#ENC[AES256_GCM,data:/wtm0uXbiWFoGNWtlTzVuNxBR7CPm2FMB98t3AxSj5V5rltLvzF9BjgWoJCiX3ltzmU=,iv:xaZXbUIGJHxPrLRQzEQI7hgRgc0y061jIhoE3zlcMaA=,tag:fQGheCIdBKm6wE+vtj7g6A==,type:comment]
|
#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]
|
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]
|
restic_password: ENC[AES256_GCM,data:u7QOZXJkxVG4J75K5nphb2uJGdz6jbWuVSsKKu+41fshp7cVoRijtr/Cs02LjVse,iv:bt1W2FeBTG6ypBFYzMPXPIkYTSn0uHURY2ui6MRgYY8=,tag:DObAMws/zQcM+UKUe9EECA==,type:str]
|
||||||
sops:
|
sops:
|
||||||
age:
|
age:
|
||||||
- recipient: age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
|
- enc: |
|
||||||
enc: |
|
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5QThHOFdVQ0F6ZDE3QXdL
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLM3NML2ozWExSTlY3ZXJH
|
||||||
QVF0WW5yRkNtNXIyRm1LeEhwY2dRRWo0WENZCmZoWmlXZDh4ZGlUR2JYOUxuaVAx
|
ZEpaN1RIOWVjNXRRQTlLQVZKTUUvZnBxREd3Cmp6b29aRDlUdDFwMVhwS2thODNO
|
||||||
WjhXVHBMNWdrdkgvTENJR2RpWldkNTAKLS0tIEF1bS9JSnFTbytBa1U5RjVzWkd3
|
QzNWSVIrak1uMGRuUlBONkR2aW1nbXMKLS0tIDh2YmFaRXBvZ0Q2blhjTVphL3J3
|
||||||
S0pKVVI2RFN5a1BMYXBEY3VOUUM5QTAKNarYZm9DKRQhosSJBn9yryFxDkmFTV/o
|
TkROaHdwWWN4YnkyczZLbDJYRTNrQTQKvHSXjP9EZkq9DrPZZHTIlFrPUhez6jZT
|
||||||
i8b/tZ1ZybjEXX+X1EsgylM5u8iKkbEyUzqKO0E0gpg4qXSsJaMMYQ==
|
LJvkmQgKluer7+lWr8XuuqtQsvhtef1di82SBG6C6YK0zWIxrMjFkA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1h0prahyukq4l564yqwgcpg3g6gdrjflk0suklussjjrjstxd9uesws8633
|
recipient: age1p4gyh5260ewp7t2ctzv5ewj4a06szl389fm4gzy6vltxhjj73ers87u33g
|
||||||
enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhdEN2M1lkMFoyQzg3bCsz
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwWThXZGtFSjFZVU1UWG9v
|
||||||
cVB0NUlkV1JPRjQ1TW5RSVFHUjdNMERoRXlzCk9tc25iMG00TkZjWjk2S2cyVE1J
|
Wk9sVEVWemtVWTJ6TGlOd3ZhVUZBYnJ1OFFRClpFNmEwTG1PR0pQUFJuTTVVZGRD
|
||||||
cUFITDh2SkZGN0lpeWJVcWM3V3JrTTQKLS0tIENOWVBzTFYrdHRsMERwV0RKS01j
|
bnpPdjZGVUJvS0NyWGJQbEV4cUlHZncKLS0tIDFNU1VxNjZYSks1OTMrblJPbDI1
|
||||||
QVI5TVl4L0dwOWpoQ1I2THZ6cWovWGMKIo1x1ZbdTyr/dNlPhvuomfk2MoPLsHyU
|
bHIrUnhsMWNhWVpVc3hCazlKTlV5YncKgR/AbgCE9JU8U8PH3ouYTZJOqL7rRODr
|
||||||
N3CP3Scu3aZ7vqVua7uwtv4xQqyQI/yOnFjwxrVYPJ+N5Y/b4wsC6A==
|
5D2zT9pcTC3LQGZrhNtgjAi6IucU4yDAU7UhT3D7cZawwFTKVaoplw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
|
recipient: age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
|
||||||
enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGSGQvL2ZYM2dtV1JWaDR1
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3K054SHE0V1RCYlRlTXJT
|
||||||
RjJnN0YwWFM4d1IwTElqUnZQQkVDTmdydUJjCjdCUWxCVDNXUHVLd2ZHcDA5L0Mw
|
bXo4ZnE1cFozY3R2dmg5S1BacC9xb2oweWpBClJJVnkrSytFaG8xR1l4SU5FNGxi
|
||||||
cEVxVE84L2xmQnRoYmsxVEdMSEZjdGcKLS0tIHViRVJyTWt4Q3JrMWtYaS9QdHlS
|
UndnNzYwMUlXSjRYK3c3MzI4NmRVRVEKLS0tIFQ4NFdPeGI4LytWK0JPcUV4VjM2
|
||||||
Yjd0MUcxcExvWVpCOUR3MkdZdGQyWUkKnru0Y2A98+0Mps7EtVK7ct3vPqIGveUt
|
b1dtYWdFTTFQcW5PQml2OWs1Z0VZOFUKrDX7y3cU8KPRLKlO/HI/jdynqwO1TAS0
|
||||||
E5fzpcKvdefzObrx7BPTwJ19t2fZg/dSi7HKwx3vmKZSzyQaqJOzsg==
|
WY4F0qFKmWwsJJjbnjdpYYP3O1/8RNeXjnUg8fGYY75sf7iAYWSe/w==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
recipient: age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
|
||||||
lastmodified: "2026-04-04T23:18:43Z"
|
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]
|
mac: ENC[AES256_GCM,data:qBgeli5lHb4pyA8nAADBuRBAaq8VbAIsFI37OZtgnbnoHW2crxo3YC+EknaIYnZpZ48kwVhQS5lGRjI6JsWWhTH3+LVAhTmS2Qj/pZTD/JDLK6XJGXS4U9nB7m9aGYyW8gFCy9/DfoJWGsS//+ZmUikKPfd5kMZgh1zGoYCIGug=,iv:h+2fA+bO2SMCNrEslP36x3BPRaIy25cU/DNX8CYSC6A=,tag:RvVyUZ4ONRaKaqGiT31eUQ==,type:str]
|
||||||
unencrypted_suffix: _unencrypted
|
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, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
flakeDir = config.homeManagerFlakeDir;
|
flakeDir = config.homeManagerFlakeDir;
|
||||||
hostnameCmd = "$(${lib.getExe pkgs.hostname} -s)";
|
|
||||||
|
|
||||||
flake-parts-check = with pkgs; writeShellApplication {
|
flake-parts-check = with pkgs; writeShellApplication {
|
||||||
name = "flake-parts-check";
|
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 {
|
test-build = with pkgs; writeShellApplication {
|
||||||
name = "test-build";
|
name = "test-build";
|
||||||
runtimeInputs = [ coreutils nix hostname ];
|
runtimeInputs = [ coreutils nix hostname ];
|
||||||
@@ -81,24 +64,6 @@
|
|||||||
nix eval "${flakeDir}#nixosConfigurations.$HOSTNAME.config.system.build.toplevel.drvPath"
|
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
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
@@ -120,13 +85,64 @@
|
|||||||
name = "build-tools";
|
name = "build-tools";
|
||||||
paths = [
|
paths = [
|
||||||
flake-parts-check
|
flake-parts-check
|
||||||
nhms
|
|
||||||
nhmu
|
|
||||||
test-build
|
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
|
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}" = {
|
users.users."${username}" = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
|
group = username;
|
||||||
home = "/home/${username}";
|
home = "/home/${username}";
|
||||||
shell = lib.mkIf config.programs.zsh.enable pkgs.zsh;
|
shell = pkgs.zsh;
|
||||||
extraGroups = [ "input" "networkmanager" ]
|
extraGroups = [ "input" "networkmanager" "video" "render" ]
|
||||||
++ lib.optional isAdmin "wheel"
|
++ lib.optional isAdmin "wheel"
|
||||||
++ lib.optional config.virtualisation.docker.enable "docker"
|
++ lib.optional config.virtualisation.docker.enable "docker"
|
||||||
++ lib.optional (isAdmin && config.services.forgejo.enable) config.services.forgejo.group
|
++ lib.optional (isAdmin && config.services.forgejo.enable) config.services.forgejo.group
|
||||||
++ lib.optional (isAdmin && config.services.postgresql.enable) "postgres";
|
++ lib.optional (isAdmin && config.services.postgresql.enable) "postgres";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
programs.zsh.enable = true;
|
||||||
|
|
||||||
security.sudo-rs.enable = lib.mkIf isAdmin true;
|
security.sudo-rs.enable = lib.mkIf isAdmin true;
|
||||||
|
|
||||||
home-manager.useGlobalPkgs = true;
|
home-manager.useGlobalPkgs = true;
|
||||||
@@ -33,7 +43,9 @@
|
|||||||
imports = [ self.modules.homeManager."${username}" ];
|
imports = [ self.modules.homeManager."${username}" ];
|
||||||
home.username = "${username}";
|
home.username = "${username}";
|
||||||
home.homeDirectory = "/home/${username}";
|
home.homeDirectory = "/home/${username}";
|
||||||
# home.packages = homePackages;
|
home.packages = with pkgs; [
|
||||||
|
# fzf zoxide starship
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{ self, inputs, ... }: {
|
{ self, inputs, ... }: {
|
||||||
flake-file.inputs = {
|
config.flake-file.inputs = {
|
||||||
wrapper-modules = {
|
wrapper-modules = {
|
||||||
url = "github:BirdeeHub/nix-wrapper-modules";
|
url = "github:BirdeeHub/nix-wrapper-modules";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
@@ -9,4 +9,13 @@
|
|||||||
inputs.nixpkgs.follows = "nixpkgs";
|
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, ... }:
|
flake.modules.nixos.login-text = { config, lib, ... }:
|
||||||
let
|
let
|
||||||
defaultServiceStatus = {
|
defaultServiceStatus = {
|
||||||
SSH = "sshd.socket";
|
"SSH Socket" = "sshd.socket";
|
||||||
"SSH Cert Renewal" = "step-ssh-host-renew.timer";
|
"SSH Cert Renewal" = "ssh-certs-renew.timer";
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
options.loginText.extraServiceStatus = lib.mkOption {
|
options.loginText.extraServiceStatus = lib.mkOption {
|
||||||
|
|||||||
+31
-17
@@ -1,24 +1,38 @@
|
|||||||
{ inputs, ... }:
|
{ inputs, ... }:
|
||||||
{
|
{
|
||||||
flake.modules.nixos.lxc = { pkgs, lib, ...}: {
|
flake.modules.nixos.lxc = { pkgs, lib, ...}:
|
||||||
imports = with inputs.self.modules.nixos; [
|
let
|
||||||
({ modulesPath, ... }: { imports = [ "${modulesPath}/virtualisation/proxmox-lxc.nix" ]; })
|
selfPackages = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||||
];
|
rootShellPath = lib.getExe' selfPackages.jsl-zsh-tty "jsl-zsh-tty";
|
||||||
nixpkgs.hostPlatform = lib.mkForce "x86_64-linux";
|
in
|
||||||
system.stateVersion = "25.11";
|
{
|
||||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
imports = with inputs.self.modules.nixos; [
|
||||||
environment.systemPackages = with pkgs; [ git zsh ];
|
({ 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
|
||||||
|
selfPackages.jsl-zsh-tty
|
||||||
|
];
|
||||||
|
environment.shells = lib.mkAfter [ rootShellPath ];
|
||||||
|
users.users.root.shell = lib.mkForce rootShellPath;
|
||||||
|
|
||||||
# security.sudo-rs.enable = true;
|
networking.nameservers = [ "192.168.1.150" ];
|
||||||
programs.nix-ld.enable = true;
|
networking.dhcpcd.extraConfig = "nohook resolv.conf";
|
||||||
nix.optimise.automatic = true;
|
|
||||||
nix.gc = {
|
# security.sudo-rs.enable = true;
|
||||||
automatic = true;
|
programs.nix-ld.enable = true;
|
||||||
dates = "weekly";
|
nix.optimise.automatic = true;
|
||||||
options = "--delete-older-than 30d";
|
nix.gc = {
|
||||||
|
automatic = true;
|
||||||
|
dates = "weekly";
|
||||||
|
options = "--delete-older-than 30d";
|
||||||
|
};
|
||||||
|
programs.bash.enable = true;
|
||||||
};
|
};
|
||||||
programs.bash.enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Generic bootstrapping lxc, use a specific host file for more
|
# Generic bootstrapping lxc, use a specific host file for more
|
||||||
flake.nixosConfigurations.lxc = inputs.nixpkgs.lib.nixosSystem {
|
flake.nixosConfigurations.lxc = inputs.nixpkgs.lib.nixosSystem {
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
{
|
{
|
||||||
flake.modules.homeManager.bash = { pkgs, lib, config, ... }:
|
flake.modules.homeManager.bash = { pkgs, lib, config, ... }:
|
||||||
{
|
{
|
||||||
programs.bash = lib.mkIf (config.shell.program == "bash") {
|
programs.bash = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableCompletion = true;
|
enableCompletion = true;
|
||||||
package = pkgs.bash;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -5,19 +5,27 @@
|
|||||||
enable = true;
|
enable = true;
|
||||||
enableBashIntegration = true;
|
enableBashIntegration = true;
|
||||||
enableZshIntegration = true;
|
enableZshIntegration = true;
|
||||||
git = true;
|
package = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.my-eza;
|
||||||
icons = "auto";
|
};
|
||||||
colors = "auto";
|
};
|
||||||
extraOptions = [
|
|
||||||
|
perSystem = { system, pkgs, ... }: {
|
||||||
|
packages.my-eza = inputs.wrappers.lib.wrapPackage {
|
||||||
|
inherit pkgs;
|
||||||
|
package = pkgs.eza;
|
||||||
|
args = [
|
||||||
"--all"
|
"--all"
|
||||||
"--long"
|
"--long"
|
||||||
"--group-directories-first"
|
"--group-directories-first"
|
||||||
|
"--icons=auto"
|
||||||
|
"--color=auto"
|
||||||
"--sort=type"
|
"--sort=type"
|
||||||
"--dereference"
|
"--dereference"
|
||||||
"--octal-permissions"
|
"--octal-permissions"
|
||||||
"--smart-group"
|
"--smart-group"
|
||||||
"--no-time"
|
"--no-time"
|
||||||
"--git"
|
"--git"
|
||||||
|
"$@"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,12 +14,5 @@
|
|||||||
ignorecase = true;
|
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, ... }:
|
flake.modules.homeManager.ghostty = { config, pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
ghosttyPackage = pkgs.symlinkJoin {
|
ghosttyX11 = pkgs.symlinkJoin {
|
||||||
name = "ghostty-x11";
|
name = "ghostty-x11";
|
||||||
paths = [ (config.lib.nixGL.wrap pkgs.ghostty) ];
|
paths = [ (config.lib.nixGL.wrap pkgs.ghostty) ];
|
||||||
nativeBuildInputs = [ pkgs.makeWrapper ];
|
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||||
@@ -24,6 +24,10 @@
|
|||||||
TERMINAL = "ghostty";
|
TERMINAL = "ghostty";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
home.packages = with pkgs; [
|
||||||
|
inputs.self.packages.${system}.ghosttyGPUCheck
|
||||||
|
];
|
||||||
|
|
||||||
targets.genericLinux.nixGL = {
|
targets.genericLinux.nixGL = {
|
||||||
packages = inputs.nixgl.packages.${pkgs.stdenv.hostPlatform.system};
|
packages = inputs.nixgl.packages.${pkgs.stdenv.hostPlatform.system};
|
||||||
defaultWrapper = "mesa";
|
defaultWrapper = "mesa";
|
||||||
@@ -33,9 +37,9 @@
|
|||||||
programs.ghostty = {
|
programs.ghostty = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableZshIntegration = true;
|
enableZshIntegration = true;
|
||||||
package = ghosttyPackage;
|
package = ghosttyX11;
|
||||||
settings = {
|
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-size = 12;
|
||||||
font-family = "Source Code Pro";
|
font-family = "Source Code Pro";
|
||||||
theme = "Catppuccin Mocha";
|
theme = "Catppuccin Mocha";
|
||||||
@@ -72,7 +76,7 @@
|
|||||||
# https://github.com/ghostty-org/ghostty/discussions/3763#discussioncomment-11699970
|
# https://github.com/ghostty-org/ghostty/discussions/3763#discussioncomment-11699970
|
||||||
xdg.desktopEntries."com.mitchellh.ghostty" =
|
xdg.desktopEntries."com.mitchellh.ghostty" =
|
||||||
let
|
let
|
||||||
ghosttyCmd = lib.getExe ghosttyPackage;
|
ghosttyCmd = lib.getExe ghosttyX11;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
name = "Ghostty";
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+160
-135
@@ -6,153 +6,178 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
perSystem = { system, pkgs, config, ... }: {
|
perSystem = { system, pkgs, config, ... }:
|
||||||
packages.my-neovim = ((inputs.nvf.lib.neovimConfiguration {
|
let
|
||||||
inherit pkgs;
|
commonNeovimModule = {
|
||||||
modules = [
|
config.vim = {
|
||||||
{
|
options = {
|
||||||
# https://nvf.notashelf.dev/search.html
|
number = true;
|
||||||
config.vim = {
|
relativenumber = true;
|
||||||
options = {
|
expandtab = true;
|
||||||
number = true;
|
shiftwidth = 4;
|
||||||
relativenumber = true;
|
tabstop = 4;
|
||||||
expandtab = true;
|
softtabstop = 4;
|
||||||
shiftwidth = 4;
|
|
||||||
tabstop = 4;
|
|
||||||
softtabstop = 4;
|
|
||||||
|
|
||||||
wrap = true;
|
wrap = true;
|
||||||
linebreak = true;
|
linebreak = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
syntaxHighlighting = true;
|
syntaxHighlighting = true;
|
||||||
|
|
||||||
# Enable custom theming options
|
theme.enable = true;
|
||||||
theme.enable = true;
|
theme.name = "catppuccin";
|
||||||
theme.name = "catppuccin";
|
theme.style = "mocha";
|
||||||
theme.style = "mocha";
|
|
||||||
|
|
||||||
git.enable = true;
|
git.enable = true;
|
||||||
# git.neogit.enable = true;
|
# git.neogit.enable = true;
|
||||||
|
|
||||||
# https://github.com/akinsho/toggleterm.nvim
|
# https://github.com/akinsho/toggleterm.nvim
|
||||||
terminal.toggleterm.enable = true;
|
terminal.toggleterm = {
|
||||||
terminal.toggleterm.lazygit.enable = true;
|
enable = true;
|
||||||
terminal.toggleterm.lazygit.direction = "float";
|
lazygit = {
|
||||||
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 = {
|
|
||||||
enable = true;
|
enable = true;
|
||||||
|
package = pkgs.lazygit;
|
||||||
|
direction = "float";
|
||||||
|
mappings.open = "<C-g>";
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# lazy = {
|
languages = {
|
||||||
# enable = true;
|
enableTreesitter = true;
|
||||||
# };
|
enableFormat = true;
|
||||||
|
|
||||||
# globals = {
|
nix.enable = true;
|
||||||
# SimpylFold_docstring_preview = 1;
|
yaml.enable = true;
|
||||||
# SimpylFold_fold_blank = 0;
|
toml.enable = true;
|
||||||
# };
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# extraPlugins = with pkgs.vimPlugins; {
|
telescopeModule = {
|
||||||
# SimpylFold.package = SimpylFold;
|
config.vim = {
|
||||||
# };
|
telescope = {
|
||||||
|
enable = true;
|
||||||
telescope = {
|
extensions = [
|
||||||
enable = true;
|
{
|
||||||
extensions = [
|
name = "fzf";
|
||||||
{
|
packages = [ pkgs.vimPlugins.telescope-fzf-native-nvim ];
|
||||||
name = "fzf";
|
setup = {
|
||||||
packages = [pkgs.vimPlugins.telescope-fzf-native-nvim];
|
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 = "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>";
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
|
||||||
];
|
keymaps = [
|
||||||
}).neovim).overrideAttrs (old: {
|
{
|
||||||
pname = "my-neovim";
|
desc = "Key Maps [Telescope]";
|
||||||
version = "custom";
|
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";
|
||||||
|
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>";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key = "<C-`>";
|
||||||
|
mode = [ "n" "v" "t" ];
|
||||||
|
silent = false;
|
||||||
|
action = "<cmd>:ToggleTerm<CR>";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
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 = "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, ... }: {
|
{ 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, ... }: {
|
flake.modules.homeManager.onepassword = { config, ... }: {
|
||||||
home.file.".config/1Password/ssh/agent.toml".text = ''
|
home.file.".config/1Password/ssh/agent.toml".text = ''
|
||||||
# https://developer.1password.com/docs/ssh/agent/config
|
# https://developer.1password.com/docs/ssh/agent/config
|
||||||
[[ssh-keys]]
|
[[ssh-keys]]
|
||||||
vault = "Private"
|
vault = "Private"
|
||||||
'';
|
'';
|
||||||
programs.ssh.matchBlocks."*".identityAgent = "${config.home.homeDirectory}/.1password/agent.sock";
|
|
||||||
|
programs.ssh.settings."*" = {
|
||||||
|
IdentityAgent = "${config.home.homeDirectory}/.1password/agent.sock";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+73
-72
@@ -1,4 +1,4 @@
|
|||||||
{ inputs, ... }:
|
{ self, inputs, ... }:
|
||||||
let
|
let
|
||||||
inputs' = inputs; # save a reference before it's shadowed
|
inputs' = inputs; # save a reference before it's shadowed
|
||||||
in
|
in
|
||||||
@@ -14,84 +14,85 @@ in
|
|||||||
imports = [ inputs.sops-nix.nixosModules.sops ];
|
imports = [ inputs.sops-nix.nixosModules.sops ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# Define the homeModules that are used by flake-parts
|
flake.modules.homeManager.mysops =
|
||||||
# https://flake.parts/options/home-manager.html#opt-flake.modules.homeManager
|
{ config, pkgs, lib, ... }:
|
||||||
flake.modules.homeManager.mysops = { inputs, 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 = [
|
|
||||||
# This import makes the sops config attribute available below
|
|
||||||
inputs'.sops-nix.homeManagerModules.sops
|
|
||||||
];
|
|
||||||
|
|
||||||
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;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config =
|
|
||||||
let
|
let
|
||||||
echo = lib.getExe' pkgs.coreutils "echo";
|
cfg = config.mysops;
|
||||||
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}
|
|
||||||
'');
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
home.packages = with pkgs; [
|
imports = [
|
||||||
eza
|
# This import makes the sops config attribute available below
|
||||||
age
|
inputs'.sops-nix.homeManagerModules.sops
|
||||||
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
|
options.mysops = {
|
||||||
${echo} "SSH identity file not found: ${config.ssh.identityFile}" >&2
|
hostSecretFile = lib.mkOption {
|
||||||
exit 1
|
description = "Path to the secrets file for this host. Used to create the edit-secrets script";
|
||||||
fi
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
if [ -e "${cfg.ageKeyFile}" ]; then
|
config =
|
||||||
${echo} "Refusing to overwrite existing age key file: ${cfg.ageKeyFile}" >&2
|
let
|
||||||
exit 1
|
identityFile = config.ssh-new.keyFile;
|
||||||
fi
|
my-sops = (inputs.self.wrappers.mySops.apply {
|
||||||
|
inherit pkgs;
|
||||||
|
sshKey = identityFile;
|
||||||
|
}).wrapper;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Option definitions for the sops home-manager module:
|
||||||
|
# https://github.com/Mic92/sops-nix/blob/master/modules/home-manager/sops.nix
|
||||||
|
sops = {
|
||||||
|
defaultSopsFile = ../../keys/secrets.yaml;
|
||||||
|
defaultSopsFormat = "yaml";
|
||||||
|
age.sshKeyPaths = [ identityFile ];
|
||||||
|
};
|
||||||
|
|
||||||
${mkdir} -p "$(${dirname} "${cfg.ageKeyFile}")"
|
home.packages = with pkgs; [
|
||||||
${lib.getExe pkgs.ssh-to-age} -i ${config.ssh.identityFile} -private-key > ${cfg.ageKeyFile}
|
my-sops
|
||||||
${echo} -n "Created ${cfg.ageKeyFile}: "
|
(inputs.wrappers.lib.wrapPackage {
|
||||||
${echo} $(${lib.getExe show-age-key})
|
binName = "ls-secrets";
|
||||||
'')
|
inherit pkgs;
|
||||||
show-age-key
|
package = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.my-eza;
|
||||||
(writeShellScriptBin "ls-secrets" "${lib.getExe pkgs.eza} -alT --follow-symlinks ~/.config/sops-nix/secrets")
|
args = [
|
||||||
] ++ editScript;
|
"-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 ];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
home.shellAliases.sops = "${sopsBin} --config ${sopsConfigPath}";
|
flake.wrappers.mySops = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
|
||||||
|
options = {
|
||||||
# Option definitions for the sops home-manager module:
|
sshKey = lib.mkOption {
|
||||||
# https://github.com/Mic92/sops-nix/blob/master/modules/home-manager/sops.nix
|
type = lib.types.str;
|
||||||
sops = {
|
description = "String path to the SSH key to use for creating an AGE key at runtime";
|
||||||
defaultSopsFile = sopsSecretsPath;
|
|
||||||
defaultSopsFormat = "yaml";
|
|
||||||
age.sshKeyPaths = [ "${config.ssh.identityFile}" ];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
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, ... }:
|
flake.modules.nixos.wireguard = { config, pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
wgInterface = "platform";
|
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
|
in
|
||||||
{
|
{
|
||||||
imports = [ inputs.sops-nix.nixosModules.sops ];
|
imports = [ inputs.sops-nix.nixosModules.sops ];
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
wireguard-tools
|
wireguard-tools # https://github.com/WireGuard/wireguard-tools
|
||||||
wg-netmanager
|
# wg-netmanager # https://github.com/gin66/wg_netmanager
|
||||||
(mkConnect "platform")
|
|
||||||
(mkDisconnect "platform")
|
|
||||||
];
|
];
|
||||||
|
|
||||||
sops.secrets.wireguard_private_key = { };
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
+123
-173
@@ -1,188 +1,138 @@
|
|||||||
{ self, inputs, ... }:
|
{ self, inputs, ... }:
|
||||||
let
|
let
|
||||||
username = "john";
|
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
|
in
|
||||||
{
|
{
|
||||||
flake.modules = {
|
flake.modules = {
|
||||||
nixos.zsh = { pkgs, ... }: {
|
nixos.zsh = { pkgs, ... }:
|
||||||
users.users."${username}".shell = pkgs.zsh;
|
let
|
||||||
programs.zsh.enable = true;
|
selfPackages = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||||
# Already being imported by the john.nix module
|
in
|
||||||
# home-manager.sharedModules = [
|
{
|
||||||
# inputs.self.modules.homeManager.zsh
|
users.users."${username}".shell = selfPackages.jsl-zsh;
|
||||||
# ];
|
programs.zsh.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
homeManager.zsh = { pkgs, config, ... }: {
|
homeManager.zsh = { pkgs, config, ... }:
|
||||||
programs.zsh = {
|
let
|
||||||
enable = true;
|
selfPackages = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||||
package = pkgs.zsh;
|
in
|
||||||
enableCompletion = true;
|
{
|
||||||
autosuggestion.enable = true;
|
programs.zsh = {
|
||||||
# syntaxHighlighting.enable = true;
|
|
||||||
initContent = "HOST=$(hostname -s)";
|
|
||||||
dotDir = "${config.xdg.configHome}/zsh";
|
|
||||||
history = {
|
|
||||||
append = true;
|
|
||||||
ignoreAllDups = true;
|
|
||||||
ignorePatterns = [
|
|
||||||
"history"
|
|
||||||
"ls"
|
|
||||||
"eza"
|
|
||||||
"clear"
|
|
||||||
];
|
|
||||||
save = 1000;
|
|
||||||
size = 1000;
|
|
||||||
share = true;
|
|
||||||
};
|
|
||||||
oh-my-zsh = {
|
|
||||||
enable = true;
|
enable = true;
|
||||||
# theme = "risto";
|
package = selfPackages.jsl-zsh;
|
||||||
theme = "agnoster";
|
dotDir = "${config.xdg.configHome}/zsh";
|
||||||
plugins = [
|
|
||||||
"sudo"
|
|
||||||
"dotenv"
|
|
||||||
"git"
|
|
||||||
"ssh"
|
|
||||||
"ssh-agent"
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
perSystem = { system, pkgs, ... }: {
|
perSystem = { config, self', pkgs, lib, ... }:
|
||||||
packages.jsl-zsh = inputs.wrapper-modules.wrappers.zsh.wrap {
|
let
|
||||||
inherit pkgs;
|
ignorePatterns = [
|
||||||
extraPackages = with pkgs; [
|
"ls" "eza" "history" "clear"
|
||||||
inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.neovim-min
|
|
||||||
btop
|
|
||||||
coreutils
|
|
||||||
curl
|
|
||||||
wget
|
|
||||||
yazi
|
|
||||||
zsh
|
|
||||||
];
|
];
|
||||||
};
|
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";
|
||||||
|
});
|
||||||
|
|
||||||
packages.neovim-min = ((inputs.nvf.lib.neovimConfiguration {
|
mkJslZsh = { binName, starshipPackage }:
|
||||||
inherit pkgs;
|
let
|
||||||
modules = [
|
loginBootstrapPath = lib.makeBinPath (integrationPackages ++ [ starshipPackage ] ++ extraToolPackages);
|
||||||
{
|
in
|
||||||
# https://nvf.notashelf.dev/search.html
|
(inputs.wrappers.wrapperModules.zsh.apply {
|
||||||
config.vim = {
|
inherit pkgs binName;
|
||||||
options = {
|
env = {
|
||||||
number = true;
|
LANG = "en_US.UTF-8";
|
||||||
relativenumber = true;
|
COLORTERM = "truecolor";
|
||||||
expandtab = true;
|
DEVENV_SHELL_TYPE = "zsh";
|
||||||
shiftwidth = 4;
|
|
||||||
tabstop = 4;
|
|
||||||
softtabstop = 4;
|
|
||||||
|
|
||||||
wrap = true;
|
|
||||||
linebreak = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
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>";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
}
|
settings = {
|
||||||
];
|
completion = {
|
||||||
}).neovim).overrideAttrs (old: {
|
enable = true;
|
||||||
pname = "neovim-min";
|
extraCompletions = true;
|
||||||
version = "custom";
|
caseInsensitive = true;
|
||||||
});
|
fuzzySearch = true;
|
||||||
};
|
};
|
||||||
}
|
autoSuggestions = {
|
||||||
|
enable = true;
|
||||||
|
strategy = [ "history" "completion" ];
|
||||||
|
};
|
||||||
|
history = {
|
||||||
|
append = true;
|
||||||
|
expanded = true;
|
||||||
|
share = true;
|
||||||
|
ignoreAllDups = true;
|
||||||
|
ignoreSpace = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
extraRC = ''
|
||||||
|
${homeEndKeyBindings}
|
||||||
|
|
||||||
|
# Login shells may reset PATH before integrations run.
|
||||||
|
export PATH=${lib.escapeShellArg loginBootstrapPath}:$PATH
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
packages.jsl-zsh = mkJslZsh {
|
||||||
|
binName = "jsl-zsh";
|
||||||
|
starshipPackage = self'.packages.starship;
|
||||||
|
};
|
||||||
|
|
||||||
|
packages.jsl-zsh-tty = mkJslZsh {
|
||||||
|
binName = "jsl-zsh-tty";
|
||||||
|
starshipPackage = self'.packages.starship-ascii;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -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, ... }: {
|
{ inputs, ... }: {
|
||||||
flake.modules.nixos.step-ssh-host = { config, pkgs, lib, ... }:
|
flake.modules.nixos.ssh-certs = { config, pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.step-ssh-host;
|
cfg = config.ssh-certs;
|
||||||
provisionerPasswordPath = config.sops.secrets."janus/admin_jwk".path;
|
wrappers = inputs.self.wrappers;
|
||||||
sshKeyPath = "/etc/ssh/ssh_host_ed25519_key";
|
sshKeyPath = "/etc/ssh/ssh_host_ed25519_key";
|
||||||
sshCertPath = "${sshKeyPath}-cert.pub";
|
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
|
in
|
||||||
{
|
{
|
||||||
# NixOS Options
|
# NixOS Options
|
||||||
options.step-ssh-host = {
|
options.ssh-certs = {
|
||||||
hostname = lib.mkOption {
|
|
||||||
description = "Networking host name to register with the CA";
|
|
||||||
type = lib.types.str;
|
|
||||||
};
|
|
||||||
provisioner = lib.mkOption {
|
provisioner = lib.mkOption {
|
||||||
description = "Provisioner inside Step CA to use for the SSH certificates";
|
description = "Provisioner inside Step CA to use for the SSH certificates";
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "admin";
|
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
|
# NixOS Config
|
||||||
config = {
|
config = {
|
||||||
ssh.certificates.enable = true;
|
# ssh.certificates.enable = true;
|
||||||
sops.secrets."janus/admin_jwk" = {
|
sops.secrets."janus/admin_jwk" = {
|
||||||
|
# Shared provisioner credential is intentionally centralized.
|
||||||
|
sopsFile = ../../../keys/secrets.yaml;
|
||||||
owner = "root";
|
owner = "root";
|
||||||
group = "root";
|
group = "root";
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
};
|
};
|
||||||
networking.nameservers = [ "192.168.1.150" ];
|
networking.nameservers = [ "192.168.1.150" ];
|
||||||
networking.dhcpcd.extraConfig = "nohook resolv.conf";
|
networking.dhcpcd.extraConfig = "nohook resolv.conf";
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = [
|
||||||
# step-cli
|
sshHostCertSign
|
||||||
(writeShellScriptBin "ssh-host-cert-renew" ''
|
sshHostCertRenew
|
||||||
${lib.getExe pkgs.step-cli} ssh certificate \
|
sshHostRenewalCheck
|
||||||
--host --sign \
|
sshHostCertCheck
|
||||||
--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}")
|
|
||||||
];
|
];
|
||||||
|
|
||||||
systemd.services.step-ssh-host-renew = {
|
systemd.services.ssh-certs-renew = {
|
||||||
description = "Renew Step SSH host certificate if needed";
|
description = "SSH host certificate renewal";
|
||||||
wantedBy = [ ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [ "network-online.target" ];
|
after = [ "network-online.target" ];
|
||||||
wants = [ "network-online.target" ];
|
wants = [ "network-online.target" ];
|
||||||
path = with pkgs; [ coreutils systemd step-cli openssh ];
|
path = with pkgs; [ step-cli systemd ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
User = "root";
|
User = "root";
|
||||||
Group = "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";
|
description = "Periodic Step SSH host certificate renewal";
|
||||||
wantedBy = [ "timers.target" ];
|
wantedBy = [ "timers.target" ];
|
||||||
|
|
||||||
timerConfig = {
|
timerConfig = {
|
||||||
OnBootSec = "5m";
|
OnBootSec = "5m";
|
||||||
OnUnitActiveSec = "4h";
|
OnUnitActiveSec = "4h";
|
||||||
RandomizedDelaySec = "15m";
|
RandomizedDelaySec = "15m";
|
||||||
Persistent = true;
|
Persistent = true;
|
||||||
Unit = "step-ssh-host-renew.service";
|
Unit = "ssh-certs-renew.service";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,25 +1,190 @@
|
|||||||
{ inputs, ... }:
|
{ inputs, ... }:
|
||||||
let
|
|
||||||
ipAddress = "0.0.0.0";
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
flake.modules.nixos.step-ca = { pkgs, ... }: {
|
flake.modules.nixos.step-ca = { config, pkgs, lib, ... }:
|
||||||
# https://github.com/NixOS/nixpkgs/blob/nixos-23.05/nixos/modules/services/security/step-ca.nix
|
let
|
||||||
services.step-ca = {
|
cfg = config.step-ca;
|
||||||
enable = true;
|
caAddress = "0.0.0.0";
|
||||||
openFirewall = true;
|
caPort = 443;
|
||||||
address = ipAddress;
|
|
||||||
port = 8443;
|
|
||||||
|
|
||||||
# https://smallstep.com/docs/step-ca/configuration/#configuration-options
|
caPasswordPath = (lib.getAttr cfg.secrets.caPassword config.sops.secrets).path;
|
||||||
settings = {
|
intermediateKeyPath = (lib.getAttr cfg.secrets.intermediateKey config.sops.secrets).path;
|
||||||
root = "";
|
sshHostCaKeyPath = (lib.getAttr cfg.secrets.sshHostCaKey config.sops.secrets).path;
|
||||||
crt = "";
|
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;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
environment.systemPackages = with pkgs; [
|
in
|
||||||
step-ca
|
{
|
||||||
step-cli
|
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 = 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 ];
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
step-ca
|
||||||
|
step-cli
|
||||||
|
];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
+37
-26
@@ -1,6 +1,10 @@
|
|||||||
{ self, inputs, lib, ... }:
|
{ self, inputs, ... }:
|
||||||
let
|
let
|
||||||
username = "john";
|
username = "john";
|
||||||
|
baseUserModules = self.factory.user {
|
||||||
|
username = username;
|
||||||
|
isAdmin = true;
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
flake.meta.users."${username}" = {
|
flake.meta.users."${username}" = {
|
||||||
@@ -8,41 +12,48 @@ in
|
|||||||
name = "John Lancaster";
|
name = "John Lancaster";
|
||||||
inherit username;
|
inherit username;
|
||||||
key = "";
|
key = "";
|
||||||
keygrip = [
|
keygrip = [ ];
|
||||||
];
|
|
||||||
authorizedKeys = [
|
authorizedKeys = [
|
||||||
# "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIAUa4dcg1TWc4pW++uodyhX4eOqrX/QYIxFWtEP7HFJ john@john-pc-ubuntu"
|
# Shared keys for every host can go here.
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMOkGLo4N/L3RYvaIZ1FmePlxa1HK0fMciZxKtRhN58F root@janus"
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIAUa4dcg1TWc4pW++uodyhX4eOqrX/QYIxFWtEP7HFJ john@john-pc-ubuntu"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
flake.modules = lib.mkMerge [
|
flake.modules = {
|
||||||
(self.factory.user {
|
nixos."${username}" = { config, pkgs, ... }: {
|
||||||
username = username;
|
imports = [
|
||||||
isAdmin = true;
|
baseUserModules.nixos."${username}"
|
||||||
})
|
];
|
||||||
{
|
users.users."${username}" = {
|
||||||
nixos."${username}" = {
|
openssh.authorizedKeys.keys = inputs.self.meta.users."${username}".authorizedKeys;
|
||||||
imports = [
|
|
||||||
inputs.home-manager.nixosModules.home-manager
|
|
||||||
];
|
|
||||||
users.users."${username}" = {
|
|
||||||
openssh.authorizedKeys.keys = inputs.self.meta.users."${username}".authorizedKeys;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# This module will be imported by the user factory
|
# 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";
|
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; [
|
imports = with inputs.self.modules.homeManager; [
|
||||||
ssh
|
ssh-new
|
||||||
shell-tools
|
shell-tools
|
||||||
git
|
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