Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
de9d633
Add systemd template renderer.
Gerrit91 Mar 6, 2026
f083402
Firewall Controller
majst01 Mar 6, 2026
0bf87eb
nftables-exporter
majst01 Mar 7, 2026
b4fea5f
next service
majst01 Mar 7, 2026
b1c22f4
Remove unused
majst01 Mar 7, 2026
9d1d001
Progress.
Gerrit91 Mar 9, 2026
3cb2e1c
Suricata.
Gerrit91 Mar 9, 2026
55176a6
Chrony.
Gerrit91 Mar 9, 2026
035b246
Progress.
Gerrit91 Mar 9, 2026
deb53cd
Interfaces.
Gerrit91 Mar 9, 2026
9cfeaf4
Less.
Gerrit91 Mar 9, 2026
614fb5e
Uffräume und tschüss.
Gerrit91 Mar 9, 2026
835c1db
Smallish
majst01 Mar 9, 2026
d40150a
Smallish
majst01 Mar 9, 2026
97eaf7b
nftables
majst01 Mar 9, 2026
eecabd3
first nftables test
majst01 Mar 10, 2026
6f22f30
next nftables test
majst01 Mar 10, 2026
de380fd
vpn nftables test
majst01 Mar 10, 2026
67c6dc4
shared nftables test, not working yet
majst01 Mar 10, 2026
b8abd29
Shared nftables test
majst01 Mar 10, 2026
0435dc5
IPv6 nftables test
majst01 Mar 10, 2026
79bbe9c
Conversion part 1 of frr
majst01 Mar 10, 2026
2c13f41
Add frr test, does not compile yet
majst01 Mar 10, 2026
d3e4159
Network names
majst01 Mar 11, 2026
66fd5b5
Remove unused
majst01 Mar 11, 2026
0ec4c31
Remove exec
majst01 Mar 11, 2026
a8ebb11
Fix test.
Gerrit91 Mar 11, 2026
b74c696
fix test
majst01 Mar 11, 2026
157460e
unexport
majst01 Mar 11, 2026
28c07ee
use new forwardpolicy
majst01 Mar 11, 2026
dd9e843
Fix.
Gerrit91 Mar 11, 2026
86cedee
Next.
Gerrit91 Mar 11, 2026
def5364
fixes
majst01 Mar 11, 2026
9e4608c
Pusn network.
Gerrit91 Mar 11, 2026
7f3dfe7
Install systemd services
majst01 Mar 11, 2026
264694f
all frr tests pass
majst01 Mar 11, 2026
e9a1fdb
Remove old, reactivate validation
majst01 Mar 11, 2026
492bfd6
package removed
majst01 Mar 11, 2026
99ffb45
go mod tidy
majst01 Mar 11, 2026
d34df4e
Add frr version detection.
Gerrit91 Mar 11, 2026
7ecc53f
Change to API v2.
Gerrit91 Mar 11, 2026
1422fd8
Emojis.
Gerrit91 Mar 11, 2026
51ae978
Remove unused
majst01 Mar 11, 2026
f36ed89
Refactor installer.
Gerrit91 Mar 11, 2026
b9595b8
Validate nftables
majst01 Mar 12, 2026
33a7416
Fix tests, go mod tidy
majst01 Mar 12, 2026
af82b36
Satisfy linter
majst01 Mar 12, 2026
cfd84e8
More linter fixes
majst01 Mar 12, 2026
2596486
Unexport
majst01 Mar 12, 2026
5e8ebf2
Allow hooking into installer through config.
Gerrit91 Mar 12, 2026
ac88341
Merge branch 'systemd-file-units' of https://github.com/metal-stack/o…
majst01 Mar 12, 2026
a55ed1a
simpler
majst01 Mar 12, 2026
8f4114a
Unexport
majst01 Mar 12, 2026
9c584cc
Unexport
majst01 Mar 12, 2026
52b1bf7
Fixes.
Gerrit91 Mar 12, 2026
72cb44d
Add main.
Gerrit91 Mar 12, 2026
b750188
Protoyaml
majst01 Mar 12, 2026
39ef0df
Update README.
Gerrit91 Mar 12, 2026
b6677ac
Adding CODEOWNERS.
Gerrit91 Mar 12, 2026
4eb964b
Remove legacy disk, move buildmeta
majst01 Mar 12, 2026
b4e91c2
Path of configs in one place
majst01 Mar 13, 2026
f6d4c1c
Add Parse/Read Configuration
majst01 Mar 14, 2026
aa3e90a
More logs
majst01 Mar 16, 2026
edec933
More logs
majst01 Mar 16, 2026
0d0d563
Pass exec.
Gerrit91 Mar 16, 2026
23c4e38
Update api
majst01 Mar 16, 2026
30a28bb
Verbose cmd output
majst01 Mar 16, 2026
d19d2fa
Detect frr client version instead of server version
majst01 Mar 17, 2026
ad59a08
Provide more tests.
Gerrit91 Mar 17, 2026
0434a69
Fix text.
Gerrit91 Mar 17, 2026
8063af8
More debug log
majst01 Mar 17, 2026
d34b9a3
More edgecase covered
majst01 Mar 17, 2026
31287d7
Fix nft validation
majst01 Mar 17, 2026
37d7cba
Debug systemd reload
majst01 Mar 17, 2026
a663aef
Do not enable services on install
majst01 Mar 17, 2026
cf10a95
Enable systemd services without dbus
majst01 Mar 17, 2026
346ba1c
Disable chrony for now
majst01 Mar 17, 2026
bf724ba
Enable services if set
majst01 Mar 17, 2026
b6b1c58
Use common enable
majst01 Mar 17, 2026
24d0e07
wrap in bash
majst01 Mar 17, 2026
0d12444
Enable chrony again
majst01 Mar 17, 2026
1304523
Disable chrony again
majst01 Mar 17, 2026
b67d8f1
Do not error out on systemctl enable
majst01 Mar 17, 2026
bd9e7f6
Remove last networker artifact
majst01 Mar 17, 2026
6c055d5
Better Tests
majst01 Mar 18, 2026
c16d51a
Legacy InstallConfig
majst01 Mar 18, 2026
525a632
Add missing process userdata task.
Gerrit91 Mar 18, 2026
0f0e215
Execute ignition with directory
majst01 Mar 18, 2026
386c29e
Next try
majst01 Mar 18, 2026
1d52e12
fix suricata external interface
majst01 Mar 18, 2026
09ea6ce
remove solved fixmes
majst01 Mar 18, 2026
2f5a75d
Debug instead of info
majst01 Mar 19, 2026
111abf4
Pin
majst01 Mar 20, 2026
37abcaa
prevent potential nilpointer
majst01 Mar 21, 2026
b78afa0
bring back routemap_test, but skip for now
majst01 Mar 21, 2026
4418aef
Make persisting installer config a task.
Gerrit91 Mar 23, 2026
b3d309a
Network tests
majst01 Mar 23, 2026
0f62218
Remove duplicate chrony template rendering
majst01 Mar 23, 2026
ca10d04
Provide tests for ntp conf.
Gerrit91 Mar 23, 2026
59876ff
Slightly better go
majst01 Mar 24, 2026
9a05368
add first passing testcase for importRulesForNetwork
iljarotar Mar 25, 2026
e7b267c
Add comment.
Gerrit91 Apr 1, 2026
7f8b495
Remove fixme
majst01 Apr 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
bin/*
bin/*
.vscode
coverage.out
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @metal-stack/os-installer-maintainers
File renamed without changes.
4 changes: 1 addition & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@ binary:

.PHONY: test
test:
GO_ENV=testing go test -race -cover ./...
GO_ENV=testing go test ./... -race -coverpkg=./... -coverprofile=coverage.out -covermode=atomic && go tool cover -func=coverage.out


.PHONY: validate
validate:
cd pkg/network
./validate.sh
cd -
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# OS Installer
# os-installer

OS installer is used to configure the already untarred metal-image on the machine based on the configuration which must be located at `/etc/metal/install.yaml`.
The OS installer is used to configure a machine according to the given allocation specification, like configuring:

- Network interfaces
- FRR configuration
- Metal user and authorized keys
- Bootloader configuration
- Ignition and cloud-init userdata
- ...

It currently supports the officially published operating system images from the [metal-images repository](https://github.com/metal-stack/metal-images).

The installer is executed by the [metal-hammer](https://github.com/metal-stack/metal-hammer) in a chroot, pointing to the root of the uncompressed operating system image.

The input configuration for the installer are:

- The `MachineDetails` as defined in [api/v1/api.go](./api/v1/api.go)
- The `MachineAllocation` as defined in the [API repository](https://github.com/metal-stack/api/)
- An optional installer `Config` as defined in [api/v1/api.go](./api/v1/api.go) (for building own images)
116 changes: 61 additions & 55 deletions api/v1/api.go
Original file line number Diff line number Diff line change
@@ -1,64 +1,70 @@
package v1

import "github.com/metal-stack/metal-go/api/models"
import (
apiv2 "github.com/metal-stack/api/go/metalstack/api/v2"
)

// Bootinfo is written by the installer in the target os to tell us
// which kernel, initrd and cmdline must be used for kexec
type Bootinfo struct {
Initrd string `yaml:"initrd"`
Cmdline string `yaml:"cmdline"`
Kernel string `yaml:"kernel"`
BootloaderID string `yaml:"bootloader_id"`
}
const (
MachineDetailsPath = "/etc/metal/machine-details.yaml"
MachineAllocationPath = "/etc/metal/machine-allocation.yaml"
InstallerConfigPath = "/etc/metal/os-installer.yaml"
BuildMetaPath = "/etc/metal/build-meta.yaml"
BootInfoPath = "/etc/metal/boot-info.yaml"
)

// InstallerConfig contains configuration items which are
// used to install the os.
type InstallerConfig struct {
// Hostname of the machine
Hostname string `yaml:"hostname"`
// Networks all networks connected to this machine
Networks []*models.V1MachineNetwork `yaml:"networks"`
// MachineUUID is the unique UUID for this machine, usually the board serial.
MachineUUID string `yaml:"machineuuid"`
// SSHPublicKey of the user
SSHPublicKey string `yaml:"sshpublickey"`
// Password is the password for the metal user.
Password string `yaml:"password"`
// Console specifies where the kernel should connect its console to.
Console string `yaml:"console"`
// Timestamp is the the timestamp of installer config creation.
Timestamp string `yaml:"timestamp"`
// Nics are the network interfaces of this machine including their neighbors.
Nics []*models.V1MachineNic `yaml:"nics"`
// VPN is the config for connecting machine to VPN
VPN *models.V1MachineVPN `yaml:"vpn"`
// Role is either firewall or machine
Role string `yaml:"role"`
// RaidEnabled is set to true if any raid devices are specified
RaidEnabled bool `yaml:"raidenabled"`
// RootUUID is the fs uuid if the root fs
RootUUID string `yaml:"root_uuid"`
// FirewallRules if not empty firewall rules to enforce
FirewallRules *models.V1FirewallRules `yaml:"firewall_rules"`
// DNSServers for the machine
DNSServers []*models.V1DNSServer `yaml:"dns_servers"`
// NTPServers for the machine
NTPServers []*models.V1NTPServer `yaml:"ntp_servers"`
}
type (
// Bootinfo is written by the installer in the target os to tell us
// which kernel, initrd and cmdline must be used for kexec
Bootinfo struct {
Initrd string `yaml:"initrd"`
Cmdline string `yaml:"cmdline"`
Kernel string `yaml:"kernel"`
BootloaderID string `yaml:"bootloader_id"`
}

// FIXME legacy structs remove once old images are gone
// Config can be placed inside the target OS to customize the os-installer.
Config struct {
// OsName enforces a specific os-installer implementation, defaults to auto-detection
OsName *string `yaml:"os_name"`
// Only allows to run installer tasks only with the given names
Only []string `yaml:"only"`
// Except allows to run installer tasks except for the given names
Except []string `yaml:"except"`
// CustomScript allows executing a custom script that's placed inside the OS at the end of the installer execution
CustomScript *struct {
ExecutablePath string `yaml:"executable_path"`
WorkDir string `yaml:"workdir"`
} `yaml:"custom_script"`
// Overwrites allows specifying os-installer overwrites for the default implementation
Overwrites struct {
BootloaderID *string `yaml:"bootloader_id"`
}
}

type (
// Disk is a physical Disk
Disk struct {
// Device the name of the disk device visible from kernel side, e.g. sda
Device string
// Partitions to create on this disk, order is preserved
Partitions []Partition
// MachineDetails which are not part of the MachineAllocation but required to complete the installation.
// Is written by by the metal-hammer
MachineDetails struct {
// Id is the machine UUID
ID string `yaml:"id"`
// Nics are the nics of the machine
Nics []*apiv2.MachineNic `yaml:"nics"`
// Password is the password for the metal user.
Password string `yaml:"password"`
// Console specifies where the kernel should connect its console to.
Console string `yaml:"console"`
// RaidEnabled is set to true if any raid devices are specified
RaidEnabled bool `yaml:"raidenabled"`
// RootUUID is the fs uuid if the root fs
RootUUID string `yaml:"root_uuid"`
}
Partition struct {
Label string
Filesystem string
Properties map[string]string

// BuildMeta is written after the installation finished to store details about the installation version.
BuildMeta struct {
Version string `json:"buildVersion" yaml:"buildVersion"`
Date string `json:"buildDate" yaml:"buildDate"`
SHA string `json:"buildSHA" yaml:"buildSHA"`
Revision string `json:"buildRevision" yaml:"buildRevision"`

IgnitionVersion string `json:"ignitionVersion" yaml:"ignitionVersion"`
}
)
10 changes: 0 additions & 10 deletions api/v1/build-meta.go

This file was deleted.

166 changes: 166 additions & 0 deletions api/v1/legacy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package v1

const (
LegacyInstallPath = "/etc/metal/install.yaml"
)

type (

// InstallerConfig contains legacy configuration items which are
// used to install the os.
// It must be serialized to /etc/metal/install.yaml to guarantee compatibility for older
// firewall-controller and lldpd
InstallerConfig struct {
// Hostname of the machine
Hostname string `yaml:"hostname"`
// Networks all networks connected to this machine
Networks []*V1MachineNetwork `yaml:"networks"`
// MachineUUID is the unique UUID for this machine, usually the board serial.
MachineUUID string `yaml:"machineuuid"`
// SSHPublicKey of the user
SSHPublicKey string `yaml:"sshpublickey"`
// Password is the password for the metal user.
Password string `yaml:"password"`
// Console specifies where the kernel should connect its console to.
Console string `yaml:"console"`
// Timestamp is the the timestamp of installer config creation.
Timestamp string `yaml:"timestamp"`
// Nics are the network interfaces of this machine including their neighbors.
Nics []*V1MachineNic `yaml:"nics"`
// VPN is the config for connecting machine to VPN
VPN *V1MachineVPN `yaml:"vpn"`
// Role is either firewall or machine
Role string `yaml:"role"`
// RaidEnabled is set to true if any raid devices are specified
RaidEnabled bool `yaml:"raidenabled"`
// RootUUID is the fs uuid if the root fs
RootUUID string `yaml:"root_uuid"`
// FirewallRules if not empty firewall rules to enforce
FirewallRules *V1FirewallRules `yaml:"firewall_rules"`
// DNSServers for the machine
DNSServers []*V1DNSServer `yaml:"dns_servers"`
// NTPServers for the machine
NTPServers []*V1NTPServer `yaml:"ntp_servers"`
}

// Copies of metal-go models.V1* structs in use in Installerconfig
// to prevent the import of metal-go.

V1MachineNetwork struct {
// ASN number for this network in the bgp configuration
// Required: true
Asn *int64 `json:"asn" yaml:"asn"`
// the destination prefixes of this network
// Required: true
Destinationprefixes []string `json:"destinationprefixes" yaml:"destinationprefixes"`
// the ip addresses of the allocated machine in this vrf
// Required: true
Ips []string `json:"ips" yaml:"ips"`
// if set to true, packets leaving this network get masqueraded behind interface ip
// Required: true
Nat *bool `json:"nat" yaml:"nat"`
// nattypev2
// Required: true
Nattypev2 *string `json:"nattypev2" yaml:"nattypev2"`
// the networkID of the allocated machine in this vrf
// Required: true
Networkid *string `json:"networkid" yaml:"networkid"`
// the network type, types can be looked up in the network package of metal-lib
// Required: true
Networktype *string `json:"networktype" yaml:"networktype"`
// networktypev2
// Required: true
Networktypev2 *string `json:"networktypev2" yaml:"networktypev2"`
// the prefixes of this network
// Required: true
Prefixes []string `json:"prefixes" yaml:"prefixes"`
// indicates whether this network is the private network of this machine
// Required: true
Private *bool `json:"private" yaml:"private"`
// project of this network, empty string if not project scoped
// Required: true
Projectid *string `json:"projectid" yaml:"projectid"`
// if set to true, this network can be used for underlay communication
// Required: true
Underlay *bool `json:"underlay" yaml:"underlay"`
// the vrf of the allocated machine
// Required: true
Vrf *int64 `json:"vrf" yaml:"vrf"`
}

V1MachineNic struct {
// the unique identifier of this network interface
// Required: true
Identifier *string `json:"identifier" yaml:"identifier"`
// the mac address of this network interface
// Required: true
Mac *string `json:"mac" yaml:"mac"`
// the name of this network interface
// Required: true
Name *string `json:"name" yaml:"name"`
// the neighbors visible to this network interface
// Required: true
Neighbors []*V1MachineNic `json:"neighbors" yaml:"neighbors"`
}

V1MachineVPN struct {
// address of VPN control plane
// Required: true
Address *string `json:"address" yaml:"address"`
// auth key used to connect to VPN
// Required: true
AuthKey *string `json:"auth_key" yaml:"auth_key"`
// connected to the VPN
// Required: true
Connected *bool `json:"connected" yaml:"connected"`
}

V1FirewallRules struct {
// list of egress rules to be deployed during firewall allocation
Egress []*V1FirewallEgressRule `json:"egress" yaml:"egress"`
// list of ingress rules to be deployed during firewall allocation
Ingress []*V1FirewallIngressRule `json:"ingress" yaml:"ingress"`
}

V1FirewallEgressRule struct {
// an optional comment describing what this rule is used for
Comment string `json:"comment,omitempty" yaml:"comment,omitempty"`
// the ports affected by this rule
// Required: true
Ports []int32 `json:"ports" yaml:"ports"`
// the protocol for the rule, defaults to tcp
// Enum: ["tcp","udp"]
Protocol string `json:"protocol,omitempty" yaml:"protocol,omitempty"`
// the cidrs affected by this rule
// Required: true
To []string `json:"to" yaml:"to"`
}

V1FirewallIngressRule struct {
// an optional comment describing what this rule is used for
Comment string `json:"comment,omitempty" yaml:"comment,omitempty"`
// the cidrs affected by this rule
// Required: true
From []string `json:"from" yaml:"from"`
// the ports affected by this rule
// Required: true
Ports []int32 `json:"ports" yaml:"ports"`
// the protocol for the rule, defaults to tcp
// Enum: ["tcp","udp"]
Protocol string `json:"protocol,omitempty" yaml:"protocol,omitempty"`
// the cidrs affected by this rule
To []string `json:"to" yaml:"to"`
}

V1DNSServer struct {
// ip address of this dns server
// Required: true
IP *string `json:"ip" yaml:"ip"`
}

V1NTPServer struct {
// ip address or dns hostname of this ntp server
// Required: true
Address *string `json:"address" yaml:"address"`
}
)
Loading
Loading