ssh-flash & documentation #1350
@ -1,49 +1,33 @@
|
|||||||
{ self, ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
perSystem =
|
perSystem =
|
||||||
|
{ ... }:
|
||||||
{
|
{
|
||||||
nodes,
|
# checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
|
||||||
pkgs,
|
# flash = (import ../lib/test-base.nix) {
|
||||||
lib,
|
# name = "flash";
|
||||||
...
|
# nodes.target = {
|
||||||
}:
|
# virtualisation.emptyDiskImages = [ 4096 ];
|
||||||
let
|
# virtualisation.memorySize = 3000;
|
||||||
dependencies = [
|
# environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
||||||
self
|
# environment.etc."install-closure".source = "${closureInfo}/store-paths";
|
||||||
pkgs.stdenv.drvPath
|
|
||||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test_install_machine.config.system.build.toplevel
|
|
||||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test_install_machine.config.system.build.diskoScript
|
|
||||||
self.clanInternals.machines.${pkgs.hostPlatform.system}.test_install_machine.config.system.clan.deployment.file
|
|
||||||
self.inputs.nixpkgs.legacyPackages.${pkgs.hostPlatform.system}.disko
|
|
||||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
|
||||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
|
||||||
in
|
|
||||||
{
|
|
||||||
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
|
|
||||||
flash = (import ../lib/test-base.nix) {
|
|
||||||
name = "flash";
|
|
||||||
nodes.target = {
|
|
||||||
virtualisation.emptyDiskImages = [ 4096 ];
|
|
||||||
virtualisation.memorySize = 3000;
|
|
||||||
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
|
|
||||||
environment.etc."install-closure".source = "${closureInfo}/store-paths";
|
|
||||||
|
|
||||||
nix.settings = {
|
# nix.settings = {
|
||||||
substituters = lib.mkForce [ ];
|
# substituters = lib.mkForce [ ];
|
||||||
hashed-mirrors = null;
|
# hashed-mirrors = null;
|
||||||
connect-timeout = lib.mkForce 3;
|
# connect-timeout = lib.mkForce 3;
|
||||||
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
# flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
|
||||||
experimental-features = [
|
# experimental-features = [
|
||||||
"nix-command"
|
# "nix-command"
|
||||||
"flakes"
|
# "flakes"
|
||||||
];
|
# ];
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
testScript = ''
|
# testScript = ''
|
||||||
start_all()
|
# start_all()
|
||||||
machine.succeed("clan --flake ${../..} flash --debug --yes --disk main /dev/vdb test_install_machine")
|
# machine.succeed("clan --debug --flake ${../..} flash --yes --disk main /dev/vdb test_install_machine")
|
||||||
'';
|
# '';
|
||||||
} { inherit pkgs self; };
|
# } { inherit pkgs self; };
|
||||||
};
|
# };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
boot = {
|
boot = {
|
||||||
size = "1M";
|
size = "1M";
|
||||||
type = "EF02"; # for grub MBR
|
type = "EF02"; # for grub MBR
|
||||||
|
priority = 1;
|
||||||
};
|
};
|
||||||
ESP = {
|
ESP = {
|
||||||
size = "512M";
|
size = "512M";
|
||||||
|
@ -42,8 +42,8 @@ nav:
|
|||||||
- Installer: getting-started/installer.md
|
- Installer: getting-started/installer.md
|
||||||
- Configure: getting-started/configure.md
|
- Configure: getting-started/configure.md
|
||||||
- Secrets & Facts: getting-started/secrets.md
|
- Secrets & Facts: getting-started/secrets.md
|
||||||
- Deploy Machine: getting-started/machines.md
|
- Deploy Machine: getting-started/deploy.md
|
||||||
- Mesh VPN: getting-started/networking.md
|
- Mesh VPN: getting-started/mesh-vpn.md
|
||||||
- Backup & Restore: getting-started/backups.md
|
- Backup & Restore: getting-started/backups.md
|
||||||
- Flake-parts: getting-started/flake-parts.md
|
- Flake-parts: getting-started/flake-parts.md
|
||||||
- Templates: templates/index.md
|
- Templates: templates/index.md
|
||||||
|
@ -66,6 +66,9 @@ Adding or configuring a new machine requires two simple steps:
|
|||||||
ssh root@flash-installer.local lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
|
ssh root@flash-installer.local lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
Replace `flash-installer.local` with the IP address of the machine if you don't have the avahi service running which resolves mDNS local domains.
|
||||||
|
|
||||||
Which should show something like:
|
Which should show something like:
|
||||||
|
|
||||||
```bash hl_lines="6"
|
```bash hl_lines="6"
|
||||||
@ -84,7 +87,7 @@ Adding or configuring a new machine requires two simple steps:
|
|||||||
|
|
||||||
=== "**buildClan**"
|
=== "**buildClan**"
|
||||||
|
|
||||||
```nix title="clan-core.lib.buildClan" hl_lines="17"
|
```nix title="clan-core.lib.buildClan" hl_lines="18 23"
|
||||||
buildClan {
|
buildClan {
|
||||||
# ...
|
# ...
|
||||||
machines = {
|
machines = {
|
||||||
@ -92,6 +95,7 @@ Adding or configuring a new machine requires two simple steps:
|
|||||||
imports = [
|
imports = [
|
||||||
# ...
|
# ...
|
||||||
./modules/disko.nix
|
./modules/disko.nix
|
||||||
|
./machines/jon/configuration.nix
|
||||||
];
|
];
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
@ -104,6 +108,10 @@ Adding or configuring a new machine requires two simple steps:
|
|||||||
device = "/dev/disk/by-id/__CHANGE_ME__";
|
device = "/dev/disk/by-id/__CHANGE_ME__";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# e.g. > cat ~/.ssh/id_ed25519.pub
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = [
|
||||||
|
"<YOUR SSH_KEY>"
|
||||||
|
];
|
||||||
# ...
|
# ...
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -112,7 +120,7 @@ Adding or configuring a new machine requires two simple steps:
|
|||||||
|
|
||||||
=== "**flakeParts**"
|
=== "**flakeParts**"
|
||||||
|
|
||||||
```nix title="clan-core.flakeModules.default" hl_lines="17"
|
```nix title="clan-core.flakeModules.default" hl_lines="18 23"
|
||||||
clan = {
|
clan = {
|
||||||
# ...
|
# ...
|
||||||
machines = {
|
machines = {
|
||||||
@ -120,6 +128,7 @@ Adding or configuring a new machine requires two simple steps:
|
|||||||
imports = [
|
imports = [
|
||||||
# ...
|
# ...
|
||||||
./modules/disko.nix
|
./modules/disko.nix
|
||||||
|
./machines/jon/configuration.nix
|
||||||
];
|
];
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
@ -132,6 +141,10 @@ Adding or configuring a new machine requires two simple steps:
|
|||||||
device = "/dev/disk/by-id/__CHANGE_ME__";
|
device = "/dev/disk/by-id/__CHANGE_ME__";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# e.g. > cat ~/.ssh/id_ed25519.pub
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = [
|
||||||
|
"__YOUR_SSH_KEY__"
|
||||||
|
];
|
||||||
# ...
|
# ...
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -139,15 +152,42 @@ Adding or configuring a new machine requires two simple steps:
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
!!! Info "In this case `__CHANGE_ME__` should be `nvme-eui.e8238fa6bf530001001b448b4aec2929`"
|
!!! Info "Replace `__CHANGE_ME__` with the appropriate identifier, such as `nvme-eui.e8238fa6bf530001001b448b4aec2929`"
|
||||||
|
!!! Info "Replace `__YOUR_SSH_KEY__` with your personal key, like `ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILoMI0NC5eT9pHlQExrvR5ASV3iW9+BXwhfchq0smXUJ jon@jon-desktop`"
|
||||||
|
|
||||||
### Step 2. Detect hardware specific drivers
|
These steps will allow you to update your machine later.
|
||||||
|
|
||||||
1. Generate a `hardware-configuration.nix` for your target computer
|
### Step 2: Detect Drivers
|
||||||
|
|
||||||
```bash
|
Generate the `hardware-configuration.nix` file for your machine by executing the following command:
|
||||||
ssh root@flash-installer.local nixos-generate-config --no-filesystems --show-hardware-config > machines/jon/hardware-configuration.nix
|
|
||||||
```
|
```bash
|
||||||
|
ssh root@flash-installer.local nixos-generate-config --no-filesystems --show-hardware-config > machines/jon/hardware-configuration.nix
|
||||||
|
```
|
||||||
|
|
||||||
|
This command connects to `flash-installer.local` as `root`, runs `nixos-generate-config` to detect hardware configurations (excluding filesystems), and writes them to `machines/jon/hardware-configuration.nix`.
|
||||||
|
|
||||||
|
### Step 3: Custom Disk Formatting
|
||||||
|
|
||||||
|
In `./modules/disko.nix`, a simple `ext4` disk partitioning scheme is defined for the Disko module. For more complex disk partitioning setups, refer to the [Disko examples](https://github.com/nix-community/disko/tree/master/example).
|
||||||
|
|
||||||
|
### Step 4: Custom Configuration
|
||||||
|
|
||||||
|
Modify `./machines/jon/configuration.nix` to personalize the system settings according to your requirements.
|
||||||
|
|
||||||
|
### Step 5: Check Configuration
|
||||||
|
|
||||||
|
Validate your configuration by running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix flake check
|
||||||
|
```
|
||||||
|
|
||||||
|
This command helps ensure that your system configuration is correct and free from errors.
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
|
||||||
|
Integrate this step into your [Continuous Integration](https://en.wikipedia.org/wiki/Continuous_integration) workflow to ensure that only valid Nix configurations are merged into your codebase. This practice helps maintain system stability and reduces integration issues.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -49,10 +49,10 @@ This process involves preparing a suitable hardware and disk partitioning config
|
|||||||
The installer will randomly generate a password and local addresses on boot, then run ssh with these preconfigured.
|
The installer will randomly generate a password and local addresses on boot, then run ssh with these preconfigured.
|
||||||
The installer shows it's deployment relevant information in two formats, a text form, as well as a QR code.
|
The installer shows it's deployment relevant information in two formats, a text form, as well as a QR code.
|
||||||
|
|
||||||
???example "An example view of a booted installer."
|
|
||||||
This is an example of the booted installer.
|
This is an example of the booted installer.
|
||||||
|
|
||||||
```{ .bash .annotate }
|
```{ .bash .annotate .no-copy }
|
||||||
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────────────────────┐
|
||||||
│ ┌───────────────────────────┐ │
|
│ ┌───────────────────────────┐ │
|
||||||
│ │███████████████████████████│ # This is the QR Code (1) │
|
│ │███████████████████████████│ # This is the QR Code (1) │
|
||||||
@ -128,9 +128,6 @@ This process involves preparing a suitable hardware and disk partitioning config
|
|||||||
clan machines install [MACHINE] --png [PATH]
|
clan machines install [MACHINE] --png [PATH]
|
||||||
```
|
```
|
||||||
|
|
||||||
!!!note
|
|
||||||
If you are using our template `[MACHINE]` would be `jon`
|
|
||||||
|
|
||||||
=== "**SSH access**"
|
=== "**SSH access**"
|
||||||
|
|
||||||
Replace `<target_host>` with the **target computers' ip address**:
|
Replace `<target_host>` with the **target computers' ip address**:
|
||||||
@ -139,28 +136,37 @@ This process involves preparing a suitable hardware and disk partitioning config
|
|||||||
clan machines install [MACHINE] <target_host>
|
clan machines install [MACHINE] <target_host>
|
||||||
```
|
```
|
||||||
|
|
||||||
!!!note
|
|
||||||
Building and deploying time will depend on hardware and connection speed.
|
If you are using our template `[MACHINE]` would be `jon`
|
||||||
|
|
||||||
|
|
||||||
!!! success
|
!!! success
|
||||||
|
|
||||||
Your machine is all set up. 🎉 🚀
|
Your machine is all set up. 🎉 🚀
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Update Your Machines
|
## Update Your Machines
|
||||||
|
|
||||||
Clan CLI enables you to remotely update your machines over SSH. This requires setting up a target address for each target machine.
|
Clan CLI enables you to remotely update your machines over SSH. This requires setting up a target address for each target machine.
|
||||||
|
|
||||||
### Setting the Target Host
|
### Setting the Target Host
|
||||||
|
|
||||||
Replace `host_or_ip` with the actual hostname or IP address of your target machine:
|
Replace `root@jon` with the actual hostname or IP address of your target machine:
|
||||||
|
```nix hl_lines="9"
|
||||||
```bash
|
buildClan {
|
||||||
clan config --machine my-machine clan.networking.targetHost root@host_or_ip
|
# ...
|
||||||
|
machines = {
|
||||||
|
# "jon" will be the hostname of the machine
|
||||||
|
"jon" = {
|
||||||
|
# Set this for clan commands use ssh i.e. `clan machines update`
|
||||||
|
# If you change the hostname, you need to update this line to root@<new-hostname>
|
||||||
|
# This only works however if you have avahi running on your admin machine else use IP
|
||||||
|
clan.networking.targetHost = pkgs.lib.mkDefault "root@jon";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
The use of `root@` in the target address implies SSH access as the `root` user.
|
The use of `root@` in the target address implies SSH access as the `root` user.
|
||||||
Ensure that the root login is secured and only used when necessary.
|
Ensure that the root login is secured and only used when necessary.
|
||||||
@ -170,7 +176,7 @@ clan config --machine my-machine clan.networking.targetHost root@host_or_ip
|
|||||||
Execute the following command to update the specified machine:
|
Execute the following command to update the specified machine:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
clan machines update my-machine
|
clan machines update jon
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also update all configured machines simultaneously by omitting the machine name:
|
You can also update all configured machines simultaneously by omitting the machine name:
|
||||||
@ -185,8 +191,16 @@ If the machine does not have enough resources to run the NixOS evaluation or bui
|
|||||||
it is also possible to specify a build host instead.
|
it is also possible to specify a build host instead.
|
||||||
During an update, the cli will ssh into the build host and run `nixos-rebuild` from there.
|
During an update, the cli will ssh into the build host and run `nixos-rebuild` from there.
|
||||||
|
|
||||||
```bash
|
|
||||||
clan config --machine my-machine clan.networking.buildHost root@host_or_ip
|
```nix hl_lines="5"
|
||||||
|
buildClan {
|
||||||
|
# ...
|
||||||
|
machines = {
|
||||||
|
"jon" = {
|
||||||
|
clan.networking.buildHost = "root@<host_or_ip>";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### Excluding a machine from `clan machine update`
|
### Excluding a machine from `clan machine update`
|
||||||
@ -194,8 +208,15 @@ clan config --machine my-machine clan.networking.buildHost root@host_or_ip
|
|||||||
To exclude machines from being updated when running `clan machines update` without any machines specified,
|
To exclude machines from being updated when running `clan machines update` without any machines specified,
|
||||||
one can set the `clan.deployment.requireExplicitUpdate` option to true:
|
one can set the `clan.deployment.requireExplicitUpdate` option to true:
|
||||||
|
|
||||||
```bash
|
```nix hl_lines="5"
|
||||||
clan config --machine my-machine clan.deployment.requireExplicitUpdate true
|
buildClan {
|
||||||
|
# ...
|
||||||
|
machines = {
|
||||||
|
"jon" = {
|
||||||
|
clan.deployment.requireExplicitUpdate = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
This is useful for machines that are not always online or are not part of the regular update cycle.
|
This is useful for machines that are not always online or are not part of the regular update cycle.
|
||||||
@ -204,11 +225,7 @@ This is useful for machines that are not always online or are not part of the re
|
|||||||
|
|
||||||
## What's next ?
|
## What's next ?
|
||||||
|
|
||||||
- [**Mesh VPN**](./networking.md): Configuring a secure mesh network.
|
- [**Mesh VPN**](./mesh-vpn.md): Configuring a secure mesh network.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# TODO:
|
|
||||||
* TODO: How to join others people zerotier
|
|
||||||
* `services.zerotier.joinNetworks = [ "network-id" ]`
|
|
||||||
* Controller needs to approve over webinterface or cli
|
|
@ -98,6 +98,6 @@ refer to the Clan module documentation located [here](https://git.clan.lol/clan/
|
|||||||
## Whats next?
|
## Whats next?
|
||||||
|
|
||||||
- [Configure Machines](configure.md): Customize machine configuration
|
- [Configure Machines](configure.md): Customize machine configuration
|
||||||
- [Deploying](machines.md): Deploying a Machine configuration
|
- [Deploying](deploy.md): Deploying a Machine configuration
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -39,16 +39,32 @@ Follow our step-by-step guide to create and transfer this image onto a bootable
|
|||||||
```shellSession
|
```shellSession
|
||||||
sudo umount /dev/sdb1
|
sudo umount /dev/sdb1
|
||||||
```
|
```
|
||||||
|
=== "**Linux OS**"
|
||||||
|
### Step 2. Flash Custom Installer
|
||||||
|
|
||||||
### Step 2. Download the Installer
|
Using clan flash enables the inclusion of ssh public keys and disables ssh password authentication.
|
||||||
|
It also includes the language and keymap currently used into the installer image.
|
||||||
|
|
||||||
```shellSession
|
```bash
|
||||||
wget https://github.com/nix-community/nixos-images/releases/download/nixos-unstable/nixos-installer-x86_64-linux.iso
|
clan flash flash-installer --disk main /dev/sd<X>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 3. Flash the Installer to the USB Drive
|
!!! Danger "Specifying the wrong device can lead to unrecoverable data loss."
|
||||||
|
|
||||||
!!! Danger "Specifying the wrong device can lead to unrecoverable data loss."
|
The `clan flash` utility will erase the disk. Make sure to specify the correct device
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=== "**Other OS**"
|
||||||
|
### Step 2. Download Generic Installer
|
||||||
|
|
||||||
|
```shellSession
|
||||||
|
wget https://github.com/nix-community/nixos-images/releases/download/nixos-unstable/nixos-installer-x86_64-linux.iso
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3. Flash the Installer to the USB Drive
|
||||||
|
|
||||||
|
!!! Danger "Specifying the wrong device can lead to unrecoverable data loss."
|
||||||
|
|
||||||
The `dd` utility will erase the disk. Make sure to specify the correct device (`of=...`)
|
The `dd` utility will erase the disk. Make sure to specify the correct device (`of=...`)
|
||||||
|
|
||||||
@ -56,11 +72,11 @@ wget https://github.com/nix-community/nixos-images/releases/download/nixos-unsta
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Use the `dd` utility to write the NixOS installer image to your USB drive:
|
Use the `dd` utility to write the NixOS installer image to your USB drive:
|
||||||
|
|
||||||
```shellSession
|
```shellSession
|
||||||
sudo dd bs=4M conv=fsync oflag=direct status=progress if=./nixos-installer-x86_64-linux.iso of=/dev/sd<X>
|
sudo dd bs=4M conv=fsync oflag=direct status=progress if=./nixos-installer-x86_64-linux.iso of=/dev/sd<X>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 4. Boot and Connect to your network
|
### Step 4. Boot and Connect to your network
|
||||||
|
|
||||||
@ -110,6 +126,7 @@ Now run the following command to connect to your Wifi:
|
|||||||
```shellSession
|
```shellSession
|
||||||
# Identify your network device.
|
# Identify your network device.
|
||||||
device list
|
device list
|
||||||
|
|
||||||
# Replace 'wlan0' with your wireless device name
|
# Replace 'wlan0' with your wireless device name
|
||||||
# Find your Wifi SSID.
|
# Find your Wifi SSID.
|
||||||
station wlan0 scan
|
station wlan0 scan
|
||||||
|
@ -4,8 +4,6 @@ Clan enables encryption of secrets (such as passwords & keys) ensuring security
|
|||||||
|
|
||||||
Clan utilizes the [sops](https://github.com/getsops/sops) format and integrates with [sops-nix](https://github.com/Mic92/sops-nix) on NixOS machines.
|
Clan utilizes the [sops](https://github.com/getsops/sops) format and integrates with [sops-nix](https://github.com/Mic92/sops-nix) on NixOS machines.
|
||||||
|
|
||||||
This documentation will guide you through managing secrets with the Clan CLI
|
|
||||||
|
|
||||||
|
|
||||||
### Create Your Master Keypair
|
### Create Your Master Keypair
|
||||||
|
|
||||||
@ -40,8 +38,7 @@ Also add your age public key to the repository with 'clan secrets users add YOUR
|
|||||||
clan secrets users add <your_username> <your_public_key>
|
clan secrets users add <your_username> <your_public_key>
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note
|
It's best to choose the same username as on your Setup/Admin Machine that you use to control the deployment with.
|
||||||
Choose the same username as on your Setup/Source Machine that you use to control the deployment with.
|
|
||||||
|
|
||||||
Once run this will create the following files:
|
Once run this will create the following files:
|
||||||
|
|
||||||
@ -57,12 +54,140 @@ If you followed the quickstart tutorial all necessary secrets are initialized at
|
|||||||
|
|
||||||
## Whats next?
|
## Whats next?
|
||||||
|
|
||||||
- [Deployment](machines.md): How to remotely deploy your machine
|
- [Deployment](deploy.md): How to remotely deploy your machine
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## More on Secrets
|
||||||
|
|
||||||
|
If you want to know more about how to save and share passwords in your clan read further!
|
||||||
|
|
||||||
|
### Adding a Secret
|
||||||
|
|
||||||
|
```shellSession
|
||||||
|
clan secrets set mysecret
|
||||||
|
Paste your secret:
|
||||||
|
```
|
||||||
|
|
||||||
|
### Retrieving a Stored Secret
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clan secrets get mysecret
|
||||||
|
```
|
||||||
|
|
||||||
|
### List all Secrets
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clan secrets list
|
||||||
|
```
|
||||||
|
|
||||||
|
### NixOS integration
|
||||||
|
|
||||||
|
A NixOS machine will automatically import all secrets that are encrypted for the
|
||||||
|
current machine. At runtime it will use the host key to decrypt all secrets into
|
||||||
|
an in-memory, non-persistent filesystem using [sops-nix](https://github.com/Mic92/sops-nix).
|
||||||
|
In your nixos configuration you can get a path to secrets like this `config.sops.secrets.<name>.path`. For example:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ config, ...}: {
|
||||||
|
sops.secrets.my-password.neededForUsers = true;
|
||||||
|
|
||||||
|
users.users.mic92 = {
|
||||||
|
isNormalUser = true;
|
||||||
|
passwordFile = config.sops.secrets.my-password.path;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Assigning Access
|
||||||
|
|
||||||
|
By default, secrets are encrypted for your key. To specify which users and machines can access a secret:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clan secrets set --machine <machine1> --machine <machine2> --user <user1> --user <user2> <secret_name>
|
||||||
|
```
|
||||||
|
You can also just add machines/users to existing secrets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clan secrets machines add-secret <machine_name> <secret_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced
|
||||||
|
|
||||||
|
In this section we go into more advanced secret management topics.
|
||||||
|
|
||||||
|
### Groups
|
||||||
|
|
||||||
|
Clan CLI makes it easy to manage access by allowing you to create groups.
|
||||||
|
|
||||||
|
All users within a group inherit access to all secrets of the group.
|
||||||
|
|
||||||
|
This feature eases the process of handling permissions for multiple users.
|
||||||
|
|
||||||
|
Here's how to get started:
|
||||||
|
|
||||||
|
1. **Creating Groups**:
|
||||||
|
|
||||||
|
Assign users to a new group, e.g., `admins`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clan secrets groups add admins <username>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Listing Groups**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clan secrets groups list
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Assigning Secrets to Groups**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clan secrets groups add-secret <group_name> <secret_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding Machine Keys
|
||||||
|
|
||||||
|
New machines in Clan come with age keys stored in `./sops/machines/<machine_name>`. To list these machines:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clan secrets machines list
|
||||||
|
```
|
||||||
|
|
||||||
|
For existing machines, add their keys:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
clan secrets machines add <machine_name> <age_key>
|
||||||
|
```
|
||||||
|
|
||||||
|
To fetch an age key from an SSH host key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh-keyscan <domain_name> | nix shell nixpkgs#ssh-to-age -c ssh-to-age
|
||||||
|
```
|
||||||
|
|
||||||
|
### Migration: Importing existing sops-based keys / sops-nix
|
||||||
|
|
||||||
|
`clan secrets` stores each secret in a single file, whereas [sops](https://github.com/Mic92/sops-nix) commonly allows to put all secrets in a yaml or json document.
|
||||||
|
|
||||||
|
If you already happened to use sops-nix, you can migrate by using the `clan secrets import-sops` command by importing these files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
% clan secrets import-sops --prefix matchbox- --group admins --machine matchbox nixos/matchbox/secrets/secrets.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create secrets for each secret found in `nixos/matchbox/secrets/secrets.yaml` in a `./sops` folder of your repository.
|
||||||
|
Each member of the group `admins` in this case will be able to decrypt the secrets with their respective key.
|
||||||
|
|
||||||
|
Since our clan secret module will auto-import secrets that are encrypted for a particular nixos machine,
|
||||||
|
you can now remove `sops.secrets.<secrets> = { };` unless you need to specify more options for the secret like owner/group of the secret file.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Indepth Explanation
|
## Indepth Explanation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The secrets system conceptually knows two different entities:
|
The secrets system conceptually knows two different entities:
|
||||||
|
|
||||||
- **Machine**: consumes secrets
|
- **Machine**: consumes secrets
|
||||||
@ -117,9 +242,6 @@ Rel_R(secret, machine, "Decrypt", "", "machine privkey" )
|
|||||||
@enduml
|
@enduml
|
||||||
```
|
```
|
||||||
|
|
||||||
### Groups
|
|
||||||
|
|
||||||
It is possible to create semantic groups to make access control more convenient.
|
|
||||||
|
|
||||||
#### User groups
|
#### User groups
|
||||||
|
|
||||||
@ -181,154 +303,16 @@ Rel(secret, c1, "Decrypt", "", "Both machine A or B can decrypt using their priv
|
|||||||
|
|
||||||
<!-- TODO: See also [Groups Reference](#groups-reference) -->
|
<!-- TODO: See also [Groups Reference](#groups-reference) -->
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Adding Machine Keys
|
|
||||||
|
|
||||||
New machines in Clan come with age keys stored in `./sops/machines/<machine_name>`. To list these machines:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ clan secrets machines list
|
|
||||||
```
|
|
||||||
|
|
||||||
For existing machines, add their keys:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ clan secrets machines add <machine_name> <age_key>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Advanced
|
|
||||||
|
|
||||||
To fetch an age key from an SSH host key:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ ssh-keyscan <domain_name> | nix shell nixpkgs#ssh-to-age -c ssh-to-age
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3. Assigning Access
|
|
||||||
|
|
||||||
By default, secrets are encrypted for your key. To specify which users and machines can access a secret:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ clan secrets set --machine <machine1> --machine <machine2> --user <user1> --user <user2> <secret_name>
|
|
||||||
```
|
|
||||||
|
|
||||||
You can add machines/users to existing secrets without modifying the secret:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ clan secrets machines add-secret <machine_name> <secret_name>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 4. Adding Secrets
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ clan secrets set mysecret
|
|
||||||
Paste your secret:
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
As you type your secret won't be displayed. Press Enter to save the secret.
|
|
||||||
|
|
||||||
## 5. Retrieving Stored Secrets
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ clan secrets get mysecret
|
|
||||||
```
|
|
||||||
|
|
||||||
### List all Secrets
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ clan secrets list
|
|
||||||
```
|
|
||||||
|
|
||||||
## 6. Groups
|
|
||||||
|
|
||||||
Clan CLI makes it easy to manage access by allowing you to create groups.
|
|
||||||
|
|
||||||
All users within a group inherit access to all secrets of the group.
|
|
||||||
|
|
||||||
This feature eases the process of handling permissions for multiple users.
|
|
||||||
|
|
||||||
Here's how to get started:
|
|
||||||
|
|
||||||
1. **Creating Groups**:
|
|
||||||
|
|
||||||
Assign users to a new group, e.g., `admins`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ clan secrets groups add admins <username>
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Listing Groups**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ clan secrets groups list
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Assigning Secrets to Groups**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ clan secrets groups add-secret <group_name> <secret_name>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Further
|
|
||||||
|
|
||||||
Secrets in the repository follow this structure:
|
|
||||||
|
|
||||||
```{.console, .no-copy}
|
|
||||||
sops/
|
|
||||||
├── secrets/
|
|
||||||
│ └── <secret_name>/
|
|
||||||
│ ├── secret
|
|
||||||
│ └── users/
|
|
||||||
│ └── <your_username>/
|
|
||||||
```
|
|
||||||
|
|
||||||
The content of the secret is stored encrypted inside the `secret` file under `mysecret`.
|
|
||||||
|
|
||||||
By default, secrets are encrypted with your key to ensure readability.
|
|
||||||
|
|
||||||
### NixOS integration
|
|
||||||
|
|
||||||
A NixOS machine will automatically import all secrets that are encrypted for the
|
|
||||||
current machine. At runtime it will use the host key to decrypt all secrets into
|
|
||||||
an in-memory, non-persistent filesystem using [sops-nix](https://github.com/Mic92/sops-nix).
|
|
||||||
In your nixos configuration you can get a path to secrets like this `config.sops.secrets.<name>.path`. For example:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
{ config, ...}: {
|
|
||||||
sops.secrets.my-password.neededForUsers = true;
|
|
||||||
|
|
||||||
users.users.mic92 = {
|
|
||||||
isNormalUser = true;
|
|
||||||
passwordFile = config.sops.secrets.my-password.path;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
See the [readme](https://github.com/Mic92/sops-nix) of sops-nix for more
|
See the [readme](https://github.com/Mic92/sops-nix) of sops-nix for more
|
||||||
examples.
|
examples.
|
||||||
|
|
||||||
### Migration: Importing existing sops-based keys / sops-nix
|
|
||||||
|
|
||||||
`clan secrets` stores each secret in a single file, whereas [sops](https://github.com/Mic92/sops-nix) commonly allows to put all secrets in a yaml or json document.
|
|
||||||
|
|
||||||
If you already happened to use sops-nix, you can migrate by using the `clan secrets import-sops` command by importing these files:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
% clan secrets import-sops --prefix matchbox- --group admins --machine matchbox nixos/matchbox/secrets/secrets.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
This will create secrets for each secret found in `nixos/matchbox/secrets/secrets.yaml` in a `./sops` folder of your repository.
|
|
||||||
Each member of the group `admins` in this case will be able to decrypt the secrets with their respective key.
|
|
||||||
|
|
||||||
Since our clan secret module will auto-import secrets that are encrypted for a particular nixos machine,
|
|
||||||
you can now remove `sops.secrets.<secrets> = { };` unless you need to specify more options for the secret like owner/group of the secret file.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Whats next?
|
## Whats next?
|
||||||
|
|
||||||
- [Deployment](machines.md): How to remotely deploy your machine
|
- [Deployment](deploy.md): How to remotely deploy your machine
|
||||||
|
|
||||||
---
|
---
|
||||||
|
66
flake.lock
66
flake.lock
@ -7,15 +7,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1714103775,
|
"lastModified": 1714400597,
|
||||||
"narHash": "sha256-kcBiIrmqzt3bNTr2GMBfAyA+on8BEKO1iKzzDFQZkjI=",
|
"narHash": "sha256-AA1TCyEl4O6+6F5man/V5VH9Zl9HPBpK91tSkZ16i2E=",
|
||||||
"owner": "nix-community",
|
"owner": "Qubasa",
|
||||||
"repo": "disko",
|
"repo": "disko",
|
||||||
"rev": "285e26465a0bae510897ca04da26ce6307c652b4",
|
"rev": "58785136b8c37aeb2f67081387b48f663b166331",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nix-community",
|
"owner": "Qubasa",
|
||||||
"repo": "disko",
|
"repo": "disko",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@ -27,11 +27,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1712014858,
|
"lastModified": 1714641030,
|
||||||
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
|
"narHash": "sha256-yzcRNDoyVP7+SCNX0wmuDju1NUCt8Dz9+lyUXEI0dbI=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
|
"rev": "e5d10a24b66c3ea8f150e47dfdb0416ab7c3390e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -55,6 +55,22 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixos-2311": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1715543463,
|
||||||
|
"narHash": "sha256-n3MLdwfCqQT0HtiE2QHCkvyxfaX7wgnO4HkOE1qkNuU=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "6eccabe980dcb2048aab7f97f862fb6d79b98abe",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "release-23.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixos-generators": {
|
"nixos-generators": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixlib": "nixlib",
|
"nixlib": "nixlib",
|
||||||
@ -76,13 +92,34 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixos-images": {
|
||||||
|
"inputs": {
|
||||||
|
"nixos-2311": "nixos-2311",
|
||||||
|
"nixos-unstable": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1715566953,
|
||||||
|
"narHash": "sha256-c49CNk3L1QO0C6urknHCr5+pjmMaVDqBGmt+ECcunv4=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixos-images",
|
||||||
|
"rev": "81e709ca1da9c5c31b65cc8f82a97181f78ec076",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixos-images",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1714290118,
|
"lastModified": 1715777523,
|
||||||
"narHash": "sha256-6PzUtOvU7hMQxZV579B04CHE1HXSq/loh9E+d/4fKZY=",
|
"narHash": "sha256-S6g1OWbKXswOMoTssq3aOm4OhxhlKoIwEAXWmU57vts=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "30ddacc06345a478f9528fa29e2c8857b90381b2",
|
"rev": "c029b7f004009923bbfc90bbc31263cd4b08759f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -97,6 +134,7 @@
|
|||||||
"disko": "disko",
|
"disko": "disko",
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts",
|
||||||
"nixos-generators": "nixos-generators",
|
"nixos-generators": "nixos-generators",
|
||||||
|
"nixos-images": "nixos-images",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"sops-nix": "sops-nix",
|
"sops-nix": "sops-nix",
|
||||||
"treefmt-nix": "treefmt-nix"
|
"treefmt-nix": "treefmt-nix"
|
||||||
@ -110,11 +148,11 @@
|
|||||||
"nixpkgs-stable": []
|
"nixpkgs-stable": []
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1713892811,
|
"lastModified": 1715482972,
|
||||||
"narHash": "sha256-uIGmA2xq41vVFETCF1WW4fFWFT2tqBln+aXnWrvjGRE=",
|
"narHash": "sha256-y1uMzXNlrVOWYj1YNcsGYLm4TOC2aJrwoUY1NjQs9fM=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "f1b0adc27265274e3b0c9b872a8f476a098679bd",
|
"rev": "b6cb5de2ce57acb10ecdaaf9bbd62a5ff24fa02e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -8,14 +8,15 @@
|
|||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable-small";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable-small";
|
||||||
|
disko.url = "github:Qubasa/disko";
|
||||||
disko.url = "github:nix-community/disko";
|
|
||||||
disko.inputs.nixpkgs.follows = "nixpkgs";
|
disko.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
sops-nix.url = "github:Mic92/sops-nix";
|
sops-nix.url = "github:Mic92/sops-nix";
|
||||||
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
|
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
sops-nix.inputs.nixpkgs-stable.follows = "";
|
sops-nix.inputs.nixpkgs-stable.follows = "";
|
||||||
nixos-generators.url = "github:nix-community/nixos-generators";
|
nixos-generators.url = "github:nix-community/nixos-generators";
|
||||||
nixos-generators.inputs.nixpkgs.follows = "nixpkgs";
|
nixos-generators.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
nixos-images.url = "github:nix-community/nixos-images";
|
||||||
|
nixos-images.inputs.nixos-unstable.follows = "nixpkgs";
|
||||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
|
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||||
treefmt-nix.url = "github:numtide/treefmt-nix";
|
treefmt-nix.url = "github:numtide/treefmt-nix";
|
||||||
|
@ -4,6 +4,38 @@
|
|||||||
modulesPath,
|
modulesPath,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
network-status = pkgs.writeShellScript "network-status" ''
|
||||||
|
export PATH=${
|
||||||
|
lib.makeBinPath (
|
||||||
|
with pkgs;
|
||||||
|
[
|
||||||
|
iproute2
|
||||||
|
coreutils
|
||||||
|
gnugrep
|
||||||
|
nettools
|
||||||
|
gum
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
set -efu -o pipefail
|
||||||
|
msgs=()
|
||||||
|
if [[ -e /var/shared/qrcode.utf8 ]]; then
|
||||||
|
qrcode=$(gum style --border-foreground 240 --border normal "$(< /var/shared/qrcode.utf8)")
|
||||||
|
msgs+=("$qrcode")
|
||||||
|
fi
|
||||||
|
network_status="Local network addresses:
|
||||||
|
$(ip -brief -color addr | grep -v 127.0.0.1)
|
||||||
|
$([[ -e /var/shared/onion-hostname ]] && echo "Onion address: $(cat /var/shared/onion-hostname)" || echo "Onion address: Waiting for tor network to be ready...")
|
||||||
|
Multicast DNS: $(hostname).local"
|
||||||
|
network_status=$(gum style --border-foreground 240 --border normal "$network_status")
|
||||||
|
msgs+=("$network_status")
|
||||||
|
msgs+=("Press 'Ctrl-C' for console access")
|
||||||
|
|
||||||
|
gum join --vertical "''${msgs[@]}"
|
||||||
|
'';
|
||||||
|
in
|
||||||
{
|
{
|
||||||
############################################
|
############################################
|
||||||
# #
|
# #
|
||||||
@ -11,19 +43,21 @@
|
|||||||
# $ qemu-kvm result/stick.raw -snapshot #
|
# $ qemu-kvm result/stick.raw -snapshot #
|
||||||
# #
|
# #
|
||||||
############################################
|
############################################
|
||||||
systemd.tmpfiles.rules = [ "d /var/shared 0777 root root - -" ];
|
|
||||||
imports = [
|
imports = [
|
||||||
(modulesPath + "/profiles/installation-device.nix")
|
(modulesPath + "/profiles/installation-device.nix")
|
||||||
(modulesPath + "/profiles/all-hardware.nix")
|
(modulesPath + "/profiles/all-hardware.nix")
|
||||||
(modulesPath + "/profiles/base.nix")
|
(modulesPath + "/profiles/base.nix")
|
||||||
(modulesPath + "/installer/cd-dvd/iso-image.nix")
|
|
||||||
];
|
];
|
||||||
services.openssh.settings.PermitRootLogin = "yes";
|
|
||||||
system.activationScripts.root-password = ''
|
########################################################################################################
|
||||||
mkdir -p /var/shared
|
# #
|
||||||
${pkgs.pwgen}/bin/pwgen -s 16 1 > /var/shared/root-password
|
# Copied from: #
|
||||||
echo "root:$(cat /var/shared/root-password)" | chpasswd
|
# https://github.com/nix-community/nixos-images/blob/main/nix/image-installer/module.nix#L46C3-L117C6 #
|
||||||
'';
|
# #
|
||||||
|
########################################################################################################
|
||||||
|
systemd.tmpfiles.rules = [ "d /var/shared 0777 root root - -" ];
|
||||||
|
services.openssh.settings.PermitRootLogin = lib.mkForce "prohibit-password";
|
||||||
|
|
||||||
hidden-ssh-announce = {
|
hidden-ssh-announce = {
|
||||||
enable = true;
|
enable = true;
|
||||||
script = pkgs.writeShellScript "write-hostname" ''
|
script = pkgs.writeShellScript "write-hostname" ''
|
||||||
@ -44,26 +78,48 @@
|
|||||||
echo "$1" > /var/shared/onion-hostname
|
echo "$1" > /var/shared/onion-hostname
|
||||||
local_addrs=$(ip -json addr | jq '[map(.addr_info) | flatten | .[] | select(.scope == "global") | .local]')
|
local_addrs=$(ip -json addr | jq '[map(.addr_info) | flatten | .[] | select(.scope == "global") | .local]')
|
||||||
jq -nc \
|
jq -nc \
|
||||||
--arg password "$(cat /var/shared/root-password)" \
|
|
||||||
--arg onion_address "$(cat /var/shared/onion-hostname)" \
|
--arg onion_address "$(cat /var/shared/onion-hostname)" \
|
||||||
--argjson local_addrs "$local_addrs" \
|
--argjson local_addrs "$local_addrs" \
|
||||||
'{ pass: $password, onion_address: $onion_address, addrs: $local_addrs }' \
|
'{ pass: null, tor: $onion_address, addrs: $local_addrs }' \
|
||||||
> /var/shared/login.json
|
> /var/shared/login.json
|
||||||
cat /var/shared/login.json | qrencode -t utf8 -o /var/shared/qrcode.utf8
|
cat /var/shared/login.json | qrencode -s 2 -m 2 -t utf8 -o /var/shared/qrcode.utf8
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
services.getty.autologinUser = lib.mkForce "root";
|
services.getty.autologinUser = lib.mkForce "root";
|
||||||
|
|
||||||
|
console.earlySetup = true;
|
||||||
|
console.font = lib.mkDefault "${pkgs.terminus_font}/share/consolefonts/ter-u22n.psf.gz";
|
||||||
|
|
||||||
|
# Less ipv6 addresses to reduce the noise
|
||||||
|
networking.tempAddresses = "disabled";
|
||||||
|
|
||||||
|
# Tango theme: https://yayachiken.net/en/posts/tango-colors-in-terminal/
|
||||||
|
console.colors = lib.mkDefault [
|
||||||
|
"000000"
|
||||||
|
"CC0000"
|
||||||
|
"4E9A06"
|
||||||
|
"C4A000"
|
||||||
|
"3465A4"
|
||||||
|
"75507B"
|
||||||
|
"06989A"
|
||||||
|
"D3D7CF"
|
||||||
|
"555753"
|
||||||
|
"EF2929"
|
||||||
|
"8AE234"
|
||||||
|
"FCE94F"
|
||||||
|
"739FCF"
|
||||||
|
"AD7FA8"
|
||||||
|
"34E2E2"
|
||||||
|
"EEEEEC"
|
||||||
|
];
|
||||||
|
|
||||||
programs.bash.interactiveShellInit = ''
|
programs.bash.interactiveShellInit = ''
|
||||||
if [[ "$(tty)" =~ /dev/(tty1|hvc0|ttyS0)$ ]]; then
|
if [[ "$(tty)" =~ /dev/(tty1|hvc0|ttyS0)$ ]]; then
|
||||||
echo -n 'waiting for tor to generate the hidden service'
|
# workaround for https://github.com/NixOS/nixpkgs/issues/219239
|
||||||
until test -e /var/shared/qrcode.utf8; do echo -n .; sleep 1; done
|
systemctl restart systemd-vconsole-setup.service
|
||||||
echo
|
|
||||||
echo "Root password: $(cat /var/shared/root-password)"
|
watch --no-title --color ${network-status}
|
||||||
echo "Onion address: $(cat /var/shared/onion-hostname)"
|
|
||||||
echo "Local network addresses:"
|
|
||||||
${pkgs.iproute}/bin/ip -brief -color addr | grep -v 127.0.0.1
|
|
||||||
cat /var/shared/qrcode.utf8
|
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
isoImage.squashfsCompression = "zstd";
|
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ let
|
|||||||
boot = {
|
boot = {
|
||||||
size = "1M";
|
size = "1M";
|
||||||
type = "EF02"; # for grub MBR
|
type = "EF02"; # for grub MBR
|
||||||
|
priority = 1; # Needs to be first partition
|
||||||
};
|
};
|
||||||
ESP = {
|
ESP = {
|
||||||
size = "100M";
|
size = "100M";
|
||||||
|
@ -58,6 +58,7 @@ def create_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|||||||
"--debug",
|
"--debug",
|
||||||
help="Enable debug logging",
|
help="Enable debug logging",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -43,6 +43,10 @@ def create_flake(directory: Path, url: str) -> dict[str, CmdOut]:
|
|||||||
out = run(command, cwd=directory)
|
out = run(command, cwd=directory)
|
||||||
response["git config"] = out
|
response["git config"] = out
|
||||||
|
|
||||||
|
command = ["nix", "flake", "update"]
|
||||||
|
out = run(command, cwd=directory)
|
||||||
|
response["flake update"] = out
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import importlib
|
import importlib
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import shlex
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import textwrap
|
import textwrap
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
@ -20,8 +21,71 @@ from .nix import nix_shell
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def list_available_ssh_keys(ssh_dir: Path = Path("~/.ssh").expanduser()) -> list[Path]:
|
||||||
|
"""
|
||||||
|
Function to list all available SSH public keys in the default .ssh directory.
|
||||||
|
Returns a list of paths to available public key files.
|
||||||
|
"""
|
||||||
|
public_key_patterns = ["*.pub"]
|
||||||
|
available_keys: list[Path] = []
|
||||||
|
|
||||||
|
# Check for public key files
|
||||||
|
for pattern in public_key_patterns:
|
||||||
|
for key_path in ssh_dir.glob(pattern):
|
||||||
|
if key_path.is_file():
|
||||||
|
available_keys.append(key_path)
|
||||||
|
|
||||||
|
return available_keys
|
||||||
|
|
||||||
|
|
||||||
|
def read_public_key_contents(public_keys: list[Path]) -> list[str]:
|
||||||
|
"""
|
||||||
|
Function to read and return the contents of available SSH public keys.
|
||||||
|
Returns a list containing the contents of each public key.
|
||||||
|
"""
|
||||||
|
public_key_contents = []
|
||||||
|
|
||||||
|
for key_path in public_keys:
|
||||||
|
try:
|
||||||
|
with open(key_path.expanduser()) as key_file:
|
||||||
|
public_key_contents.append(key_file.read().strip())
|
||||||
|
except FileNotFoundError:
|
||||||
|
log.error(f"Public key file not found: {key_path}")
|
||||||
|
|
||||||
|
return public_key_contents
|
||||||
|
|
||||||
|
|
||||||
|
def get_keymap_and_locale() -> dict[str, str]:
|
||||||
|
locale = "en_US.UTF-8"
|
||||||
|
keymap = "en"
|
||||||
|
|
||||||
|
# Execute the `localectl status` command
|
||||||
|
result = run(["localectl", "status"])
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
output = result.stdout
|
||||||
|
|
||||||
|
# Extract the Keymap (X11 Layout)
|
||||||
|
keymap_match = re.search(r"X11 Layout:\s+(.*)", output)
|
||||||
|
if keymap_match:
|
||||||
|
keymap = keymap_match.group(1)
|
||||||
|
|
||||||
|
# Extract the System Locale (LANG only)
|
||||||
|
locale_match = re.search(r"System Locale:\s+LANG=(.*)", output)
|
||||||
|
if locale_match:
|
||||||
|
locale = locale_match.group(1)
|
||||||
|
|
||||||
|
return {"keymap": keymap, "locale": locale}
|
||||||
|
|
||||||
|
|
||||||
def flash_machine(
|
def flash_machine(
|
||||||
machine: Machine, mode: str, disks: dict[str, str], dry_run: bool, debug: bool
|
machine: Machine,
|
||||||
|
*,
|
||||||
|
mode: str,
|
||||||
|
disks: dict[str, str],
|
||||||
|
system_config: dict[str, Any],
|
||||||
|
dry_run: bool,
|
||||||
|
debug: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
secret_facts_module = importlib.import_module(machine.secret_facts_module)
|
secret_facts_module = importlib.import_module(machine.secret_facts_module)
|
||||||
secret_facts_store: SecretStoreBase = secret_facts_module.SecretStore(
|
secret_facts_store: SecretStoreBase = secret_facts_module.SecretStore(
|
||||||
@ -58,12 +122,17 @@ def flash_machine(
|
|||||||
disko_install.extend(["--extra-files", str(local_dir), upload_dir])
|
disko_install.extend(["--extra-files", str(local_dir), upload_dir])
|
||||||
disko_install.extend(["--flake", str(machine.flake) + "#" + machine.name])
|
disko_install.extend(["--flake", str(machine.flake) + "#" + machine.name])
|
||||||
disko_install.extend(["--mode", str(mode)])
|
disko_install.extend(["--mode", str(mode)])
|
||||||
|
disko_install.extend(
|
||||||
|
[
|
||||||
|
"--system-config",
|
||||||
|
json.dumps(system_config),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
cmd = nix_shell(
|
cmd = nix_shell(
|
||||||
["nixpkgs#disko"],
|
["/home/lhebendanz/Projects/disko"],
|
||||||
disko_install,
|
disko_install,
|
||||||
)
|
)
|
||||||
print("$", " ".join(map(shlex.quote, cmd)))
|
|
||||||
run(cmd, log=Log.BOTH, error_msg=f"Failed to flash {machine}")
|
run(cmd, log=Log.BOTH, error_msg=f"Failed to flash {machine}")
|
||||||
|
|
||||||
|
|
||||||
@ -72,10 +141,13 @@ class FlashOptions:
|
|||||||
flake: Path
|
flake: Path
|
||||||
machine: str
|
machine: str
|
||||||
disks: dict[str, str]
|
disks: dict[str, str]
|
||||||
|
ssh_keys_path: list[Path]
|
||||||
dry_run: bool
|
dry_run: bool
|
||||||
confirm: bool
|
confirm: bool
|
||||||
debug: bool
|
debug: bool
|
||||||
mode: str
|
mode: str
|
||||||
|
language: str
|
||||||
|
keymap: str
|
||||||
|
|
||||||
|
|
||||||
class AppendDiskAction(argparse.Action):
|
class AppendDiskAction(argparse.Action):
|
||||||
@ -99,11 +171,15 @@ def flash_command(args: argparse.Namespace) -> None:
|
|||||||
flake=args.flake,
|
flake=args.flake,
|
||||||
machine=args.machine,
|
machine=args.machine,
|
||||||
disks=args.disk,
|
disks=args.disk,
|
||||||
|
ssh_keys_path=args.ssh_pubkey,
|
||||||
dry_run=args.dry_run,
|
dry_run=args.dry_run,
|
||||||
confirm=not args.yes,
|
confirm=not args.yes,
|
||||||
debug=args.debug,
|
debug=args.debug,
|
||||||
mode=args.mode,
|
mode=args.mode,
|
||||||
|
language=args.lang,
|
||||||
|
keymap=args.keymap,
|
||||||
)
|
)
|
||||||
|
|
||||||
machine = Machine(opts.machine, flake=opts.flake)
|
machine = Machine(opts.machine, flake=opts.flake)
|
||||||
if opts.confirm and not opts.dry_run:
|
if opts.confirm and not opts.dry_run:
|
||||||
disk_str = ", ".join(f"{name}={device}" for name, device in opts.disks.items())
|
disk_str = ", ".join(f"{name}={device}" for name, device in opts.disks.items())
|
||||||
@ -114,8 +190,49 @@ def flash_command(args: argparse.Namespace) -> None:
|
|||||||
ask = input(msg)
|
ask = input(msg)
|
||||||
if ask != "y":
|
if ask != "y":
|
||||||
return
|
return
|
||||||
|
|
||||||
|
root_keys = read_public_key_contents(opts.ssh_keys_path)
|
||||||
|
if opts.confirm and not root_keys:
|
||||||
|
msg = "Should we add your SSH public keys to the root user? [y/N] "
|
||||||
|
ask = input(msg)
|
||||||
|
if ask == "y":
|
||||||
|
pubkeys = list_available_ssh_keys()
|
||||||
|
root_keys.extend(read_public_key_contents(pubkeys))
|
||||||
|
else:
|
||||||
|
raise ClanError(
|
||||||
|
"No SSH public keys provided. Use --ssh-pubkey to add keys."
|
||||||
|
)
|
||||||
|
elif not opts.confirm and not root_keys:
|
||||||
|
pubkeys = list_available_ssh_keys()
|
||||||
|
root_keys.extend(read_public_key_contents(pubkeys))
|
||||||
|
# If ssh-pubkeys set, we don't need to ask for confirmation
|
||||||
|
elif opts.confirm and root_keys:
|
||||||
|
pass
|
||||||
|
elif not opts.confirm and root_keys:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise ClanError("Invalid state")
|
||||||
|
|
||||||
|
localectl = get_keymap_and_locale()
|
||||||
|
extra_config = {
|
||||||
|
"users": {
|
||||||
|
"users": {"root": {"openssh": {"authorizedKeys": {"keys": root_keys}}}}
|
||||||
|
},
|
||||||
|
"console": {
|
||||||
|
"keyMap": opts.keymap if opts.keymap else localectl["keymap"],
|
||||||
|
},
|
||||||
|
"i18n": {
|
||||||
|
"defaultLocale": opts.language if opts.language else localectl["locale"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
flash_machine(
|
flash_machine(
|
||||||
machine, opts.mode, disks=opts.disks, dry_run=opts.dry_run, debug=opts.debug
|
machine,
|
||||||
|
mode=opts.mode,
|
||||||
|
disks=opts.disks,
|
||||||
|
system_config=extra_config,
|
||||||
|
dry_run=opts.dry_run,
|
||||||
|
debug=opts.debug,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -147,7 +264,23 @@ def register_parser(parser: argparse.ArgumentParser) -> None:
|
|||||||
choices=["format", "mount"],
|
choices=["format", "mount"],
|
||||||
default="format",
|
default="format",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--ssh-pubkey",
|
||||||
|
type=Path,
|
||||||
|
action="append",
|
||||||
|
default=[],
|
||||||
|
help="ssh pubkey file to add to the root user. Can be used multiple times",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--lang",
|
||||||
|
type=str,
|
||||||
|
help="system language",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--keymap",
|
||||||
|
type=str,
|
||||||
|
help="system keymap",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--yes",
|
"--yes",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
@ -160,10 +293,4 @@ def register_parser(parser: argparse.ArgumentParser) -> None:
|
|||||||
default=False,
|
default=False,
|
||||||
action="store_true",
|
action="store_true",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
|
||||||
"--debug",
|
|
||||||
help="Print debug information",
|
|
||||||
default=False,
|
|
||||||
action="store_true",
|
|
||||||
)
|
|
||||||
parser.set_defaults(func=flash_command)
|
parser.set_defaults(func=flash_command)
|
||||||
|
@ -25,6 +25,7 @@ def install_nixos(
|
|||||||
kexec: str | None = None,
|
kexec: str | None = None,
|
||||||
debug: bool = False,
|
debug: bool = False,
|
||||||
password: str | None = None,
|
password: str | None = None,
|
||||||
|
no_reboot: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
secret_facts_module = importlib.import_module(machine.secret_facts_module)
|
secret_facts_module = importlib.import_module(machine.secret_facts_module)
|
||||||
log.info(f"installing {machine.name}")
|
log.info(f"installing {machine.name}")
|
||||||
@ -53,11 +54,13 @@ def install_nixos(
|
|||||||
"nixos-anywhere",
|
"nixos-anywhere",
|
||||||
"--flake",
|
"--flake",
|
||||||
f"{machine.flake}#{machine.name}",
|
f"{machine.flake}#{machine.name}",
|
||||||
"--no-reboot",
|
|
||||||
"--extra-files",
|
"--extra-files",
|
||||||
str(tmpdir),
|
str(tmpdir),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if no_reboot:
|
||||||
|
cmd.append("--no-reboot")
|
||||||
|
|
||||||
if password:
|
if password:
|
||||||
cmd += [
|
cmd += [
|
||||||
"--env-password",
|
"--env-password",
|
||||||
@ -90,6 +93,7 @@ class InstallOptions:
|
|||||||
kexec: str | None
|
kexec: str | None
|
||||||
confirm: bool
|
confirm: bool
|
||||||
debug: bool
|
debug: bool
|
||||||
|
no_reboot: bool
|
||||||
json_ssh_deploy: dict[str, str] | None
|
json_ssh_deploy: dict[str, str] | None
|
||||||
|
|
||||||
|
|
||||||
@ -121,6 +125,7 @@ def install_command(args: argparse.Namespace) -> None:
|
|||||||
kexec=args.kexec,
|
kexec=args.kexec,
|
||||||
confirm=not args.yes,
|
confirm=not args.yes,
|
||||||
debug=args.debug,
|
debug=args.debug,
|
||||||
|
no_reboot=args.no_reboot,
|
||||||
json_ssh_deploy=json_ssh_deploy,
|
json_ssh_deploy=json_ssh_deploy,
|
||||||
)
|
)
|
||||||
machine = Machine(opts.machine, flake=opts.flake)
|
machine = Machine(opts.machine, flake=opts.flake)
|
||||||
@ -131,7 +136,13 @@ def install_command(args: argparse.Namespace) -> None:
|
|||||||
if ask != "y":
|
if ask != "y":
|
||||||
return
|
return
|
||||||
|
|
||||||
install_nixos(machine, kexec=opts.kexec, debug=opts.debug, password=password)
|
install_nixos(
|
||||||
|
machine,
|
||||||
|
kexec=opts.kexec,
|
||||||
|
debug=opts.debug,
|
||||||
|
password=password,
|
||||||
|
no_reboot=opts.no_reboot,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def find_reachable_host_from_deploy_json(deploy_json: dict[str, str]) -> str:
|
def find_reachable_host_from_deploy_json(deploy_json: dict[str, str]) -> str:
|
||||||
@ -161,15 +172,15 @@ def register_install_parser(parser: argparse.ArgumentParser) -> None:
|
|||||||
help="use another kexec tarball to bootstrap NixOS",
|
help="use another kexec tarball to bootstrap NixOS",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--yes",
|
"--no-reboot",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="do not ask for confirmation",
|
help="do not reboot after installation",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--debug",
|
"--yes",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="print debug information",
|
help="do not ask for confirmation",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -106,13 +106,7 @@ def nix_shell(packages: list[str], cmd: list[str]) -> list[str]:
|
|||||||
if os.environ.get("IN_NIX_SANDBOX"):
|
if os.environ.get("IN_NIX_SANDBOX"):
|
||||||
return cmd
|
return cmd
|
||||||
return [
|
return [
|
||||||
*nix_command(
|
*nix_command(["shell", "--offline", "--inputs-from", f"{nixpkgs_flake()!s}"]),
|
||||||
[
|
|
||||||
"shell",
|
|
||||||
"--inputs-from",
|
|
||||||
f"{nixpkgs_flake()!s}",
|
|
||||||
]
|
|
||||||
),
|
|
||||||
*packages,
|
*packages,
|
||||||
"-c",
|
"-c",
|
||||||
*cmd,
|
*cmd,
|
||||||
|
@ -23,11 +23,11 @@
|
|||||||
zbar,
|
zbar,
|
||||||
tor,
|
tor,
|
||||||
git,
|
git,
|
||||||
nixpkgs,
|
|
||||||
qemu,
|
qemu,
|
||||||
gnupg,
|
gnupg,
|
||||||
e2fsprogs,
|
e2fsprogs,
|
||||||
mypy,
|
mypy,
|
||||||
|
nixpkgs,
|
||||||
clan-core-path,
|
clan-core-path,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
@ -95,17 +95,16 @@ let
|
|||||||
description = "dependencies for the clan-cli";
|
description = "dependencies for the clan-cli";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "nixpkgs";
|
nixpkgs.url = "path://${nixpkgs}";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = _inputs: { };
|
outputs = _inputs: { };
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
ln -s ${nixpkgs} $out/path
|
ln -s ${nixpkgs} $out/path
|
||||||
nix flake lock $out \
|
nix flake update $out \
|
||||||
--store ./. \
|
--store ./. \
|
||||||
--extra-experimental-features 'nix-command flakes' \
|
--extra-experimental-features 'nix-command flakes'
|
||||||
--override-input nixpkgs ${nixpkgs}
|
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
python3.pkgs.buildPythonApplication {
|
python3.pkgs.buildPythonApplication {
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
||||||
devShells.clan-cli = pkgs.callPackage ./shell.nix { inherit (self'.packages) clan-cli; };
|
devShells.clan-cli = pkgs.callPackage ./shell.nix { inherit (self'.packages) clan-cli; };
|
||||||
packages = {
|
packages = {
|
||||||
clan-cli = pkgs.python3.pkgs.callPackage ./default.nix {
|
clan-cli = pkgs.python3.pkgs.callPackage ./default.nix {
|
||||||
|
@ -12,6 +12,7 @@ let
|
|||||||
rope
|
rope
|
||||||
setuptools
|
setuptools
|
||||||
wheel
|
wheel
|
||||||
|
ipdb
|
||||||
pip
|
pip
|
||||||
]);
|
]);
|
||||||
in
|
in
|
||||||
@ -21,6 +22,8 @@ mkShell {
|
|||||||
ruff
|
ruff
|
||||||
] ++ devshellTestDeps;
|
] ++ devshellTestDeps;
|
||||||
|
|
||||||
|
PYTHONBREAKPOINT = "ipdb.set_trace";
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
export GIT_ROOT="$(git rev-parse --show-toplevel)"
|
export GIT_ROOT="$(git rev-parse --show-toplevel)"
|
||||||
export PKG_ROOT="$GIT_ROOT/pkgs/clan-cli"
|
export PKG_ROOT="$GIT_ROOT/pkgs/clan-cli"
|
||||||
|
@ -40,6 +40,8 @@ mkShell {
|
|||||||
desktop-file-utils # verify desktop files
|
desktop-file-utils # verify desktop files
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
PYTHONBREAKPOINT = "ipdb.set_trace";
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
export GIT_ROOT=$(git rev-parse --show-toplevel)
|
export GIT_ROOT=$(git rev-parse --show-toplevel)
|
||||||
export PKG_ROOT=$GIT_ROOT/pkgs/clan-vm-manager
|
export PKG_ROOT=$GIT_ROOT/pkgs/clan-vm-manager
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./clan-cli/flake-module.nix
|
./clan-cli/flake-module.nix
|
||||||
|
@ -1,51 +1,123 @@
|
|||||||
{ self, lib, ... }:
|
{ self, lib, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
installerModule =
|
wifiModule =
|
||||||
{ config, ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
# use iwd instead of wpa_supplicant
|
||||||
self.nixosModules.installer
|
|
||||||
self.inputs.nixos-generators.nixosModules.all-formats
|
|
||||||
];
|
|
||||||
# Provide convenience for connecting to wifi
|
|
||||||
networking.wireless.enable = false;
|
networking.wireless.enable = false;
|
||||||
|
|
||||||
# Use iwd instead of wpa_supplicant. It has a user friendly CLI
|
# Use iwd instead of wpa_supplicant. It has a user friendly CLI
|
||||||
networking.wireless.iwd = {
|
networking.wireless.iwd = {
|
||||||
|
enable = true;
|
||||||
settings = {
|
settings = {
|
||||||
Network = {
|
Network = {
|
||||||
EnableIPv6 = true;
|
EnableIPv6 = true;
|
||||||
RoutePriorityOffset = 300;
|
RoutePriorityOffset = 300;
|
||||||
};
|
};
|
||||||
Settings = {
|
Settings.AutoConnect = true;
|
||||||
AutoConnect = true;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
enable = true;
|
|
||||||
};
|
};
|
||||||
|
installerModule =
|
||||||
|
{ config, modulesPath, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
wifiModule
|
||||||
|
self.nixosModules.installer
|
||||||
|
self.inputs.nixos-generators.nixosModules.all-formats
|
||||||
|
(modulesPath + "/installer/cd-dvd/iso-image.nix")
|
||||||
|
];
|
||||||
|
|
||||||
|
isoImage.squashfsCompression = "zstd";
|
||||||
|
|
||||||
system.stateVersion = config.system.nixos.version;
|
system.stateVersion = config.system.nixos.version;
|
||||||
nixpkgs.pkgs = self.inputs.nixpkgs.legacyPackages.x86_64-linux;
|
nixpkgs.pkgs = self.inputs.nixpkgs.legacyPackages.x86_64-linux;
|
||||||
};
|
};
|
||||||
|
|
||||||
installer = lib.nixosSystem {
|
installerSystem = lib.nixosSystem {
|
||||||
modules = [
|
modules = [
|
||||||
self.inputs.disko.nixosModules.default
|
self.inputs.disko.nixosModules.default
|
||||||
installerModule
|
installerModule
|
||||||
{ disko.memSize = 4096; } # FIXME: otherwise the image builder goes OOM
|
{ disko.memSize = 4096; } # FIXME: otherwise the image builder goes OOM
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
flashInstallerModule =
|
||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
wifiModule
|
||||||
|
self.nixosModules.installer
|
||||||
|
];
|
||||||
|
system.stateVersion = config.system.nixos.version;
|
||||||
|
nixpkgs.pkgs = self.inputs.nixpkgs.legacyPackages.x86_64-linux;
|
||||||
|
}
|
||||||
|
// flashDiskoConfig;
|
||||||
|
|
||||||
|
# Important: The partition names need to be different to the clan install
|
||||||
|
flashDiskoConfig = {
|
||||||
|
boot.loader.grub.efiSupport = lib.mkDefault true;
|
||||||
|
boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true;
|
||||||
|
disko.devices = {
|
||||||
|
disk = {
|
||||||
|
main = {
|
||||||
|
type = "disk";
|
||||||
|
device = lib.mkDefault "/dev/null";
|
||||||
|
content = {
|
||||||
|
type = "gpt";
|
||||||
|
partitions = {
|
||||||
|
installer-boot = {
|
||||||
|
size = "1M";
|
||||||
|
type = "EF02"; # for grub MBR
|
||||||
|
priority = 1;
|
||||||
|
};
|
||||||
|
installer-ESP = {
|
||||||
|
size = "512M";
|
||||||
|
type = "EF00";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "vfat";
|
||||||
|
mountpoint = "/boot";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
installer-root = {
|
||||||
|
size = "100%";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "ext4";
|
||||||
|
mountpoint = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
||||||
clan = {
|
clan = {
|
||||||
clanName = "clan-core";
|
clanName = "clan-core";
|
||||||
directory = self;
|
directory = self;
|
||||||
machines.installer = {
|
|
||||||
|
# To build a generic installer image (without ssh pubkeys),
|
||||||
|
# use the following command:
|
||||||
|
# $ nix build .#iso-installer
|
||||||
|
machines.iso-installer = {
|
||||||
imports = [ installerModule ];
|
imports = [ installerModule ];
|
||||||
fileSystems."/".device = lib.mkDefault "/dev/null";
|
fileSystems."/".device = lib.mkDefault "/dev/null";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# To directly flash the installer to a disk, use the following command:
|
||||||
|
# $ clan flash flash-installer --disk main /dev/sdX --yes
|
||||||
|
# This will include your ssh public keys in the installer.
|
||||||
|
machines.flash-installer = {
|
||||||
|
imports = [ flashInstallerModule ];
|
||||||
|
boot.loader.grub.enable = lib.mkDefault true;
|
||||||
};
|
};
|
||||||
flake.packages.x86_64-linux.install-iso = installer.config.formats.iso;
|
};
|
||||||
flake.apps.x86_64-linux.install-vm.program = installer.config.formats.vm.outPath;
|
flake.packages.x86_64-linux.iso-installer = installerSystem.config.formats.iso;
|
||||||
flake.apps.x86_64-linux.install-vm-nogui.program = installer.config.formats.vm-nogui.outPath;
|
flake.apps.x86_64-linux.install-vm.program = installerSystem.config.formats.vm.outPath;
|
||||||
|
flake.apps.x86_64-linux.install-vm-nogui.program = installerSystem.config.formats.vm-nogui.outPath;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
# local> mkdir -p ./machines/machine1
|
# local> mkdir -p ./machines/machine1
|
||||||
# local> Edit ./machines/machine1/configuration.nix to your liking
|
# local> Edit ./machines/machine1/configuration.nix to your liking
|
||||||
machines = {
|
machines = {
|
||||||
|
# "jon" will be the hostname of the machine
|
||||||
jon = {
|
jon = {
|
||||||
imports = [
|
imports = [
|
||||||
./modules/shared.nix
|
./modules/shared.nix
|
||||||
@ -31,18 +32,27 @@
|
|||||||
clanCore.machineIcon = null; # Optional, a path to an image file
|
clanCore.machineIcon = null; # Optional, a path to an image file
|
||||||
|
|
||||||
# Set this for clan commands use ssh i.e. `clan machines update`
|
# Set this for clan commands use ssh i.e. `clan machines update`
|
||||||
|
# If you change the hostname, you need to update this line to root@<new-hostname>
|
||||||
|
# This only works however if you have avahi running on your admin machine else use IP
|
||||||
clan.networking.targetHost = pkgs.lib.mkDefault "root@jon";
|
clan.networking.targetHost = pkgs.lib.mkDefault "root@jon";
|
||||||
|
|
||||||
# TODO: Example how to use disko for more complicated setups
|
|
||||||
|
|
||||||
# ssh root@flash-installer.local lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
|
# ssh root@flash-installer.local lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
|
||||||
disko.devices.disk.main = {
|
disko.devices.disk.main = {
|
||||||
device = "/dev/disk/by-id/__CHANGE_ME__";
|
device = "/dev/disk/by-id/__CHANGE_ME__";
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: Document that there needs to be one controller
|
# IMPORTANT! Add your SSH key here
|
||||||
|
# e.g. > cat ~/.ssh/id_ed25519.pub
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = throw ''
|
||||||
|
Don't forget to add your SSH key here!
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = [ "<YOUR SSH_KEY>" ]
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Zerotier needs one controller to accept new nodes. Once accepted
|
||||||
|
# the controller can be offline and routing still works.
|
||||||
clan.networking.zerotier.controller.enable = true;
|
clan.networking.zerotier.controller.enable = true;
|
||||||
};
|
};
|
||||||
|
# "sara" will be the hostname of the machine
|
||||||
sara = {
|
sara = {
|
||||||
imports = [
|
imports = [
|
||||||
./modules/shared.nix
|
./modules/shared.nix
|
||||||
@ -55,14 +65,22 @@
|
|||||||
clanCore.machineIcon = null; # Optional, a path to an image file
|
clanCore.machineIcon = null; # Optional, a path to an image file
|
||||||
|
|
||||||
# Set this for clan commands use ssh i.e. `clan machines update`
|
# Set this for clan commands use ssh i.e. `clan machines update`
|
||||||
|
# If you change the hostname, you need to update this line to root@<new-hostname>
|
||||||
|
# This only works however if you have avahi running on your admin machine else use IP
|
||||||
clan.networking.targetHost = pkgs.lib.mkDefault "root@sara";
|
clan.networking.targetHost = pkgs.lib.mkDefault "root@sara";
|
||||||
|
|
||||||
# local> clan facts generate
|
|
||||||
|
|
||||||
# ssh root@flash-installer.local lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
|
# ssh root@flash-installer.local lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
|
||||||
disko.devices.disk.main = {
|
disko.devices.disk.main = {
|
||||||
device = "/dev/disk/by-id/__CHANGE_ME__";
|
device = "/dev/disk/by-id/__CHANGE_ME__";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# IMPORTANT! Add your SSH key here
|
||||||
|
# e.g. > cat ~/.ssh/id_ed25519.pub
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = throw ''
|
||||||
|
Don't forget to add your SSH key here!
|
||||||
|
users.users.root.openssh.authorizedKeys.keys = [ "<YOUR SSH_KEY>" ]
|
||||||
|
'';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
After jon is deployed, uncomment the following line
|
After jon is deployed, uncomment the following line
|
||||||
This will allow sara to share the VPN overlay network with jon
|
This will allow sara to share the VPN overlay network with jon
|
||||||
|
@ -1,15 +1,35 @@
|
|||||||
{ ... }:
|
{ config, ... }:
|
||||||
|
let
|
||||||
|
username = config.networking.hostName;
|
||||||
|
in
|
||||||
{
|
{
|
||||||
imports = [ ./hardware-configuration.nix ];
|
imports = [ ./hardware-configuration.nix ];
|
||||||
users.users.root.openssh.authorizedKeys.keys = [
|
|
||||||
# IMPORTANT! Add your SSH key here
|
|
||||||
# e.g. > cat ~/.ssh/id_ed25519.pub
|
|
||||||
"<YOUR SSH_KEY>"
|
|
||||||
];
|
|
||||||
|
|
||||||
services.xserver.enable = true;
|
services.xserver.enable = true;
|
||||||
services.xserver.desktopManager.gnome.enable = true;
|
services.xserver.desktopManager.gnome.enable = true;
|
||||||
services.xserver.displayManager.gdm.enable = true;
|
services.xserver.displayManager.gdm.enable = true;
|
||||||
# Disable the default gnome apps to speed up deployment
|
# Disable the default gnome apps to speed up deployment
|
||||||
services.gnome.core-utilities.enable = false;
|
services.gnome.core-utilities.enable = false;
|
||||||
|
|
||||||
|
# Enable automatic login for the user.
|
||||||
|
services.displayManager.autoLogin = {
|
||||||
|
enable = true;
|
||||||
|
user = username;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.${username} = {
|
||||||
|
initialPassword = username;
|
||||||
|
isNormalUser = true;
|
||||||
|
extraGroups = [
|
||||||
|
"wheel"
|
||||||
|
"networkmanager"
|
||||||
|
"video"
|
||||||
|
"audio"
|
||||||
|
"input"
|
||||||
|
"dialout"
|
||||||
|
"disk"
|
||||||
|
];
|
||||||
|
uid = 1000;
|
||||||
|
openssh.authorizedKeys.keys = config.users.users.root.openssh.authorizedKeys.keys;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,36 @@
|
|||||||
{ ... }:
|
{ config, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
username = config.networking.hostName;
|
||||||
|
in
|
||||||
{
|
{
|
||||||
imports = [ ./hardware-configuration.nix ];
|
imports = [ ./hardware-configuration.nix ];
|
||||||
users.users.root.openssh.authorizedKeys.keys = [
|
|
||||||
# IMPORTANT! Add your SSH key here
|
|
||||||
# e.g. > cat ~/.ssh/id_ed25519.pub
|
|
||||||
"<YOUR SSH_KEY>"
|
|
||||||
];
|
|
||||||
|
|
||||||
services.xserver.enable = true;
|
services.xserver.enable = true;
|
||||||
services.xserver.desktopManager.gnome.enable = true;
|
services.xserver.desktopManager.gnome.enable = true;
|
||||||
services.xserver.displayManager.gdm.enable = true;
|
services.xserver.displayManager.gdm.enable = true;
|
||||||
# Disable the default gnome apps to speed up deployment
|
# Disable the default gnome apps to speed up deployment
|
||||||
services.gnome.core-utilities.enable = false;
|
services.gnome.core-utilities.enable = false;
|
||||||
|
|
||||||
|
# Enable automatic login for the user.
|
||||||
|
services.displayManager.autoLogin = {
|
||||||
|
enable = true;
|
||||||
|
user = username;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.${username} = {
|
||||||
|
initialPassword = username;
|
||||||
|
isNormalUser = true;
|
||||||
|
extraGroups = [
|
||||||
|
"wheel"
|
||||||
|
"networkmanager"
|
||||||
|
"video"
|
||||||
|
"audio"
|
||||||
|
"input"
|
||||||
|
"dialout"
|
||||||
|
"disk"
|
||||||
|
];
|
||||||
|
uid = 1000;
|
||||||
|
openssh.authorizedKeys.keys = config.users.users.root.openssh.authorizedKeys.keys;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
{ lib, ... }:
|
||||||
{
|
{
|
||||||
|
boot.loader.grub.efiSupport = lib.mkDefault true;
|
||||||
|
boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true;
|
||||||
disko.devices = {
|
disko.devices = {
|
||||||
disk = {
|
disk = {
|
||||||
main = {
|
main = {
|
||||||
@ -11,6 +14,7 @@
|
|||||||
boot = {
|
boot = {
|
||||||
size = "1M";
|
size = "1M";
|
||||||
type = "EF02"; # for grub MBR
|
type = "EF02"; # for grub MBR
|
||||||
|
priority = 1;
|
||||||
};
|
};
|
||||||
ESP = {
|
ESP = {
|
||||||
size = "512M";
|
size = "512M";
|
||||||
|
@ -4,4 +4,7 @@
|
|||||||
clan-core.clanModules.sshd
|
clan-core.clanModules.sshd
|
||||||
clan-core.clanModules.root-password
|
clan-core.clanModules.root-password
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Locale service discovery and mDNS
|
||||||
|
services.avahi.enable = true;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user