|
All checks were successful
CI / format (push) Successful in 28s
CI / lint (push) Successful in 1m44s
CI / test (push) Successful in 1m46s
CI / build (push) Successful in 2m3s
CI / build (pull_request) Successful in 1m37s
CI / format (pull_request) Successful in 18s
CI / lint (pull_request) Successful in 1m16s
CI / test (pull_request) Successful in 1m18s
|
||
|---|---|---|
| .forgejo/workflows | ||
| src | ||
| .gitignore | ||
| Cargo.toml | ||
| example.yaml | ||
| README.md | ||
hero-init
hero-init is a system initialization tool designed to configure Linux systems at boot time using a SEED device. It reads configuration from a YAML file on a mounted SEED device and applies various system configurations including network settings, user accounts, hostname, and custom commands.
Table of Contents
- Overview
- Features
- How It Works
- Configuration
- Module Functionalities
- Tool Functions
- How to Configure
- Adding Configuration
- Seed Partition
- Example Configuration
Overview
hero-init is a Rust-based system initialization tool that runs at boot time to configure a Linux system based on a configuration file stored on a SEED device. The SEED device is a specially labeled storage device that contains the configuration needed to initialize the system.
The tool is designed to be idempotent, meaning it can be run multiple times without causing issues. It tracks which configuration modules have been applied using a state file to prevent re-applying configurations on subsequent boots.
Features
- Automatic discovery and mounting of SEED device
- Network configuration (static IPs, DHCP, routes, DNS)
- User account management (creation, SSH keys, sudo access)
- Hostname and hosts file configuration
- Custom command execution with environment variables
- Idempotent operation with state tracking
- Support for multiple network providers (netplan, ifupdown)
- Comprehensive logging
How It Works
- At boot time, hero-init searches for a block device labeled "SEED"
- The SEED device is mounted read-only
- Configuration is loaded from
hero-init.yamlon the mounted device - The tool checks if this is the first boot by comparing instance IDs
- If it's the first boot, configuration modules are applied in order:
- Metadata (hostname, instance ID)
- Network configuration
- User account creation
- Custom commands execution
- Each module's completion is tracked in a state file to prevent re-application
Configuration
Configuration Structure
The configuration file (hero-init.yaml) uses YAML format and contains the following top-level sections:
instance_id: "unique-identifier"
hostname: "system-hostname"
network:
# Network configuration
users:
- # User configurations
runcmd:
- # Custom commands
Metadata Configuration
The metadata section defines system identification:
instance_id: "hero-001" # Unique identifier for this instance
hostname: "hero-vm" # System hostname
Network Configuration
The network section configures network interfaces:
network:
provider: netplan # or ifupdown
interfaces:
- name: ens3
mac: "52:54:00:12:34:56"
dhcp4: false
addresses:
- 10.0.2.100/24
routes:
- to: default
via: 10.0.2.2
nameservers:
addresses:
- 10.0.2.3
- 8.8.8.8
search:
- hero.local
Important: Network configuration files are written but not applied automatically. You must add the appropriate network commands to the runcmd section to apply the network configuration:
For netplan provider:
runcmd:
- "netplan apply"
For ifupdown provider:
runcmd:
- "systemctl restart networking"
User Configuration
The users section defines user accounts to create:
users:
- name: john
ssh_authorized_keys:
- "ssh-ed25519 AAAAC.... john@hero-test"
groups:
- sudo
sudo: true
Run Commands
The runcmd section allows execution of custom commands:
runcmd:
- "echo 'Hello from hero-init' >> /tmp/test1.txt"
- cmd: "echo $MY_VAR > /tmp/test2.txt"
env:
MY_VAR: "190"
Module Functionalities
Discovery Module
The discovery module is responsible for finding and mounting the SEED device:
- Searches for block devices with the label "SEED" in
/dev/disk/by-label - Mounts the device read-only to
/run/hero-init/seed - Provides functions for device capacity detection and partition management
Key functions:
find_seed_device(label: &str) -> Option<PathBuf>- Finds a device by labelmount_seed(device: PathBuf, target: &Path) -> io::Result<()>- Mounts the device
Metadata Module
The metadata module handles system identification configuration:
- Sets the system hostname using both file update and system call
- Writes the instance ID to persistent storage
- Updates the hosts file with the new hostname
Key functions:
set_hostname(hostname: &str) -> Result<()>- Sets system hostnamewrite_instance_id(instance_id: &str) -> Result<()>- Persists instance IDupdate_hosts_file(hostname: &str) -> Result<()>- Updates /etc/hosts
Network Module
The network module configures network interfaces:
- Supports both netplan and ifupdown providers
- Configures static IPs, DHCP, routes, and DNS settings
- Generates appropriate configuration files based on provider
- Writes network configuration files (manual application required via runcmd)
Key functions:
apply(network: &Network) -> Result<()>- Main network configuration functionwrite_network_config(config_yaml: &str, provider: NetworkConfigType) -> io::Result<()>- Writes network config file
Users Module
The users module manages user accounts:
- Creates system users with specified shells and groups
- Sets up SSH authorized keys for passwordless authentication
- Configures sudo access for users
- Sets proper file permissions and ownership
Key functions:
add_system_user(username: &str, shell: &str, groups: &[&str]) -> Result<()>- Creates a userinject_ssh_keys(username: &str, keys: &[String]) -> Result<()>- Sets up SSH keysadd_sudo_rule(username: &str) -> Result<()>- Grants sudo accessapply(users: &[User]) -> Result<()>- Main user configuration function
Execution Module
The execution module handles custom command execution:
- Runs shell commands with optional environment variables
- Provides atomic file writing functionality
- Executes commands in the order specified in configuration
Key functions:
run_cmd(command: &str, env_vars: HashMap<String, String>) -> Result<()>- Executes a commandwrite_file_atomic(path: &Path, content: &str, mode: u32) -> Result<()>- Writes file atomicallyapply(commands: &[RunCommand]) -> Result<()>- Main execution function
State Module
The state module tracks which configuration modules have been applied:
- Loads and saves state to
/var/lib/hero-init/state - Prevents re-application of configurations on subsequent boots
- Uses a YAML file with a set of completed module names
Key functions:
load_state() -> Result<HeroState>- Loads state from disksave_state(state: &HeroState) -> Result<()>- Saves state to diskis_module_complete(state: &HeroState, module: &str) -> bool- Checks if module is completemark_module_complete(state: &mut HeroState, module: &str)- Marks module as complete
Tool Functions
hero-init provides several utility functions across its modules:
- Device Discovery: Automatically finds and mounts the SEED device
- Configuration Parsing: Reads and validates YAML configuration
- Idempotent Operations: Ensures configurations are only applied once
- State Management: Tracks completed configuration modules
- Error Handling: Comprehensive error handling with detailed logging
- Atomic Operations: File operations are atomic to prevent corruption
How to Configure
- Create a SEED device with a filesystem labeled "SEED"
- Create a
hero-init.yamlfile on the SEED device with your configuration - Boot the system with the SEED device attached
- hero-init will automatically run and apply the configuration
Adding Configuration
To add configuration:
-
Mount the SEED device on a running system:
sudo mkdir -p /mnt/seed sudo mount /dev/disk/by-label/SEED /mnt/seed -
Edit or create
/mnt/seed/hero-init.yamlwith your desired configuration -
Unmount the device:
sudo umount /mnt/seed -
Boot the target system with the SEED device attached
Seed Partition
The SEED partition must:
- Be a filesystem on a block device
- Have the label "SEED"
- Contain a
hero-init.yamlfile with valid configuration - Be readable by the system
To create a SEED partition:
-
Create a filesystem with the SEED label:
dd if=/dev/zero of=seed.img bs=1M count=10 mkfs.vfat -n SEED seed.img -
Mount and populate with configuration:
sudo mount seed.img /mnt # Add hero-init.yaml sudo umount /mnt/seed
Example Configuration
See example.yaml for a complete configuration example:
instance_id: "hero-001"
hostname: "hero-vm"
network:
provider: netplan
interfaces:
- name: ens3
mac: "52:54:00:12:34:56"
dhcp4: false
addresses:
- 10.0.2.100/24
routes:
- to: default
via: 10.0.2.2
nameservers:
addresses:
- 10.0.2.3
- 8.8.8.8
search:
- hero.local
users:
- name: john
ssh_authorized_keys:
- "ssh-ed25519 AAAAC.... john@hero-test"
groups:
- sudo
sudo: true
runcmd:
- "echo 'Hello from hero-init' >> /tmp/test1.txt"
- cmd: "echo $MY_VAR > /tmp/test2.txt"
env:
MY_VAR: "190"