Add Ubuntu Server autoinstall for Iron Legion G9 — headless, zero-touch, replaces MAAS for small batches
This commit is contained in:
151
ubuntu-autoinstall/README.md
Normal file
151
ubuntu-autoinstall/README.md
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
# Ubuntu Server Autoinstall — Iron Legion G9
|
||||||
|
|
||||||
|
Headless, zero-touch Ubuntu 24.04 LTS deployment for GMKtec G9 mini-PCs.
|
||||||
|
Replaces MAAS for small batches. Fully autonomous after USB boot.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `autoinstall.yaml` | Main config — save as `user-data` on USB |
|
||||||
|
| `meta-data` | Empty companion file (required by nocloud) |
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Create the USB stick
|
||||||
|
|
||||||
|
1. Download **Ubuntu Server 24.04 LTS ISO** (not Desktop)
|
||||||
|
2. Flash to USB with **Rufus** (Windows) or `dd` (Linux)
|
||||||
|
- Partition scheme: GPT
|
||||||
|
- Target system: UEFI (non-CSM)
|
||||||
|
3. Mount the USB's writable partition
|
||||||
|
|
||||||
|
### 2. Add autoinstall files
|
||||||
|
|
||||||
|
Create these two files in the **root of the USB's writable partition**:
|
||||||
|
|
||||||
|
**`user-data`**
|
||||||
|
```
|
||||||
|
[paste entire contents of autoinstall.yaml here]
|
||||||
|
```
|
||||||
|
|
||||||
|
**`meta-data`** (can be empty)
|
||||||
|
```yaml
|
||||||
|
instance-id: iid-ironlegion01
|
||||||
|
local-hostname: ubuntu
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Configure GRUB for headless boot
|
||||||
|
|
||||||
|
Edit `boot/grub/grub.cfg` on the USB. Find the first menuentry and add kernel params:
|
||||||
|
|
||||||
|
```grub
|
||||||
|
set timeout=5
|
||||||
|
|
||||||
|
menuentry "Try or Install Ubuntu Server" {
|
||||||
|
linux /casper/vmlinuz autoinstall ds=nocloud\;s=/cdrom/ quiet ---
|
||||||
|
initrd /casper/initrd
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**`timeout=5`** means 5-second countdown then auto-boots. Fully headless.
|
||||||
|
|
||||||
|
### 4. Boot the G9
|
||||||
|
|
||||||
|
1. Plug USB into G9
|
||||||
|
2. Power on
|
||||||
|
3. Walk away — 5 minutes later it's done
|
||||||
|
4. SSH in as `jarvis` / `ubuntu`
|
||||||
|
|
||||||
|
## What Happens
|
||||||
|
|
||||||
|
| Stage | Action |
|
||||||
|
|-------|--------|
|
||||||
|
| Boot | Ubuntu Server installer loads from USB |
|
||||||
|
| Storage | GPT on `/dev/nvme0n1` — 1GB boot + rest root (ext4) |
|
||||||
|
| Identity | Creates `jarvis` user with fleet SSH key |
|
||||||
|
| Hostname | Sets from MAC → `mk-33`, `mk-34`, `mk-39`, `mk-42`, or `g9-<last4>` |
|
||||||
|
| Packages | Installs Docker, Tailscale, git, curl, ufw, nfs-common |
|
||||||
|
| Docker | Adds `jarvis` to `docker` group |
|
||||||
|
| Tailscale | Installed, ready for `tailscale up` |
|
||||||
|
| Ansible | Clones `ansible-pull-deploy` repo to `~/.ansible-repo` |
|
||||||
|
| Network | Wildcard DHCP on all `en*` interfaces |
|
||||||
|
| SSH | Open on port 22, password auth enabled (fleet standard) |
|
||||||
|
| Firewall | UFW enabled — allows SSH only |
|
||||||
|
| Final | Reboots into fresh system |
|
||||||
|
|
||||||
|
## Post-Install (one command per node)
|
||||||
|
|
||||||
|
After the node is up:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Join Tailscale (interactive — generates auth URL)
|
||||||
|
sudo tailscale up
|
||||||
|
|
||||||
|
# Or with pre-generated auth key:
|
||||||
|
sudo tailscale up --auth-key=tskey-auth-xxxxxxxx
|
||||||
|
|
||||||
|
# Run ansible-pull for remaining fleet config
|
||||||
|
cd ~/.ansible-repo && ansible-pull -U https://gitea.nb.bobbysh.me/Iron-Legion/ansible-pull-deploy.git -d .
|
||||||
|
```
|
||||||
|
|
||||||
|
## MAC-to-Hostname Mapping
|
||||||
|
|
||||||
|
| MAC | Hostname |
|
||||||
|
|-----|----------|
|
||||||
|
| `e0:51:d8:1c:5d:56` | `mk-33` |
|
||||||
|
| `e0:51:d8:1c:5c:75` | `mk-34` |
|
||||||
|
| `e0:51:d8:1c:5d:ca` | `mk-39` |
|
||||||
|
| `e0:51:d8:1c:5d:5c` | `mk-42` |
|
||||||
|
| other | `g9-<last4mac>` |
|
||||||
|
|
||||||
|
## Tailscale Auth Key
|
||||||
|
|
||||||
|
To auto-join without interaction, generate a reusable auth key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tailscale login # on Artemis or any node
|
||||||
|
tailscale web
|
||||||
|
tailscale authkeys create --reusable --preauthorized --tags=tag:g9
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the key to `late-commands` in `autoinstall.yaml`:
|
||||||
|
```yaml
|
||||||
|
- sudo tailscale up --auth-key=tskey-auth-xxxxxxxx
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Symptom | Fix |
|
||||||
|
|---------|-----|
|
||||||
|
| Stops at boot menu | Check GRUB `timeout` is set, not `#` commented |
|
||||||
|
| Can't find `user-data` | Verify files are on writable partition root, not inside a folder |
|
||||||
|
| Wrong disk | Confirm G9 has 1TB NVMe in first slot (`/dev/nvme0n1`) |
|
||||||
|
| Hostname is `ubuntu` | Check MAC is correct; check `/etc/cloud/cloud-init.disabled` exists |
|
||||||
|
| No SSH | Verify `openssh-server` installed; check `ufw status` |
|
||||||
|
|
||||||
|
## Differences from MAAS
|
||||||
|
|
||||||
|
| | MAAS | Autoinstall |
|
||||||
|
|--|------|-------------|
|
||||||
|
| PXE server | Required (Shield) | Not needed |
|
||||||
|
| Subnet | Isolated `/27` required | Any network with DHCP |
|
||||||
|
| Internet during install | Not available | Available via your LAN |
|
||||||
|
| Time per node | 15-30 min | ~5 min |
|
||||||
|
| Touch required | Power on + rename in UI | Zero (headless) |
|
||||||
|
| Scale | Excellent for 10+ nodes | Ideal for 1-10 nodes |
|
||||||
|
| Phase 2 bootstrap | Required manually | Included in late-commands |
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Password is fleet standard: `ubuntu`
|
||||||
|
- SSH key is the same fleet-wide key used on all nodes
|
||||||
|
- Cloud-init is disabled post-install to prevent hostname stomping
|
||||||
|
- If you get more G9s, just update the MAC→hostname mapping and re-flash
|
||||||
|
|
||||||
|
## Version
|
||||||
|
|
||||||
|
- Ubuntu 24.04 LTS (Noble Numbat)
|
||||||
|
- Docker CE latest
|
||||||
|
- Tailscale stable
|
||||||
|
- Ansible 2.x (pulled from repo)
|
||||||
135
ubuntu-autoinstall/autoinstall.yaml
Normal file
135
ubuntu-autoinstall/autoinstall.yaml
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# Ubuntu Server Autoinstall — Iron Legion G9 Headless Deploy
|
||||||
|
# Usage: Place as 'user-data' on nocloud USB alongside empty 'meta-data' file
|
||||||
|
# See README.md for USB creation instructions
|
||||||
|
|
||||||
|
version: 1
|
||||||
|
autoinstall:
|
||||||
|
# ── Identity ──
|
||||||
|
identity:
|
||||||
|
hostname: ubuntu
|
||||||
|
username: jarvis
|
||||||
|
password: "$6$iypA63f5vLDzTGQ2$eOrvsyhnM6c4istoy65GUConWL4St.rzy28wFt8QxUWk7F3fSx7mHytwnjHosvIj7JAMBPeC4jkUctAZJeKDx/"
|
||||||
|
ssh:
|
||||||
|
install-server: true
|
||||||
|
allow-pw: true
|
||||||
|
authorized-keys:
|
||||||
|
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPSBrRCROUHOiZX9IB3teEK89VFfghbdu7OF5NoJ1Y6g
|
||||||
|
|
||||||
|
# ── Storage ──
|
||||||
|
storage:
|
||||||
|
config:
|
||||||
|
- type: disk
|
||||||
|
id: disk0
|
||||||
|
ptable: gpt
|
||||||
|
wipe: superblock-recursive
|
||||||
|
path: /dev/nvme0n1
|
||||||
|
- type: partition
|
||||||
|
id: boot-part
|
||||||
|
number: 1
|
||||||
|
size: 1GB
|
||||||
|
device: disk0
|
||||||
|
flag: boot
|
||||||
|
grub_device: true
|
||||||
|
- type: format
|
||||||
|
id: boot-format
|
||||||
|
fstype: ext4
|
||||||
|
volume: boot-part
|
||||||
|
- type: mount
|
||||||
|
id: boot-mount
|
||||||
|
device: boot-format
|
||||||
|
path: /boot
|
||||||
|
- type: partition
|
||||||
|
id: root-part
|
||||||
|
number: 2
|
||||||
|
size: -1
|
||||||
|
device: disk0
|
||||||
|
- type: format
|
||||||
|
id: root-format
|
||||||
|
fstype: ext4
|
||||||
|
volume: root-part
|
||||||
|
- type: mount
|
||||||
|
id: root-mount
|
||||||
|
device: root-format
|
||||||
|
path: /
|
||||||
|
|
||||||
|
# ── Networking ──
|
||||||
|
network:
|
||||||
|
version: 2
|
||||||
|
ethernets:
|
||||||
|
en0:
|
||||||
|
match:
|
||||||
|
name: "en*"
|
||||||
|
dhcp4: true
|
||||||
|
dhcp6: true
|
||||||
|
|
||||||
|
# ── Packages ──
|
||||||
|
packages:
|
||||||
|
- openssh-server
|
||||||
|
- curl
|
||||||
|
- git
|
||||||
|
- ca-certificates
|
||||||
|
- gnupg
|
||||||
|
- net-tools
|
||||||
|
- iputils-ping
|
||||||
|
- htop
|
||||||
|
- ufw
|
||||||
|
- nfs-common
|
||||||
|
|
||||||
|
# ── First boot scripts ──
|
||||||
|
late-commands:
|
||||||
|
# Hostname from MAC address
|
||||||
|
- |
|
||||||
|
MAC=$(cat /sys/class/net/en*/address 2>/dev/null | head -1 | tr -d ':')
|
||||||
|
case "$MAC" in
|
||||||
|
e051d81c5d56) HOST="mk-33" ;;
|
||||||
|
e051d81c5c75) HOST="mk-34" ;;
|
||||||
|
e051d81c5dca) HOST="mk-39" ;;
|
||||||
|
e051d81c5d5c) HOST="mk-42" ;;
|
||||||
|
*) HOST="g9-$(echo $MAC | tail -c 5)" ;;
|
||||||
|
esac
|
||||||
|
hostnamectl set-hostname "$HOST"
|
||||||
|
echo "$HOST" > /etc/hostname
|
||||||
|
printf "127.0.1.1\t%s\n" "$HOST" >> /etc/hosts
|
||||||
|
|
||||||
|
# Disable cloud-init re-run
|
||||||
|
- |
|
||||||
|
mkdir -p /etc/cloud/cloud.cfg.d
|
||||||
|
echo "preserve_hostname: true" > /etc/cloud/cloud.cfg.d/99_preserve_hostname.cfg
|
||||||
|
touch /etc/cloud/cloud-init.disabled
|
||||||
|
|
||||||
|
# Install Docker
|
||||||
|
- |
|
||||||
|
install -m 0755 -d /etc/apt/keyrings
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||||
|
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||||
|
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu noble stable" > /etc/apt/sources.list.d/docker.list
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||||
|
usermod -aG docker jarvis
|
||||||
|
|
||||||
|
# Install Tailscale
|
||||||
|
- |
|
||||||
|
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
|
||||||
|
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list | tee /etc/apt/sources.list.d/tailscale.list
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y tailscale
|
||||||
|
|
||||||
|
# Clone ansible-pull repo
|
||||||
|
- |
|
||||||
|
sudo -u jarvis bash -c 'mkdir -p ~/.ansible-repo && cd ~/.ansible-repo && git clone https://gitea.nb.bobbysh.me/Iron-Legion/ansible-pull-deploy.git . 2>/dev/null || true'
|
||||||
|
|
||||||
|
# SSH hardening (keep PW auth enabled for fleet standard)
|
||||||
|
- |
|
||||||
|
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
|
||||||
|
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
|
||||||
|
systemctl restart ssh
|
||||||
|
|
||||||
|
# Enable UFW basic rules
|
||||||
|
- |
|
||||||
|
ufw default deny incoming
|
||||||
|
ufw default allow outgoing
|
||||||
|
ufw allow 22/tcp
|
||||||
|
ufw --force enable
|
||||||
|
|
||||||
|
# Reboot to finalize
|
||||||
|
- reboot
|
||||||
Reference in New Issue
Block a user