Rekey secrets for green generation nodes

This commit is contained in:
Adriano Caloiaro 2026-03-31 21:55:28 -06:00
parent 43e03343b9
commit d7317fbb58
No known key found for this signature in database
48 changed files with 1097 additions and 215 deletions

View file

@ -138,5 +138,5 @@ Each module has detailed documentation:
- **Colmena**: NixOS deployment tool
- **Nomad**: Workload orchestration
- **Tailscale**: Zero-config VPN
- **agenix**: Age-based secrets management
- **agenix-rekey**: Automated age-based secrets management

235
SECRETS_MANAGEMENT.md Normal file
View file

@ -0,0 +1,235 @@
# Secrets Management with agenix-rekey
## Overview
NCM uses [agenix-rekey](https://github.com/oddlama/agenix-rekey) for automated secrets management. This eliminates the need for manually maintaining per-host `secrets.nix` files and automatically handles re-encryption when host keys change.
## Architecture
### Central Secret Storage
```
secrets/
├── master/ # Source secrets (encrypted with master key)
│ ├── tailscale_auth_key
│ └── linode_cli_pat
└── rekeyed/ # Per-host encrypted secrets (auto-generated)
├── ncm-0/
│ ├── <hash>-tailscale_auth_key.age
│ └── <hash>-linode_cli_pat.age
├── ncm-1/
│ └── ...
└── ...
```
### Master Key
- Location: `~/.ssh/id_rsa_agenix`
- Purpose: Decrypt master secrets and re-encrypt for hosts
- Never deployed to servers
### Host Keys
Each host has a unique age public key derived from its SSH ed25519 host key:
```nix
# In operate/hosts/ncm-X/default.nix
age.rekey.hostPubkey = "age1...";
```
## How It Works
1. **Master secrets** are encrypted with the master key and stored in `secrets/master/`
2. **agenix-rekey** reads each host's `hostPubkey` from NixOS configurations
3. For each host, it:
- Decrypts the master secret using the master key
- Re-encrypts it with the host's public key
- Stores the result in `secrets/rekeyed/<hostname>/`
4. During deployment, NixOS uses the host's private key to decrypt its secrets
## Common Operations
### Edit a Secret
```bash
# Edit the master copy
cd secrets/master/
agenix -e tailscale_auth_key -i ~/.ssh/id_rsa_agenix
# Rekey for all hosts
nix run .#agenix-rekey.x86_64-linux.rekey
# Commit the changes
git add secrets/
git commit -m "Update tailscale_auth_key"
```
### Add a New Secret
```bash
# Create the master secret
cd secrets/master/
echo "secret-value" | agenix -e new_secret -i ~/.ssh/id_rsa_agenix
# Add to git (required for agenix-rekey to see it)
git add new_secret
# Add to host configurations
# Edit operate/hosts/*/default.nix:
age.secrets.new_secret = {
rekeyFile = ../../../secrets/master/new_secret;
};
# Rekey for all hosts
nix run .#agenix-rekey.x86_64-linux.rekey
# Commit everything
git add secrets/ operate/hosts/
git commit -m "Add new_secret"
```
### Handle Host Key Changes
When a host is recreated and gets a new SSH host key:
```bash
# 1. Scan the new SSH key
ssh-keyscan <host-ip> | grep ssh-ed25519
# 2. Convert to age format
echo "ssh-ed25519 AAAA..." | nix-shell -p ssh-to-age --run "ssh-to-age"
# 3. Update the host configuration
# Edit operate/hosts/<hostname>/default.nix:
age.rekey.hostPubkey = "age1..."; # New key
# 4. Rekey secrets
nix run .#agenix-rekey.x86_64-linux.rekey
# 5. Commit and deploy
git add secrets/rekeyed/ operate/hosts/<hostname>/default.nix
git commit -m "Update <hostname> host key"
colmena apply --on <hostname> --impure
```
The upgrade script (`nomad-1.11-upgrade.sh`) automates this process.
## Configuration
### Host Configuration
Each host needs:
```nix
{
# Shared configuration (from rekey.nix)
imports = [
../rekey.nix # Provides masterIdentities
# ...
];
# Host-specific configuration
age.rekey.storageMode = "local";
age.rekey.hostPubkey = "age1..."; # From ssh-to-age
age.rekey.localStorageDir = ../../../secrets/rekeyed/ncm-X;
# Secrets this host needs
age.secrets.tailscale_auth_key = {
rekeyFile = ../../../secrets/master/tailscale_auth_key;
};
age.secrets.linode_cli_pat = {
rekeyFile = ../../../secrets/master/linode_cli_pat;
};
}
```
### Shared Configuration
`operate/hosts/rekey.nix` provides the master identity:
```nix
{
age.rekey.masterIdentities = [
{
identity = "/home/adriano/.ssh/id_rsa_agenix";
}
];
}
```
### Flake Configuration
`flake.nix` configures agenix-rekey:
```nix
{
outputs = { self, ... }: {
# nixosConfigurations required for agenix-rekey to discover hosts
nixosConfigurations = lib.genAttrs hostNames mkHost;
# agenix-rekey integration
agenix-rekey = inputs.agenix-rekey.configure {
userFlake = self;
nixosConfigurations = self.nixosConfigurations;
};
};
}
```
## Advantages over Manual agenix
1. **No secrets.nix maintenance**: Don't need to manually list recipient public keys
2. **Automatic re-encryption**: When host keys change, just update `hostPubkey` and rekey
3. **Git-friendly**: Rekeyed secrets are deterministic (same inputs = same outputs)
4. **Type-safe**: NixOS validates secret paths at build time
5. **Centralized**: All master secrets in one place
## Troubleshooting
### "rekeyFile doesn't exist" error
Secrets must be committed to git for agenix-rekey to see them:
```bash
git add secrets/master/<secret-name>
nix run .#agenix-rekey.x86_64-linux.rekey
```
### "storageMode not set" error
Each host needs `age.rekey.storageMode = "local";` in its configuration.
### Rekeyed secrets not updating
Force a rekey:
```bash
nix run .#agenix-rekey.x86_64-linux.rekey -- --force
git add secrets/rekeyed/
```
### Host can't decrypt secret
Check the hostPubkey matches the actual host:
```bash
# On the host:
ssh-keyscan localhost | grep ssh-ed25519 | ssh-to-age
# Compare with:
nix eval .#nixosConfigurations.<hostname>.config.age.rekey.hostPubkey
```
## Security Considerations
- **Master key**: Keep `~/.ssh/id_rsa_agenix` secure; it can decrypt all secrets
- **Rekeyed secrets**: Safe to commit to git; each is encrypted for only one host
- **Public repo**: If your repo is public and a host private key leaks, an attacker could decrypt that host's secrets from git history
- **CI/CD**: Can build systems without the master key (rekeyed secrets are already encrypted)
## References
- [agenix-rekey GitHub](https://github.com/oddlama/agenix-rekey)
- [agenix GitHub](https://github.com/ryantm/agenix)
- [age encryption](https://age-encryption.org/)

370
UPGRADE_SCRIPT_USAGE.md Normal file
View file

@ -0,0 +1,370 @@
# Nomad 1.11 Upgrade Script Usage (AI-Executable)
## Overview
The `nomad-1.11-upgrade.sh` script is designed to be executed by an AI agent with full automation and resume capability. All file edits are performed programmatically - no manual intervention required.
## Key Features
### 1. State Tracking
Progress is tracked in `.upgrade-state.json`:
```json
{
"started_at": "2026-03-29T10:30:00Z",
"phases": {
"1": {"status": "completed", "completed_at": "2026-03-29T10:35:00Z"},
"2": {"status": "in_progress"},
...
},
"current_phase": 2
}
```
### 2. Resume Capability
Stop at any point and resume later:
```bash
# Script stops/crashes during Phase 3
./nomad-1.11-upgrade.sh --resume
# Automatically resumes from Phase 3
```
### 3. Automated File Editing
All configuration changes are automated:
- Template updates
- Host config updates
- Terraform outputs
- Flake.nix updates
No human intervention needed.
## Usage
### Initial Run (Full Automation)
```bash
# Dry run first (recommended)
./nomad-1.11-upgrade.sh --dry-run
# Then live execution
./nomad-1.11-upgrade.sh
```
### Resume After Interruption
```bash
./nomad-1.11-upgrade.sh --resume
```
### Start from Specific Phase
```bash
# Start from Phase 3 (skip 1 and 2)
./nomad-1.11-upgrade.sh --phase 3
```
## Command Line Options
| Option | Description |
|--------|-------------|
| `--dry-run` | Show what would be executed without making changes |
| `--resume` | Resume from last completed phase (reads state file) |
| `--phase N` | Start from phase N (overrides resume) |
| `--help` | Show help text |
## Phases & State
### Phase 1: Pre-Upgrade Validation
**State**: `phases.1.status = "completed"`
- Creates backup directory
- Documents current cluster state
- Validates cluster health
- Records current leader
**Exit Point**: Clean state, safe to stop
### Phase 2: Upgrade Green Generation
**State**: `phases.2.status = "completed"`
- Updates templates (automated)
- Updates host configs (automated)
- Fetches new SSH host keys from green nodes
- Converts SSH keys to age format
- Updates hostPubkey in host configurations
- Runs agenix-rekey to encrypt secrets for new host keys
- Deploys via colmena
- Monitors upgrade
- Transfers leadership if needed
**Exit Point**: Mixed version cluster (1.9 + 1.11), safe to stop
### Phase 3: Migrate Workloads
**State**: `phases.3.status = "completed"`
- Disables scheduling on old nodes
- Drains nodes one-by-one
- Verifies workload migration
**Exit Point**: All workloads on green generation, old nodes idle
### Phase 4: Decommission Old Generation
**State**: `phases.4.status = "completed"`
- Stops Nomad on old nodes
- Removes from Terraform state
- Updates Terraform outputs (automated)
- Updates DNS records
- Destroys Linode instances
**Exit Point**: Only green generation remains
### Phase 5: Cleanup & Finalization
**State**: `phases.5.status = "completed"`
- Archives old configs
- Updates flake (automated)
- Commits changes
- Final verification
**Exit Point**: Complete, fully cleaned up
## Resume Behavior
The script automatically detects what needs to be done:
```bash
# Scenario 1: Phase 1 complete, Phase 2 not started
./nomad-1.11-upgrade.sh --resume
# Starts at Phase 2
# Scenario 2: Phase 3 in progress (crashed mid-drain)
./nomad-1.11-upgrade.sh --resume
# Starts at Phase 3 (idempotent drain operations)
# Scenario 3: All phases complete
./nomad-1.11-upgrade.sh --resume
# Shows "All phases already completed!"
```
## Idempotency
Each phase is designed to be idempotent:
- **Phase 1**: Re-running captures latest state
- **Phase 2**: Re-running checks version, skips if already 1.11
- **Phase 3**: Re-running drains checks allocation count first
- **Phase 4**: Re-running state removal checks if resource exists
- **Phase 5**: Re-running archival checks if already moved
## Output & Logging
### Log File
```
nomad-upgrade-20260329-103000.log
```
Timestamped, contains full execution history.
### State File
```
.upgrade-state.json
```
Machine-readable progress tracking.
### Backup Directory
```
./backups/upgrade-20260329-103000/
```
Pre-upgrade snapshots for rollback.
### Log Levels
- **[INFO]** - General information
- **[SUCCESS]** - Successful operations
- **[WARNING]** - Warnings (non-fatal)
- **[ERROR]** - Errors (may be fatal)
- **[PHASE]** - Phase transitions
- **[STEP]** - Step within phase
- **[STATE]** - State file updates
## AI Agent Integration
### Recommended Workflow for AI
1. **Validate Environment**
```bash
# Check prerequisites
nomad server members
nomad node status
```
2. **Run Dry-Run**
```bash
./nomad-1.11-upgrade.sh --dry-run 2>&1 | tee dry-run.log
# Parse output, check for errors
```
3. **Execute with Monitoring**
```bash
./nomad-1.11-upgrade.sh 2>&1 | tee execution.log
# Parse state file periodically
# Monitor for errors in log
```
4. **Handle Interruptions**
```bash
# If script stops/fails
cat .upgrade-state.json # Check progress
./nomad-1.11-upgrade.sh --resume # Continue
```
5. **Verify Completion**
```bash
jq '.phases | to_entries | map(select(.value.status != "completed"))' .upgrade-state.json
# Should return []
```
### State File Schema
```json
{
"started_at": "ISO8601 timestamp",
"phases": {
"1": {
"status": "pending|in_progress|completed|failed",
"name": "phase_name",
"in_progress_at": "ISO8601 timestamp",
"completed_at": "ISO8601 timestamp"
}
},
"current_phase": 1-5,
"dry_run": true|false
}
```
### Parsing Status
```bash
# Get current phase
jq -r '.current_phase' .upgrade-state.json
# Get phase status
jq -r '.phases."3".status' .upgrade-state.json
# Check if all complete
jq -r '.phases | to_entries | all(.value.status == "completed")' .upgrade-state.json
```
## Error Handling
### Script Exits on Error
Exit codes:
- `0` - Success
- `1` - Error in phase execution
- Non-zero - Command failure
### Resume After Error
```bash
# Review error in log
tail -n 100 nomad-upgrade-*.log
# Check state
cat .upgrade-state.json
# Fix issue (if manual intervention needed)
# Then resume
./nomad-1.11-upgrade.sh --resume
```
## Troubleshooting
### State File Corrupted
```bash
# Reset state
rm .upgrade-state.json
# Start fresh or from specific phase
./nomad-1.11-upgrade.sh --phase 1
```
### Phase Stuck
```bash
# Check what's running
nomad node status
nomad server members
# Manually complete operation
# Update state file
jq '.phases."2".status = "completed"' .upgrade-state.json > .upgrade-state.json.tmp
mv .upgrade-state.json.tmp .upgrade-state.json
# Resume next phase
./nomad-1.11-upgrade.sh --resume
```
### Rollback Needed
```bash
# Restore from backup
cp operate/hosts/ncm-{3,4,5}/default.nix.backup operate/hosts/ncm-{3,4,5}/default.nix
# Redeploy old version
colmena apply --on ncm-3,ncm-4,ncm-5 --impure
# Re-enable old nodes
nomad node eligibility -enable <node-id>
```
## Examples
### Full Automation (AI Agent)
```bash
#!/bin/bash
set -e
# Pre-flight checks
echo "Checking cluster health..."
nomad server members | grep -q "alive" || exit 1
# Dry run
echo "Running dry-run..."
./nomad-1.11-upgrade.sh --dry-run
# Execute
echo "Starting upgrade..."
./nomad-1.11-upgrade.sh
# Verify
echo "Verifying completion..."
jq -e '.phases | to_entries | all(.value.status == "completed")' .upgrade-state.json
nomad server members
nomad node status
echo "Upgrade complete!"
```
### Phase-by-Phase with Validation
```bash
#!/bin/bash
phases=(1 2 3 4 5)
for phase in "${phases[@]}"; do
echo "=== Running Phase $phase ==="
./nomad-1.11-upgrade.sh --phase $phase
echo "=== Phase $phase Complete ==="
echo "Validating..."
nomad server members
nomad node status
echo "Press Enter to continue to next phase..."
read
done
```
## Integration with Claude Code
When executing this script via Claude Code:
1. Claude reads the plan
2. Claude executes: `./nomad-1.11-upgrade.sh --dry-run`
3. Claude reviews dry-run output
4. Claude executes: `./nomad-1.11-upgrade.sh`
5. Claude monitors `.upgrade-state.json` and logs
6. If interrupted, Claude resumes: `./nomad-1.11-upgrade.sh --resume`
7. Claude verifies final state
No manual intervention needed at any point.

View file

@ -6,7 +6,7 @@ This module manages NixOS configurations for cluster nodes using Colmena.
The operate module:
- Defines NixOS system configurations for each cluster node
- Manages secrets with agenix
- Manages secrets with agenix-rekey (automated re-encryption on host key changes)
- Configures Nomad cluster (server + client mode)
- Sets up Tailscale VPN
- Manages firewall rules and networking
@ -27,11 +27,14 @@ operate/
## Host Configuration
Each host directory contains:
- `default.nix` - Main system configuration
- `default.nix` - Main system configuration (includes hostPubkey for secret encryption)
- `base-system.nix` - Common system settings
- `hardware-configuration.nix` - Hardware-specific config
- `nomad.nix` - Nomad cluster configuration
- `secrets/` - Encrypted secrets (agenix)
Secrets are managed centrally:
- `../../secrets/master/` - Source secrets (encrypted with master key)
- `../../secrets/rekeyed/` - Per-host encrypted secrets (auto-generated by agenix-rekey)
## Prerequisites
@ -98,13 +101,22 @@ colmena exec --on @all -- tailscale status
### Manage Secrets
Secrets are encrypted with agenix. To edit:
Secrets are managed centrally with agenix-rekey. To edit a secret:
```bash
cd operate/hosts/<hostname>/secrets/
# Edit the master secret
cd secrets/master/
agenix -e <secret-name> -i ~/.ssh/id_rsa_agenix
# Rekey for all hosts (auto-encrypts for each host's public key)
nix run .#agenix-rekey.x86_64-linux.rekey
# Add rekeyed secrets to git
git add secrets/rekeyed/
```
When host SSH keys change (e.g., after instance recreation), just run the rekey command to automatically re-encrypt secrets for the new keys.
### Rebuild Single Node
```bash
@ -172,11 +184,22 @@ colmena apply --show-trace
### Secret Decryption Failures
Ensure public keys in `secrets/secrets.nix` include:
- Your admin SSH key
- The host's SSH key (from `ssh-keyscan`)
If secrets fail to decrypt on a host:
Re-encrypt if needed:
```bash
agenix -e <secret> -i ~/.ssh/id_rsa_agenix
```
1. Check the host's public key is set correctly in `operate/hosts/<hostname>/default.nix`:
```nix
age.rekey.hostPubkey = "age1...";
```
2. Verify the master key can decrypt the source secret:
```bash
cd secrets/master/
agenix -d <secret-name> -i ~/.ssh/id_rsa_agenix
```
3. Re-run agenix-rekey to regenerate host-specific secrets:
```bash
nix run .#agenix-rekey.x86_64-linux.rekey --force
git add secrets/rekeyed/
colmena apply --on <hostname> --impure
```

View file

@ -4,6 +4,7 @@
config,
pkgs,
agenix,
agenix-rekey,
...
}: {
deployment = {
@ -34,6 +35,7 @@
imports = [
agenix.nixosModules.default
../rekey.nix
./base-system.nix
./hardware-configuration.nix
(import ./nomad.nix {
@ -42,6 +44,12 @@
})
];
# agenix-rekey configuration
age.rekey.storageMode = "local";
age.rekey.localStorageDir = ../../../secrets/rekeyed/ncm-0;
boot = {
supportedFilesystems = ["btrfs" "ntfs" "vfat" "ext4"];
loader.grub = {

View file

@ -1,15 +0,0 @@
age-encryption.org/v1
-> ssh-rsa QGRkYA
DWb37kQ5fhkraWg90TMjxv49VsAIlCnOeGhKHrYeGUvDFR5CP6o/mtMLRsBUaY3I
UkczSB7cPqejc+1rYEITI1QpCC1EKet8WYN3DwaHLHOnBQyaqCaPrL8dWARJsba8
GdncimCGXbuNR9o2kDe0CcsNVYm16rfTaGxze+LoWzrUESSvx6/oy7+Lc3J3ZsBU
n9hekGOhC0ra0ItTaa6I+JX3OjzWDkAum+dUdgsjZ51s+5s+hADOvnbaTLlaj/a7
gBShGo558jwiwLXaZ3Tsv+UWAkmWJBj18LCLF61TXIOSDEQ1yRq3qceUiS0+M6hv
F8vKBx+pg+yco3Kg6WJOQ0jozwrZTxeQS3PF+HMEkxtZ0LCoorQ58Ud3n5NBUFWs
cQj77qZpxok9p5CM5IxsXhUZIAly+jGj2Pm25cLjlI2iBvn+lJmFhHs9qkWIjv+L
+OWiqQTjhMCzLT+zfNVP1AfpCuz9mOQnqjrY/OyMbMVrfaDlBO7OcHbM6k8PDmB3
-> ssh-ed25519 9wiyCA 60M7bcZMRZT/Y4Rsj4QMvxDDtly1vUNULHhn60zF1BE
btWbynM9Nlrk7H++y6LCHnuSi0XESrC7JushDnkuDRo
--- v50l8afGolaf+RSC34DTvq+NYvWTCG6pvMGqy2ZAZxg
@ÜÛ Ò8׫Èmô(£¨*&¥ŸÊñ¾ÀŠÂß<C39F>·9Œ0{;ôÈy1R&/ÿÆ<C3BF>%cþ{X0! ŠÂì<E280BA>Ì=8™¤_ÝZiK”„ÜÚ1”RnU§]u³ÀŸXLòY³

View file

@ -1,6 +0,0 @@
# Use ssh-keyscan <SERVER> to fetch the below keys for target systems (SSH server must be running on system)
let
in {
tailscale_auth_key.publicKeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILUJR7tGcQEwWj9lGEoC2sqWzxFif1BFddYzZfcfTFp+"];
linode_cli_pat.publicKeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILUJR7tGcQEwWj9lGEoC2sqWzxFif1BFddYzZfcfTFp+"];
}

View file

@ -1,15 +0,0 @@
age-encryption.org/v1
-> ssh-rsa QGRkYA
nfOvsCD1a+BI7iLi8pHUbWXVd/1bD1SbfncHkCaZMKL3Eu1s4tkkAYDdBJuDjB6w
iX7WymzwE56AxNWbjJ9oytNNuFWVRnwij9JRddZrqN0BuAf9suz9DShw/ZhS5WpV
3iqu0sEFiDfS4o0RYbO8N5VEctCVlKU6JDuuR59NszpveH+OOo9Cf8ktI+D/hBxr
aK2hNCyro/XvFXnBKx8HCrrnN/2c108DGUb6CbTfeBTf16nen+Dr4dhe8pwzQMgf
phC1S7RAR2J7Gg6u9elC+mUE9Q8uQvR2KISr2x5vTjxdeaXNnVu1nt2H28LNhUDO
UfjXrmGzWnX/oMCjTy3IhVqMHj+5KN9NrVZW7nD6bggwuNCqzgD4D8DfzN/EBkZ1
gxvhLN1iMDnLPXDxjr/V8+SUzQF7b00N/yhBBiUW+pzcOPIXZ3I4WFzJnMLTy8+r
D07MM6OtYzYJPj6054kRPJlZs5bJTwyqHisK9yaKu29QA/BBCD6YctrzmkRR4Bof
-> ssh-ed25519 9wiyCA UREl4v6fTZe2pRMazoHQTDtixfBfuJ0pobjND06F+iM
3RXyb5/AN6JMdlYvbKfrRfAVwhi9LAzewNfUT2H/ZBs
--- YHrW11yRAjE01fgUjBllbLwaXx7fofsmay0y8YrZ1GQ
<ÇXøž<C3B8>wuŠr0¢]½u°Q¥àò]wEp©Ä<Tin ÷÷J4»arRK'v5FðK.ô¥¡Ê¶ì¢Ê¬<C38A>>TωÏ{£?XüZü.òîCQe$f~$M6

View file

@ -4,6 +4,7 @@
config,
pkgs,
agenix,
agenix-rekey,
...
}: {
deployment = {
@ -34,6 +35,7 @@
imports = [
agenix.nixosModules.default
../rekey.nix
./base-system.nix
./hardware-configuration.nix
(import ./nomad.nix {
@ -42,6 +44,12 @@
})
];
# agenix-rekey configuration
age.rekey.storageMode = "local";
age.rekey.localStorageDir = ../../../secrets/rekeyed/ncm-1;
boot = {
supportedFilesystems = ["btrfs" "ntfs" "vfat" "ext4"];
loader.grub = {

View file

@ -1,15 +0,0 @@
age-encryption.org/v1
-> ssh-rsa QGRkYA
UVvVR1AjoXWrlZ0KCf5t+Fust/9cD7YmgWcmEFtMnbWw0b7Ns3PmXJ9LfDeVz6Dp
hBbwlEx0eKZdT9NFYRKUJQ0uyhgm0jpuHnDTroauSy0l5yiZCrAoSu2kE4VTYSqr
nW36MFwhwW72ttWwDB4K6iMkX9/ttyVzQDCCbsVFT/miWQOnVYJ6/Zxqf6LxXw+2
9shypscJcA1IeMXXwwd0kK+zl1dYhqWMiILqSLRxqMUjTkh9HyODKknjfoQ2kMLs
mSkew1+t1/Z4AaC69vztStZBIeNt4WlrtqDRmAVzj4pWzcQGo0Tr+sV6UU2f6rca
BJqE4SF0pCA3laTbEyvhRgtfvQ6KlYCnCHwAF+oOaCoEvArn0dK83HcjX35txxr+
ZD43tgQ2kEPSNxUIlJ+SfMFzTrN8UvF5tgbbyv+4GpL1oGlPPvz66WsKX76K199w
T5CMgna4IH9uZstEZCV+/53nEjKaW7YAezHeCgopcQYxscIh0Gu8MiHTWKPztyFI
-> ssh-ed25519 o8Va9g Ov+y+2crfYP+z33Re7W32CF03gPXOFhS1Sa+Pk/n9W8
rwvlQtCSbuW8xEI5mQrnGgQuVh34bFjnjF+HxdNnlZw
--- O71Uoai3gZMhQowUKAMHLqxS/2aIkjmTXz3lVQSa4F0
_‡&Ñï^IHì˜rnÃBCˆïÁ ˆ|SK­ŠM<C5A0>¶<ÄdFÈŽ5µz>é¿!kk ·lÕ!<21>ãRê4…2B!hm®GåÓçCVVœ .„ìÍ

View file

@ -1,6 +0,0 @@
# Use ssh-keyscan <SERVER> to fetch the below keys for target systems (SSH server must be running on system)
let
in {
tailscale_auth_key.publicKeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKbgB/dhJKY4lX8jmvY0C9enBHDnP5sx5GOYqFQlR+op"];
linode_cli_pat.publicKeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKbgB/dhJKY4lX8jmvY0C9enBHDnP5sx5GOYqFQlR+op"];
}

View file

@ -1,15 +0,0 @@
age-encryption.org/v1
-> ssh-rsa QGRkYA
pL3LXLX8e8zzCiuF6vH1ELnnGWAzdFsW/PybRMSXGXYnM4f+dNmSow+h2MF+u+C6
CRZpB16SW98VpLscwXmD/iOpCERXAnBqvmG1XdIDUYeKOke5K1n+93+l3pb/eezV
04/tgGTkdAedqprAxyyFgwBMfWlbi/+IU4OKCSuKvZOdiLiELHGAmSHCHrKnsitn
MZEDvc5XYzSLmA2+umtNiGj9BPMPu/lkKN1tWqiWNXt5it8Zqj/ErVXIOm7byk5H
V2/8y14GCA5LBU900VDLiTx0fwyegoy2t9IOioDdpM2KSmzUBugQV8vQFYsQPzGN
xzRiG7nx+L42vCwvLg5okWyugYN2HhqUstHuYmzksL7rMjTGzrurlNzdYoGRH8R7
lQLbr8SFq/7zuI2Jt59ZiF9rR/2qHITPwyiZ3GoYBOjVM9EhHUg8bBvEWXkyS6dT
+Ryb1mlNrn5DBHRb0pSAEvLkDoyw23ZvhgKEmXZ524MkvmBnPNwn2ctlDmRv0SIa
-> ssh-ed25519 o8Va9g qgFXCMZN95QUlDu8CQlxgOHC7CfgKe9lhWWFr5m1P3c
lyEdtMmjlEAp3TdKw6fbx/APBtUgDVuesUbxisdHZ+s
--- 3rHayoYA0fZM2VNckGhTwSDZPREKcKWG/tbZC7WB/Ck
^V`·á¤f»í4Ú/œêÑ?&9þaK$qiL1AQºJ~åBØ•·?™wìD'çµWï„N2!î%¤<><17>Ï<EFBFBD>ÉÿÛ–‹$f¢PχÅã™mŽzgYœ.Ö­ ŽÀÙ°Ø

View file

@ -4,6 +4,7 @@
config,
pkgs,
agenix,
agenix-rekey,
...
}: {
deployment = {
@ -34,6 +35,7 @@
imports = [
agenix.nixosModules.default
../rekey.nix
./base-system.nix
./hardware-configuration.nix
(import ./nomad.nix {
@ -42,6 +44,12 @@
})
];
# agenix-rekey configuration
age.rekey.storageMode = "local";
age.rekey.localStorageDir = ../../../secrets/rekeyed/ncm-2;
boot = {
supportedFilesystems = ["btrfs" "ntfs" "vfat" "ext4"];
loader.grub = {

View file

@ -1,15 +0,0 @@
age-encryption.org/v1
-> ssh-rsa QGRkYA
pbrOgDiO/3XWQgDL6UQx8cItdLq38zNigrAhEUQNJGAJU/qBuPT+2PV6siaXJirP
dr3eeuTDIhSJhsjJR3YVxxFCHV+xUm391Ic4ywIDiuF6IjGT9SNb+T9UNrhPIySV
jpSi0ES6/QXcqHRc96cmrq/khJ4+02IknkIRa0VqzmAqvMw2uOLBYT1gAnS7ncpY
Kvgs+JIjO6WEsE7byjKrtzH+fJnzGXq/t+YkvO3RzGQu/2QqLcn/vx+uBQHCiUr5
IN4JafmdyDM6FArjvZvngRC0VcfQxGUoyoSzAMoMFX48JJ83J7c2mzEhZ8ai4ZmN
Hkcbm0F6BSJw0pvyvURyLi/M1Weyoef3bpgXtH9PsJr1mlMKEW8LjgLlu15cQG9V
JJlPFRUvf3/lzL3yGDETHOYCFeXrH+TTp7KLYIdHMWhHhQMJ68OKkpHnnJaQPStF
hy8xAmI6z3/kQKrFifL8SAKZkWwVPxq3mdsbBRaLM9t3spXT8FhCDbYgAntLAmCK
-> ssh-ed25519 mCobKg d7hP1AFvHdXCUGtOh01/06IEHmtHHSH/wJTJEBOjWUs
G1uS06aAaZE/ReQ9q/FTzc586dhSZX95fTBYwa2TZ6U
--- 4R5LkLFZoBfnPTP//TDfnP0JIajNo7rLIIrqEVtDzyo
þÚ!dIûWúHMFkwÈ (ýÀÚç¯=²šÌoOŒßý«êdäF„eÿ²F©åÇhŦUÔ¨|ѳɥmÙ6<C399>=n<NtEÜž½Ä<C2BD>Žz.ƒªi7˜Ù<CB9C>Ïš+U Ý

View file

@ -1,6 +0,0 @@
# Use ssh-keyscan <SERVER> to fetch the below keys for target systems (SSH server must be running on system)
let
in {
tailscale_auth_key.publicKeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILzE/CkZokri8tfeuA9EWompjmBVPC07FV0JLkfDsv5l"];
linode_cli_pat.publicKeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILzE/CkZokri8tfeuA9EWompjmBVPC07FV0JLkfDsv5l"];
}

View file

@ -4,10 +4,11 @@
config,
pkgs,
agenix,
agenix-rekey,
...
}: {
deployment = {
targetHost = "172.235.48.77";
targetHost = "172.236.226.134";
targetPort = 22;
targetUser = "root";
buildOnTarget = false;
@ -34,6 +35,7 @@
imports = [
agenix.nixosModules.default
../rekey.nix
./base-system.nix
./hardware-configuration.nix
(import ./nomad.nix {
@ -42,6 +44,10 @@
})
];
# agenix-rekey configuration
age.rekey.storageMode = "local";
age.rekey.localStorageDir = ../../../secrets/rekeyed/ncm-3;
boot = {
supportedFilesystems = ["btrfs" "ntfs" "vfat" "ext4"];
loader.grub = {
@ -59,7 +65,7 @@
environment.systemPackages = with pkgs; [
helix
nomad_1_9
(if builtins.elem name ["ncm-3" "ncm-4" "ncm-5"] then nomad_1_11 else nomad_1_9)
nomad-driver-podman
jq
postgresql # So hosts can perform postgres system backups

View file

@ -0,0 +1,114 @@
{
name,
nodes,
config,
pkgs,
agenix,
...
}: {
deployment = {
targetHost = "172.235.48.77";
targetPort = 22;
targetUser = "root";
buildOnTarget = false;
};
networking = {
networkmanager.enable = true;
# Linode is no longer able to connect to Linode Object Store via ipv6, and and linode object store is now advertising
# ipv6 addrs via DNS. This is the simple and dumb solution to fix the problem of Nomad not being able to fetch
# artifacts from object store.
enableIPv6 = false;
hostName = "ncm-3";
nftables.enable = true;
firewall = {
logRefusedConnections = true;
logRefusedPackets = true;
checkReversePath = false;
# always allow traffic from the tailnet and internal network
trustedInterfaces = ["tailscale0" "enp0s5" "podman0" "veth0"];
allowedTCPPorts = [22 80 443 24017];
allowedUDPPorts = [config.services.tailscale.port];
};
};
imports = [
agenix.nixosModules.default
./base-system.nix
./hardware-configuration.nix
(import ./nomad.nix {
hostname = name;
pkgs = pkgs;
})
];
boot = {
supportedFilesystems = ["btrfs" "ntfs" "vfat" "ext4"];
loader.grub = {
enable = true;
};
};
age.secrets.tailscale_auth_key = {
file = ./secrets/tailscale_auth_key;
};
age.secrets.linode_cli_pat = {
file = ./secrets/linode_cli_pat;
};
environment.systemPackages = with pkgs; [
helix
nomad_1_9
nomad-driver-podman
jq
postgresql # So hosts can perform postgres system backups
python3
restic # For performing backups
(writeShellScriptBin "list-peer-nodes" ''
#!/usr/bin/env bash
${pkgs.tailscale}/bin/tailscale status --json | ${pkgs.jq}/bin/jq '.Peer[] | select(.Online==true) | select(has("Tags")) | select( [ .Tags[] | contains("tag:ncm")] | any).TailscaleIPs[0]' | tr '\n' ' ';
'')
];
users.users = {
root = {
openssh.authorizedKeys.keys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1"];
};
sfos = {
isNormalUser = true;
extraGroups = ["wheel"];
# TODO Initial password and authorized keys need to come from .env, perhaps using nix-edit
initialPassword = "Test.123";
openssh.authorizedKeys.keys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1"];
};
};
services = {
# TODO caddy needs a plugin that watches the nomad node list for specific tags that cause them to be reverse proxied
# from this host system
caddy = {
enable = true;
# package = pkgs.caddy.withPlugins {
# plugins = ["github.com/acaloiaro/caddy-incus-upstreams@v0.0.0-20241228235755-786cd3b6a9dc"];
# hash = "sha256-yjqXgo7WiJPBVmN3+O8Wq9Nxd1xU8KrXW7Hv7Elcf7I=";
# };
# virtualHosts = {
# "https://adrianofyi.incus.adriano.fyi:443" = {
# extraConfig = ''
# reverse_proxy {
# dynamic incus
# }
# '';
# };
# };
};
openssh.enable = true;
tailscale = {
enable = true;
authKeyFile = config.age.secrets.tailscale_auth_key.path;
extraUpFlags = ["--reset" "--advertise-tags=tag:ncm,tag:nomad-server" "--accept-dns=false"];
};
};
system.stateVersion = "24.05";
}

View file

@ -16,6 +16,7 @@
};
services.nomad = {
enable = true;
package = pkgs.nomad_1_11;
extraSettingsPlugins = [pkgs.nomad-driver-podman];
enableDocker = true;
dropPrivileges = false;
@ -57,7 +58,7 @@
interface = "tailscale0";
};
"public" = {
interface = "enp0s3";
interface = "eth0";
};
"private" = {
cidr = "10.0.1.2/24";

View file

@ -1,8 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEhsV2VvUSBYYmVT
WmFQZk5VOG10NnZBWjVvYWJGYVV0dGNzeTdhVkphaGVuR2tYdzNFCnVRVllhYThR
U3p6SlE1T01zdTBTV25RSVdGU29pREJmUFVOL1VrckdkSVkKLS0tIGl5WGE4UnRr
V2txaG9kcjk4c3QwSm54TWllVHBiWm5ZcXMvYUNVMzZjMG8KTZ5QU6IK1ueizdo2
4CWq5fTxo0Y/yjB1QvkNCZAYYS0PiCThUxX82U7nOllUB6dZ/ODA9oitXum2FXd2
RjAZYr0SY3sLHbmOV932SyYiQKZRxTVhEBFwwxV98bong87d
-----END AGE ENCRYPTED FILE-----

View file

@ -1,6 +0,0 @@
# Use ssh-keyscan <SERVER> to fetch the below keys for target systems (SSH server must be running on system)
let
in {
tailscale_auth_key.publicKeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHuJNBURq4NZpt+EgQ/750HCqu43tFbV0UT4Vpz4drId"];
linode_cli_pat.publicKeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHuJNBURq4NZpt+EgQ/750HCqu43tFbV0UT4Vpz4drId"];
}

View file

@ -1,8 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEhsV2VvUSBkMXhH
Zi9Tb2sxSjFFbVJRR25LSWFwT1F2ZEY1dEduRlBRRmk2TXZuQlRzCm1kTEE0bGFQ
UTZXM3VveGliN0ZDWTl2RlRBT3NHYnhrTTJ1QlZObU1KNFUKLS0tIC9ocXZRVEFz
VHBWRzFHVktyaG4yTG1CeFM0SmVxZ2x4bzNDQXB1aHVWOE0KgPi29as8x5Blrzh9
jzhvnvjtBUnBimmRkZG8ZFNiaBo0nF73itnEhjPAOi2wS4BKg2SQm7n93RjVd8EZ
Id7VonE4Bf7CIkDtCx0fNqzadjhfEfdjdo6E2xXvti4YbQ==
-----END AGE ENCRYPTED FILE-----

View file

@ -4,10 +4,11 @@
config,
pkgs,
agenix,
agenix-rekey,
...
}: {
deployment = {
targetHost = "172.236.244.127";
targetHost = "172.236.226.94";
targetPort = 22;
targetUser = "root";
buildOnTarget = false;
@ -34,6 +35,7 @@
imports = [
agenix.nixosModules.default
../rekey.nix
./base-system.nix
./hardware-configuration.nix
(import ./nomad.nix {
@ -42,6 +44,12 @@
})
];
# agenix-rekey configuration
age.rekey.storageMode = "local";
age.rekey.localStorageDir = ../../../secrets/rekeyed/ncm-4;
boot = {
supportedFilesystems = ["btrfs" "ntfs" "vfat" "ext4"];
loader.grub = {
@ -59,7 +67,7 @@
environment.systemPackages = with pkgs; [
helix
nomad_1_9
(if builtins.elem name ["ncm-3" "ncm-4" "ncm-5"] then nomad_1_11 else nomad_1_9)
nomad-driver-podman
jq
postgresql # So hosts can perform postgres system backups

View file

@ -0,0 +1,114 @@
{
name,
nodes,
config,
pkgs,
agenix,
...
}: {
deployment = {
targetHost = "172.236.244.127";
targetPort = 22;
targetUser = "root";
buildOnTarget = false;
};
networking = {
networkmanager.enable = true;
# Linode is no longer able to connect to Linode Object Store via ipv6, and and linode object store is now advertising
# ipv6 addrs via DNS. This is the simple and dumb solution to fix the problem of Nomad not being able to fetch
# artifacts from object store.
enableIPv6 = false;
hostName = "ncm-4";
nftables.enable = true;
firewall = {
logRefusedConnections = true;
logRefusedPackets = true;
checkReversePath = false;
# always allow traffic from the tailnet and internal network
trustedInterfaces = ["tailscale0" "enp0s5" "podman0" "veth0"];
allowedTCPPorts = [22 80 443 24017];
allowedUDPPorts = [config.services.tailscale.port];
};
};
imports = [
agenix.nixosModules.default
./base-system.nix
./hardware-configuration.nix
(import ./nomad.nix {
hostname = name;
pkgs = pkgs;
})
];
boot = {
supportedFilesystems = ["btrfs" "ntfs" "vfat" "ext4"];
loader.grub = {
enable = true;
};
};
age.secrets.tailscale_auth_key = {
file = ./secrets/tailscale_auth_key;
};
age.secrets.linode_cli_pat = {
file = ./secrets/linode_cli_pat;
};
environment.systemPackages = with pkgs; [
helix
nomad_1_9
nomad-driver-podman
jq
postgresql # So hosts can perform postgres system backups
python3
restic # For performing backups
(writeShellScriptBin "list-peer-nodes" ''
#!/usr/bin/env bash
${pkgs.tailscale}/bin/tailscale status --json | ${pkgs.jq}/bin/jq '.Peer[] | select(.Online==true) | select(has("Tags")) | select( [ .Tags[] | contains("tag:ncm")] | any).TailscaleIPs[0]' | tr '\n' ' ';
'')
];
users.users = {
root = {
openssh.authorizedKeys.keys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1"];
};
sfos = {
isNormalUser = true;
extraGroups = ["wheel"];
# TODO Initial password and authorized keys need to come from .env, perhaps using nix-edit
initialPassword = "Test.123";
openssh.authorizedKeys.keys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1"];
};
};
services = {
# TODO caddy needs a plugin that watches the nomad node list for specific tags that cause them to be reverse proxied
# from this host system
caddy = {
enable = true;
# package = pkgs.caddy.withPlugins {
# plugins = ["github.com/acaloiaro/caddy-incus-upstreams@v0.0.0-20241228235755-786cd3b6a9dc"];
# hash = "sha256-yjqXgo7WiJPBVmN3+O8Wq9Nxd1xU8KrXW7Hv7Elcf7I=";
# };
# virtualHosts = {
# "https://adrianofyi.incus.adriano.fyi:443" = {
# extraConfig = ''
# reverse_proxy {
# dynamic incus
# }
# '';
# };
# };
};
openssh.enable = true;
tailscale = {
enable = true;
authKeyFile = config.age.secrets.tailscale_auth_key.path;
extraUpFlags = ["--reset" "--advertise-tags=tag:ncm,tag:nomad-server" "--accept-dns=false"];
};
};
system.stateVersion = "24.05";
}

View file

@ -16,6 +16,7 @@
};
services.nomad = {
enable = true;
package = pkgs.nomad_1_11;
extraSettingsPlugins = [pkgs.nomad-driver-podman];
enableDocker = true;
dropPrivileges = false;
@ -57,7 +58,7 @@
interface = "tailscale0";
};
"public" = {
interface = "enp0s3";
interface = "eth0";
};
"private" = {
cidr = "10.0.1.2/24";

View file

@ -1,8 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHM2UGF2USAvOGNR
SCtjd2JVNjdUZWJSNFdCRWxPL2tjaUxpS2JxdEdHdjMzNjJhbWxjCkxGN2F2RHVu
OWJFUXZYV09GYzhXdGNndHcwcXVPMC9odUhpT1YrMWF2ZmcKLS0tIDc1M3VzL1Vs
MHg4R2pvbm14ZXFGNVFlSUlKOGwvc050RTRURkp6T3lveE0KtY5PWXv3UrVevElc
zGQ92CajQ9LMiE8bn6rhF6E6mkVVs9CIbGSympApNwHtSFJZEeaVNzqgWgVQWvO2
hDLD/T1e9kSwfKI+dSSqS7Wx3hyHiAgdYVOYSIckYjdZKpoM
-----END AGE ENCRYPTED FILE-----

View file

@ -1,6 +0,0 @@
# Use ssh-keyscan <SERVER> to fetch the below keys for target systems (SSH server must be running on system)
let
in {
tailscale_auth_key.publicKeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEFXD8JoGWsCP/4GZyqKjP+PCMffJOXnsWSZuBJ7czUC"];
linode_cli_pat.publicKeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEFXD8JoGWsCP/4GZyqKjP+PCMffJOXnsWSZuBJ7czUC"];
}

View file

@ -1,8 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHM2UGF2USBRWU5Y
T2pDUlpEdXM5NHcwTDMxUzN6c2JCVDBucWJGdUh5OGJoSUtDRnljCjNWalk2eTB6
OG5kd1FZMW9yYndwUk5kYmN5Y0tDYXczazlLYmJQZnlKREEKLS0tIGU2ODZyZ1Fz
WDNIQmh2QlVlQW1ZcEtrQ1RWZjdNV2ErZ3lHWWFXMzlMUmsKxVqwWDYTOYLXuGCb
Bhi3H+xd1uTO74FCjhAoCsaaHcjXsqw5p1fJCHzVwjBFvf3C2q/bCKUVVPqz9LcR
JJGJ4SX00difYqgTdUBRwSpQ1OvxtHWB5UPcN20BdUc/Xw==
-----END AGE ENCRYPTED FILE-----

View file

@ -4,10 +4,11 @@
config,
pkgs,
agenix,
agenix-rekey,
...
}: {
deployment = {
targetHost = "172.235.48.213";
targetHost = "172.236.226.82";
targetPort = 22;
targetUser = "root";
buildOnTarget = false;
@ -34,6 +35,7 @@
imports = [
agenix.nixosModules.default
../rekey.nix
./base-system.nix
./hardware-configuration.nix
(import ./nomad.nix {
@ -42,6 +44,12 @@
})
];
# agenix-rekey configuration
age.rekey.storageMode = "local";
age.rekey.localStorageDir = ../../../secrets/rekeyed/ncm-5;
boot = {
supportedFilesystems = ["btrfs" "ntfs" "vfat" "ext4"];
loader.grub = {
@ -59,7 +67,7 @@
environment.systemPackages = with pkgs; [
helix
nomad_1_9
(if builtins.elem name ["ncm-3" "ncm-4" "ncm-5"] then nomad_1_11 else nomad_1_9)
nomad-driver-podman
jq
postgresql # So hosts can perform postgres system backups

View file

@ -0,0 +1,114 @@
{
name,
nodes,
config,
pkgs,
agenix,
...
}: {
deployment = {
targetHost = "172.235.48.213";
targetPort = 22;
targetUser = "root";
buildOnTarget = false;
};
networking = {
networkmanager.enable = true;
# Linode is no longer able to connect to Linode Object Store via ipv6, and and linode object store is now advertising
# ipv6 addrs via DNS. This is the simple and dumb solution to fix the problem of Nomad not being able to fetch
# artifacts from object store.
enableIPv6 = false;
hostName = "ncm-5";
nftables.enable = true;
firewall = {
logRefusedConnections = true;
logRefusedPackets = true;
checkReversePath = false;
# always allow traffic from the tailnet and internal network
trustedInterfaces = ["tailscale0" "enp0s5" "podman0" "veth0"];
allowedTCPPorts = [22 80 443 24017];
allowedUDPPorts = [config.services.tailscale.port];
};
};
imports = [
agenix.nixosModules.default
./base-system.nix
./hardware-configuration.nix
(import ./nomad.nix {
hostname = name;
pkgs = pkgs;
})
];
boot = {
supportedFilesystems = ["btrfs" "ntfs" "vfat" "ext4"];
loader.grub = {
enable = true;
};
};
age.secrets.tailscale_auth_key = {
file = ./secrets/tailscale_auth_key;
};
age.secrets.linode_cli_pat = {
file = ./secrets/linode_cli_pat;
};
environment.systemPackages = with pkgs; [
helix
nomad_1_9
nomad-driver-podman
jq
postgresql # So hosts can perform postgres system backups
python3
restic # For performing backups
(writeShellScriptBin "list-peer-nodes" ''
#!/usr/bin/env bash
${pkgs.tailscale}/bin/tailscale status --json | ${pkgs.jq}/bin/jq '.Peer[] | select(.Online==true) | select(has("Tags")) | select( [ .Tags[] | contains("tag:ncm")] | any).TailscaleIPs[0]' | tr '\n' ' ';
'')
];
users.users = {
root = {
openssh.authorizedKeys.keys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1"];
};
sfos = {
isNormalUser = true;
extraGroups = ["wheel"];
# TODO Initial password and authorized keys need to come from .env, perhaps using nix-edit
initialPassword = "Test.123";
openssh.authorizedKeys.keys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1"];
};
};
services = {
# TODO caddy needs a plugin that watches the nomad node list for specific tags that cause them to be reverse proxied
# from this host system
caddy = {
enable = true;
# package = pkgs.caddy.withPlugins {
# plugins = ["github.com/acaloiaro/caddy-incus-upstreams@v0.0.0-20241228235755-786cd3b6a9dc"];
# hash = "sha256-yjqXgo7WiJPBVmN3+O8Wq9Nxd1xU8KrXW7Hv7Elcf7I=";
# };
# virtualHosts = {
# "https://adrianofyi.incus.adriano.fyi:443" = {
# extraConfig = ''
# reverse_proxy {
# dynamic incus
# }
# '';
# };
# };
};
openssh.enable = true;
tailscale = {
enable = true;
authKeyFile = config.age.secrets.tailscale_auth_key.path;
extraUpFlags = ["--reset" "--advertise-tags=tag:ncm,tag:nomad-server" "--accept-dns=false"];
};
};
system.stateVersion = "24.05";
}

View file

@ -16,6 +16,7 @@
};
services.nomad = {
enable = true;
package = pkgs.nomad_1_11;
extraSettingsPlugins = [pkgs.nomad-driver-podman];
enableDocker = true;
dropPrivileges = false;
@ -57,7 +58,7 @@
interface = "tailscale0";
};
"public" = {
interface = "enp0s3";
interface = "eth0";
};
"private" = {
cidr = "10.0.1.2/24";

View file

@ -1,8 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFp6MkdLUSBxOUpu
ZU1yOXB6WlUvcDNPanA1MFhtQ0lVZWlyUlV0dGZCQ3VlYUo0MUFRClZCMWdpb0ls
Y2htVTN0SHdTajArdm9odVZJd1I3NmZuZ0FYUXJodGZ4NVkKLS0tIDUwUUdZYkJs
T3ZIcytOeEpxUnlURnlFdkE1VVBTNHZiVXFZR2hUOUt6NGMK4rvngvo0nAiUxeyT
qj5d3EcYEj4AZOuy0nzlCXpqIqqCWRDctKF30uXv/6HSL1KpHq3o6bJatsHtu/73
mUKApnNT+AW+5LnJpt3n1ZCHGhHOUxOmrPCvaRtFtIPzf5+/
-----END AGE ENCRYPTED FILE-----

View file

@ -1,6 +0,0 @@
# Use ssh-keyscan <SERVER> to fetch the below keys for target systems (SSH server must be running on system)
let
in {
tailscale_auth_key.publicKeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMqJFMTeWs/TpKNbVTp9S5kU8XVqoV2mmHq3p7ZEIxfy"];
linode_cli_pat.publicKeys = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCx/tvR11RUMbYaauuHPxR3j79vm1Hxg+Y3LXR+rLgZ7N/2ulvIzLLZgVuIUbmJHDqkp9Au5zZPMZjsKbaqQ8V75wSEcyaSMbaxC9PaNDCwEHMJWaz8XPQe5IjVRE5O+4sTyh7ZBx+NMQmPcQbrNInoKuy4EjFmwTW/t0xYo/sCrC0NX0cCyeBwii2JdFXytXfxF+RMzGNXw1xfcOFJe6F7JdS/Cpf/0fe+VmNg8d0nic4Obcb/djYsRLAAC6Cvb+4i3EBZWl+9Ih9hId8bFCRKhI6TmGT2z4YUTa+v+3j/JZUh1gD5n4vRGf8QjLj9N6DrBnKcbywwqyqnLhTLgBBN35rcLdU1k3n0NorRRDCU0Lg/ejsFe3oi2FmOwNmmd8zqBNZHjJTi5Wy63EXMFwHltEY2M+hAhgWsQ5U4zuVPgv1HfD6LYPodRwhdZivwTNr2IClAiVVxR//O0WtrRXrrEM5uudj+Y30/ah7bn9Mje86UV0TqfS2tdjtMkySHL+M= adriano@z1" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMqJFMTeWs/TpKNbVTp9S5kU8XVqoV2mmHq3p7ZEIxfy"];
}

View file

@ -1,8 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IFp6MkdLUSBScFA0
Q29ITEM1ajJlbUJDNHJwNnAxeHNQWFJLbkdpWml5R3EwbVZPcEMwCkE3Znk5S042
eUNOWTdSTE9JcS9hV25GV3hOcjczVDhxbnlwZlBzMXYySzQKLS0tIG5GZmZra3Bu
SmVVd3VoYU0zRkd3SWNSODhIRkN2cGdubC82S2U5RGNSaG8KJn0bYumsij87mJsa
fV/a+M/Yr22+/0swdwt4Gd+UBxDxDnNCPjp97hsb+H8glq1OvP809IAEGRhTCPqR
rPRkl0VUkRv/k9fO7wZMN/tbXZSiYe/yt8wJGcXJMDaPow==
-----END AGE ENCRYPTED FILE-----

10
operate/hosts/rekey.nix Normal file
View file

@ -0,0 +1,10 @@
{...}: {
age.rekey = {
masterIdentities = [
{
identity = "/home/adriano/.ssh/id_rsa_agenix";
}
];
# Note: storageMode and localStorageDir are set per-host in their individual configs
};
}

10
operate/rekey.nix Normal file
View file

@ -0,0 +1,10 @@
{...}: {
age.rekey = {
masterIdentities = [
{
identity = "/home/adriano/.ssh/id_rsa_agenix";
}
];
storageMode = "local";
};
}

View file

@ -1,7 +1,8 @@
age-encryption.org/v1
-> X25519 MpoiaRFtOff7Ycz6vGqkizDZqgWWtcAxqKbELUhZ9WI
ggFrXOKdX5uwfOYHRi5LFnTh5zv/M7frmtjvEOcsVpg
-> pJvO.-grease 'vNWX 6le >G JvAYfDR
NdaJLcjQA3gpk3y9qwq3uekNGfclTG47o0PSoJK2LBpfY/k8Gw
--- gkbYjfJNcz7opJRiWgTUNQwq9eIcOcJfFTHtbMnDYYA
ÖÓhҤ~uş¬ł˘ä"Wr“űĎ1ą¸ě^aĺŠíEŽąđň>ń U$ä˙ÝÝ!Ż:S¤ő7Â:ŗ̌şźl C܉Ƶé÷Ý(EʦîÓ÷ĆŢ{î“ń<>|ţű&ő
-> X25519 P7v8KEsgsWU0NdEgT/VE7mP06IBsrPQx2KupSMg/IVg
2Ou6ekmQjmAMz0CnUFh4OIH5sKuffdr9yhgKviozsQ4
-> 3v-grease *E+u# aX/^VHm# <v6 Bw\c
CazGEusZ8MsQSJrFjhhUr4elARTjZq6GZSExxzgXsKA
--- 1ezFzMiL168P+rVExAwOasFl/6MsmCf2VQ6He/aEr6U
„˜?¾£UOO2¸fijíyñú§üŒP9Ï1ÐF<C390>Û\ÿÉ!\8bÄ}$ØØêu¢æœy)sY‰k©éÜ©-¥¤C<43>F¹}AÆDл
” ÿ¤çîèîwë©„øÞcHœÕ

View file

@ -1,8 +1,10 @@
age-encryption.org/v1
-> X25519 33iPaPNi2FvCs8OeFdn+Y3OdHYQW0ChfW6JaGOoek0E
lTBZv3kxjvb+IwmZI773E6ZgagZsrWHwFazQpz7yJ+8
-> lfnR9-grease qo>< ?#/F;jKX
qKcaXyL6vMrvWA6ioA4S8eo52CsQLbIwbuCxLDHcbqm/T33DR0EqgSrVaGANOgtU
+oLPaYTB7tvT+A
--- dz4hwXJSFvg6ShENBdnvMwiJpl7iNCYmQEbiRSIn/fs
{¥Õ¸%:÷H%ÅUÙ*âq<C3A2>ĶIs~™ƒ"Éâܹ³%©ŽK õ5³˜Bxt6÷ê}ÓÔ7óý;6ÑÏž=÷6ó3µøÐùèávÎYjLÕ§|®4øÅô` j
-> X25519 qzDVonozY6ZZbKhsIzkTXCk+KmmcvBDOPKnAheZa3B8
Cl6mUUSjh95SOnZhPa4FjRDdk+PX6dYgJqot0SR/Vtw
-> [<k87uJ_-grease
wAwriEERz4XJF4flJjYF740Q6RWcvbVqrECbTHVGZ5YtowVUplaDVLSkkImpW7Xx
WXnbg3EqxoTo8G9gjNAuPTSA+EKf8QVU
--- bIsUxkIebGJVYt6yE5H5C7kpGmeAfgibREunUElmS3I
¨Ę(aĚ<61>ÝěĘ6`ˇâ3± ÷7<C3B7>޸ 8]î ŕ
Á@m}ËDaŤîF÷!|ż6^TŽ ädAf|
ŻŃ>_íN™4Űy‰ĹĂn§űŕ[Nź¨Oüd­¸E‰ęxDNş

View file

@ -1,8 +1,8 @@
age-encryption.org/v1
-> X25519 Iv8Gsu+25l5E2G8hynodBW4iEgWrGhutlAAnh1hUdQw
nWcivI47dmgTGFmyxeZ1lRt9W1eVwXHsAFY2QHOu3ag
-> @*-grease Uz>{5 t:_mx$ f >
x+ffUGIqocAyNhgcEaZTGjQ
--- +A6E0O0Qs7zqoA5ymTEV9adTAPhKFdhpvrpnJa2jpHo
1ŽBLĺÄŃŔVˇbĄ“ě=s¸ đľqĄ Ćçňľ<C588>ţš ßb˘gáe…¦îTaË8ĘKę
óđ9q&BĎwZ v[ćĆ9žG#<23> XĎŔ)Ó#óĺQ¦żę§˘d
-> X25519 9sNWiLY6KxSU2DN49wcZavVUWCUa6fTqyVbHKpCjPmw
JyHrgVsTwmKf6Jswdlm9mCP6ApTT1FJ7YzDTPPKu2H4
-> i'wFl-grease o$--T e>{
LuHU+7D+d6eScRe8NRXqNoPxQqQMNf2yJvH1t4ILaKorxUZewlqymrZ0kDEal9fW
JCjfwpLuGsQWi+88he6vZFO73CoOsFxwByPSQGxYOZSNcw
--- nDBnuhOjuyxzAinrHlkunRFChSDt8l+0jT5SHprrJqg
Õ¥€XqY¥écz¦¿lK¼ 'ùãJóðfž¤W;s‰/SøOvÖíÆG¹aÉ Ÿ­¾&uL<75>ø4‰*ãô#5É阹Ëí\¿¿ŠT´s ðæŽ'SÄsʆ

View file

@ -1,8 +1,7 @@
age-encryption.org/v1
-> X25519 w3EJdXskGzIxAY0ez+JB6G6l7mP7BSTAhOUS/wsTL2w
nrBtcMvV65t7LW/tZNYxaHsXdWjXmBBwnFkVpKGMDrQ
-> }vFY-grease )q $7rpv.[@ 0-V.$~
ojB+g60OX+6gI5HzE6mSeDF5KenNPx51+xKYefCaIMlBfS+CYQLVC89PjlV7mRnF
kDWz/0windFATKUBbQTtZdYPrYgkWiAPc3wE
--- hTT4E7hhHVPfxdf7uI/YdLQ/G1PP9vDtSm+a7vK6g8o
nř9?_4÷VEÚÜ<óĚ˝ňpÔóčď9_Ą<5F>ľ`OĆAšfFáÜQ,}//¤LČo¨°Ě˘âÇIxfbNÂřę\—ť—ŹtkRj†ŕ"%H‰î"T3ŹEßä°úŻů
-> X25519 N9ddAjqwpVr9smLTZoVA9In2Q1wK/UG5XN1OHVQK2ls
KNdHlkoRSRKOE25kWfmv4nbpgz0NS7KVpTneMuNbCjk
-> Htx-grease %ekB\"J F]YYbD_ =qG,ZU6 gG
uniXe+Lwt+SR6vTxVaHuYErcSbcUdeno+37iRYgF2gJOGq6XtkTydE0eGxeLf94
--- vBa90z3WJkbx3GuDJyQ4n5jTbuRKXzbhmL3I0hkVt04
”ûsŠ²Ý“ipžTV¤ˆ_Mê3;¯˜¬Ê¯Ðms¹ù é¢ó9©tíJs”<1E>ŸÜYˆÁß8Ÿ¤ZÏÈ'…(„ð–çÜjnÐpäÉÿŒá6u^E‰c~§)Éj.™¡Ð

View file

@ -1,8 +1,8 @@
age-encryption.org/v1
-> X25519 3Dt3qhNQnec9qQVEY2OuBWy+GtIrjxG1zBcLVpH+uHA
gJzCy86W0+ussg+YLhPeKsZgwpeNra2Y4TsaGNrBjbU
-> 3qT-grease b_u*j `#tX7BN {5
9Zn8SSKLELDxTW6mxxIJZmKvvhcXwf8wZ8/88o3ql7osEMHVB8UZ2uTTzPJDULCI
WGWMIZDBDi1ro/pIoDDU541XT4GlNjAQhnuRIKxCWqzz+VBHpTq29CrPFLkO
--- aEbRZ+EbWkCBtdpCNj9a8aBBPIqECmXXpxYF7TVpPAo
ÄAZøOJ:SŠML—œuxõ"’Ûçkpï½u!¼ˆð÷…÷_ÉñŒ xXòÆÜâ¶½1ÜP-˜Ú;«oÕçXýôulâƒ(îûø¡ªTª¬/ßt
-> X25519 bQlwmEf3KDPK10CQv1s4GaSKUIEb3o0FHy5xrp38VyQ
ZTpnWyNQl30Xn3uXiVHB5nSZHA9vlBTu9wUoQyljLTM
-> ?U+mu;R-grease 9eP ^9f .`1U*6h =R("_
/o2v8vVLrdfw62XpxLD9ybWEbZOfd1Jx2VT1C7XmdVdW+zXk6VgvEFkKG/QCrLJ0
cUsOVslcENuGuPzvbA
--- C0HrEb7CZDpukncxIiQ62KEwYyTujRscXdn5GipGqtM
5î2˝¬ ŢlĄűüŤ•ÉnVăć«Ď+¸[[óńüQĺđžÉK$š0t>9c^šS¸ŰT-wXĽťőĚ<C591>­ű<12>ů˘§>ŃčŽ4\¨O+K ϢËwµÖ°.šžëK×