Compare commits
11 Commits
95a7058eb2
...
feature/us
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a6a0e39c3 | ||
|
|
d60bc96f1d | ||
|
|
f3e7c5d108 | ||
|
|
2370049e48 | ||
|
|
091f11f036 | ||
|
|
74c26b370b | ||
|
|
b09139bbe7 | ||
|
|
49b0ddcc7f | ||
|
|
39ff842a65 | ||
|
|
89e518612a | ||
|
|
f857f15d50 |
21
archive/maas/README.md
Normal file
21
archive/maas/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# MAAS Archive
|
||||
|
||||
**Status: ABANDONED — May 23, 2026**
|
||||
|
||||
These files are the final state of the MAAS PXE deployment effort for Iron Legion fleet nodes. MAAS was removed from Shield after repeated failures on isolated subnets, IP exhaustion, and general complexity. Replaced by USB autoinstall approach.
|
||||
|
||||
## Files
|
||||
|
||||
- `curtin_userdata_fleet_v5.yaml` — Fleet preseed targeting `/dev/nvme0n1`, no apt-get (isolated subnet safe), SSH enable only. Final working version before abandonment.
|
||||
|
||||
## Lessons Captured
|
||||
|
||||
1. MAAS ephemeral commissioning fails on subnets without internet (apt-get hangs)
|
||||
2. IP exhaustion on /27 pools after failed deployments (sticky reservations)
|
||||
3. G9 Intel I226-V NIC (enp5s0) drops link during Linux driver init
|
||||
4. Linux enumerates first NVMe as `nvme0n1`, not `nvme1`
|
||||
5. Cloud-init stomps hostname unless `preserve_hostname: true` is set
|
||||
|
||||
## Replacement
|
||||
|
||||
See `autoinstall/` in repo root for the USB-based replacement workflow.
|
||||
49
archive/maas/curtin_userdata_fleet_v5.yaml
Normal file
49
archive/maas/curtin_userdata_fleet_v5.yaml
Normal file
@@ -0,0 +1,49 @@
|
||||
#cloud-config
|
||||
# MAAS Fleet Preseed v5 — Iron Legion DR Standard
|
||||
# No apt-get during late_commands (isolated subnet)
|
||||
# Targets /dev/nvme0n1 (first NVMe, skip eMMC)
|
||||
# Defer internet tasks to post-deploy on fleet LAN
|
||||
# Corrected 2026-05-23: nvme1 → nvme0n1 (Linux enumeration)
|
||||
|
||||
debconf_selections:
|
||||
maas: |
|
||||
{{for line in str(curtin_preseed).splitlines()}}
|
||||
{{line}}
|
||||
{{endfor}}
|
||||
|
||||
storage:
|
||||
swap:
|
||||
size: 0
|
||||
config:
|
||||
- type: disk
|
||||
id: root-disk
|
||||
path: /dev/nvme0n1
|
||||
ptable: gpt
|
||||
wipe: superblock-recursive
|
||||
- type: partition
|
||||
id: root-partition
|
||||
device: root-disk
|
||||
size: -1
|
||||
flag: boot
|
||||
- type: format
|
||||
id: root-format
|
||||
volume: root-partition
|
||||
fstype: ext4
|
||||
- type: mount
|
||||
id: root-mount
|
||||
device: root-format
|
||||
path: /
|
||||
|
||||
late_commands:
|
||||
fleet_01_create_user: ["curtin", "in-target", "--", "sh", "-c", "useradd -m -s /bin/bash -G sudo jarvis && echo 'jarvis:ubuntu' | chpasswd"]
|
||||
fleet_02_hostname: ["curtin", "in-target", "--", "sh", "-c", "hostnamectl set-hostname $(echo {{node.hostname}} | sed 's/[^a-zA-Z0-9-]//g') && echo $(echo {{node.hostname}} | sed 's/[^a-zA-Z0-9-]//g') > /etc/hostname"]
|
||||
fleet_02b_preserve_hostname: ["curtin", "in-target", "--", "sh", "-c", "echo 'preserve_hostname: true' > /etc/cloud/cloud.cfg.d/99_preserve_hostname.cfg"]
|
||||
fleet_03_enable_ssh: ["curtin", "in-target", "--", "systemctl", "enable", "ssh"]
|
||||
fleet_04_start_ssh: ["curtin", "in-target", "--", "systemctl", "start", "ssh"]
|
||||
fleet_05_ssh_dir: ["curtin", "in-target", "--", "sh", "-c", "mkdir -p /home/jarvis/.ssh && chmod 700 /home/jarvis/.ssh"]
|
||||
fleet_06_auth_keys: ["curtin", "in-target", "--", "sh", "-c", "echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPSBrRCROUHOiZX9IB3teEK89VFfghbdu7OF5NoJ1Y6g Generated By Termius' > /home/jarvis/.ssh/authorized_keys"]
|
||||
fleet_07_chmod: ["curtin", "in-target", "--", "chmod", "600", "/home/jarvis/.ssh/authorized_keys"]
|
||||
fleet_08_chown: ["curtin", "in-target", "--", "chown", "-R", "jarvis:jarvis", "/home/jarvis/.ssh"]
|
||||
fleet_09_sudo: ["curtin", "in-target", "--", "sh", "-c", "echo 'jarvis ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/jarvis && chmod 440 /etc/sudoers.d/jarvis"]
|
||||
fleet_10_ansible_dirs: ["curtin", "in-target", "--", "sh", "-c", "mkdir -p /var/lib/ansible/local"]
|
||||
maas: [wget, '--no-proxy', {{node_disable_pxe_url|escape.json}}, '--post-data', {{node_disable_pxe_data|escape.json}}, '-O', '/dev/null']
|
||||
@@ -33,6 +33,7 @@
|
||||
- vim
|
||||
- python3-pip
|
||||
- cifs-utils
|
||||
- nfs-common
|
||||
state: present
|
||||
when: ansible_os_family == "Debian"
|
||||
tags: [baseline]
|
||||
|
||||
35
new-build/portainer/docker-compose.yml
Normal file
35
new-build/portainer/docker-compose.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
portainer:
|
||||
image: portainer/portainer-ce:latest
|
||||
container_name: portainer
|
||||
restart: always
|
||||
ports:
|
||||
- "8000:8000" # Edge agent port
|
||||
- "9000:9000" # HTTP web UI
|
||||
- "9443:9443" # HTTPS web UI (optional)
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- portainer_data:/data
|
||||
environment:
|
||||
- TZ=America/New_York
|
||||
networks:
|
||||
- portainer_net
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.portainer.rule=Host(`portainer.nb.bobbysh.me`)"
|
||||
- "traefik.http.routers.portainer.entrypoints=websecure"
|
||||
- "traefik.http.routers.portainer.tls=true"
|
||||
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
|
||||
# Optional: disable Traefik if not using it yet
|
||||
# - "traefik.enable=false"
|
||||
|
||||
volumes:
|
||||
portainer_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
portainer_net:
|
||||
driver: bridge
|
||||
attachable: true
|
||||
156
ubuntu-autoinstall/README.md
Normal file
156
ubuntu-autoinstall/README.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# 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`** — copy `autoinstall.yaml` and rename it to `user-data` (no extension):
|
||||
```bash
|
||||
cp autoinstall.yaml /media/usb/user-data
|
||||
```
|
||||
|
||||
**`meta-data`** — create an empty file literally named `meta-data` (no extension):
|
||||
```bash
|
||||
touch /media/usb/meta-data
|
||||
```
|
||||
|
||||
Optional content for `meta-data`:
|
||||
```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