Compare commits
7 Commits
main
...
091f11f036
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
091f11f036 | ||
|
|
74c26b370b | ||
|
|
b09139bbe7 | ||
|
|
49b0ddcc7f | ||
|
|
39ff842a65 | ||
|
|
89e518612a | ||
|
|
f857f15d50 |
26
README.md
26
README.md
@@ -14,24 +14,14 @@ Each node runs `ansible-pull` every 5 minutes via cron. It clones this repo and
|
|||||||
├── group_vars/
|
├── group_vars/
|
||||||
│ └── all.yml # Fleet-wide variables
|
│ └── all.yml # Fleet-wide variables
|
||||||
├── host_vars/
|
├── host_vars/
|
||||||
│ ├── artemis.yml # Artemis (AI Foreman)
|
│ ├── artemis.yml # Artemis (AI Foreman) specific
|
||||||
│ ├── cinnamint--elitebook.yml # Cinnamint-EliteBook (WSL2 workstation)
|
│ ├── mark44.yml # Mark44 (Hulkbuster) specific
|
||||||
│ ├── hulkbuster.yml # Mark44 (GPU heavy)
|
│ ├── mark5.yml # Mark5 (Suitcase) specific
|
||||||
│ ├── mark5.yml # Mark5 (GPU light / Suitcase)
|
│ └── bones.yml # Bones (Mark XLI) specific
|
||||||
│ ├── mark-vii.yml # Mark VII (Swarm manager + services)
|
└── roles/
|
||||||
│ ├── mission-control.yml # Mission-Control (WSL2 workstation)
|
└── common/
|
||||||
│ ├── mk-33.yml # MK-33 Silver Centurion (Swarm worker)
|
└── tasks/
|
||||||
│ ├── mk-34.yml # MK-34 (Swarm worker)
|
└── main.yml
|
||||||
│ ├── mk-39.yml # MK-39 (Swarm worker)
|
|
||||||
│ ├── mk-42.yml # MK-42 Extremis (Swarm worker)
|
|
||||||
│ └── nebuchadnezzar.yml # Neo (Nextcloud + Vaultwarden)
|
|
||||||
├── new-build/
|
|
||||||
│ └── portainer/
|
|
||||||
│ └── docker-compose.yml # Portainer CE stack for Swarm manager
|
|
||||||
├── ubuntu-autoinstall/
|
|
||||||
│ └── autoinstall.yaml # Fleet-standard headless autoinstall
|
|
||||||
└── archive/
|
|
||||||
└── maas/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Adding Node-Specific Tasks
|
## Adding Node-Specific Tasks
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Ansible Pull — Iron Legion fleet baseline
|
|
||||||
After=network-online.target
|
|
||||||
Wants=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
WorkingDirectory=/var/lib/ansible/local
|
|
||||||
ExecStartPre=/bin/bash -c 'if [ ! -d /var/lib/ansible/local/.git ]; then git clone -b main https://gitea.nb.bobbysh.me/Iron-Legion/ansible-pull-deploy.git /var/lib/ansible/local; else git -C /var/lib/ansible/local pull origin main; fi'
|
|
||||||
ExecStart=/usr/bin/ansible-playbook /var/lib/ansible/local/local.yml
|
|
||||||
StandardOutput=journal
|
|
||||||
StandardError=journal
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Ansible Pull — Iron Legion fleet baseline
|
|
||||||
After=network-online.target
|
|
||||||
Wants=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
WorkingDirectory=/var/lib/ansible/local
|
|
||||||
ExecStartPre=/bin/bash -c 'if [ ! -d /var/lib/ansible/local/.git ]; then git clone -b main https://gitea.nb.bobbysh.me/Iron-Legion/ansible-pull-deploy.git /var/lib/ansible/local; else git -C /var/lib/ansible/local pull origin main; fi'
|
|
||||||
ExecStart=/usr/bin/ansible-playbook /var/lib/ansible/local/local.yml
|
|
||||||
StandardOutput=journal
|
|
||||||
StandardError=journal
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Run ansible-pull every 15 minutes
|
|
||||||
|
|
||||||
[Timer]
|
|
||||||
OnBootSec=2min
|
|
||||||
OnUnitActiveSec=15min
|
|
||||||
Persistent=true
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=timers.target
|
|
||||||
88
autoinstall/first-boot.sh
Normal file
88
autoinstall/first-boot.sh
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# first-boot.sh — Iron Legion Fleet Node Post-Install Bootstrap
|
||||||
|
# Run manually AFTER confirming autoinstall boots successfully
|
||||||
|
# Date: May 23, 2026
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ========== CONFIG ==========
|
||||||
|
TRUENAS_IP="192.168.16.254"
|
||||||
|
NFS_SHARE="/mnt/Ice/Backup/swarm-data"
|
||||||
|
LOCAL_MOUNT="/mnt/swarm-data"
|
||||||
|
ANSIBLE_REPO="https://gitea.nb.bobbysh.me/Iron-Legion/ansible-pull-deploy.git"
|
||||||
|
ANSIBLE_DIR="/var/lib/ansible/local"
|
||||||
|
TAILSCALE_API_KEY="${TAILSCALE_API_KEY:-}" # Set before running, or hardcode for unattended
|
||||||
|
|
||||||
|
# ========== DOCKER ==========
|
||||||
|
echo "[1/6] Installing Docker CE..."
|
||||||
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker.gpg
|
||||||
|
echo "deb [arch=amd64 signed-by=/usr/share/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-buildx-plugin docker-compose-plugin
|
||||||
|
systemctl enable docker
|
||||||
|
systemctl start docker
|
||||||
|
usermod -aG docker jarvis
|
||||||
|
echo "Docker installed."
|
||||||
|
else
|
||||||
|
echo "Docker already present."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ========== NFS AUTOMOUNT ==========
|
||||||
|
echo "[2/6] Configuring NFS automount for TrueNAS..."
|
||||||
|
mkdir -p "$LOCAL_MOUNT"
|
||||||
|
if ! grep -q "$TRUENAS_IP:$NFS_SHARE" /etc/fstab; then
|
||||||
|
echo "$TRUENAS_IP:$NFS_SHARE $LOCAL_MOUNT nfs defaults,_netdev 0 0" >> /etc/fstab
|
||||||
|
mount "$LOCAL_MOUNT"
|
||||||
|
echo "NFS mount added to fstab and mounted."
|
||||||
|
else
|
||||||
|
echo "NFS fstab entry already exists."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ========== ANSIBLE-PULL REPO ==========
|
||||||
|
echo "[3/6] Cloning ansible-pull repo..."
|
||||||
|
mkdir -p "$ANSIBLE_DIR"
|
||||||
|
if [ ! -d "$ANSIBLE_DIR/.git" ]; then
|
||||||
|
cd "$ANSIBLE_DIR"
|
||||||
|
git clone "$ANSIBLE_REPO" .
|
||||||
|
echo "Ansible repo cloned."
|
||||||
|
else
|
||||||
|
echo "Ansible repo already present."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ========== TAILSCALE ==========
|
||||||
|
echo "[4/6] Installing Tailscale..."
|
||||||
|
if ! command -v tailscale >/dev/null 2>&1; then
|
||||||
|
curl -fsSL https://tailscale.com/install.sh | sh
|
||||||
|
echo "Tailscale installed."
|
||||||
|
else
|
||||||
|
echo "Tailscale already present."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$TAILSCALE_API_KEY" ]; then
|
||||||
|
echo "[5/6] Joining Tailnet..."
|
||||||
|
tailscale up --auth-key="$TAILSCALE_API_KEY" --ssh --accept-routes
|
||||||
|
echo "Tailscale joined."
|
||||||
|
else
|
||||||
|
echo "[5/6] TAILSCALE_API_KEY not set. Run manually:"
|
||||||
|
echo " export TAILSCALE_API_KEY=tskey-..."
|
||||||
|
echo " tailscale up --auth-key=\$TAILSCALE_API_KEY --ssh --accept-routes"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ========== ENABLE SERVICES ==========
|
||||||
|
echo "[6/6] Enabling services..."
|
||||||
|
systemctl enable ssh docker
|
||||||
|
systemctl restart ssh docker
|
||||||
|
|
||||||
|
# ========== DONE ==========
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo " Fleet node bootstrap complete"
|
||||||
|
echo "========================================"
|
||||||
|
echo "Docker: $(docker --version)"
|
||||||
|
echo "Tailscale: $(tailscale version 2>/dev/null | head -1 || echo 'not joined')"
|
||||||
|
echo "NFS mount: $(df -h | grep swarm-data || echo 'not mounted')"
|
||||||
|
echo "Ansible: $(ansible --version | head -1 || echo 'not checked')"
|
||||||
|
echo ""
|
||||||
|
echo "Next: run ansible-pull from $ANSIBLE_DIR"
|
||||||
|
echo " cd $ANSIBLE_DIR && ansible-pull -d . -U $ANSIBLE_REPO"
|
||||||
95
autoinstall/ubuntu-autoinstall-fleet-minimal.yaml
Normal file
95
autoinstall/ubuntu-autoinstall-fleet-minimal.yaml
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# Ubuntu Autoinstall — Iron Legion Fleet Standard (MINIMAL)
|
||||||
|
# Targets: GMKtec G9 N150, 1TB NVMe (/dev/nvme0n1)
|
||||||
|
# Date: May 23, 2026
|
||||||
|
# Role: Bare-metal bootable fleet node — manual post-install via first-boot.sh
|
||||||
|
|
||||||
|
version: 1
|
||||||
|
reporting:
|
||||||
|
builtin:
|
||||||
|
type: print
|
||||||
|
|
||||||
|
autoinstall:
|
||||||
|
identity:
|
||||||
|
hostname: fleet-node
|
||||||
|
username: jarvis
|
||||||
|
password: "$6$0DL8vh2WMWRpPiOt$xP1XyKFbX8J0hGSwd9GD6RsPAM5Ajdkrd8PYW2KJAv64YBJC3NAHgGr4BNYORodCVf1hkv3D2KhbezFoIlVsL1"
|
||||||
|
|
||||||
|
ssh:
|
||||||
|
install-server: true
|
||||||
|
authorized-keys:
|
||||||
|
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPSBrRCROUHOiZX9IB3teEK89VFfghbdu7OF5NoJ1Y6g Generated By Termius
|
||||||
|
allow-pw: true
|
||||||
|
|
||||||
|
network:
|
||||||
|
version: 2
|
||||||
|
ethernets:
|
||||||
|
enp4s0:
|
||||||
|
dhcp4: true
|
||||||
|
optional: true
|
||||||
|
enp5s0:
|
||||||
|
dhcp4: true
|
||||||
|
optional: true
|
||||||
|
enp6s0:
|
||||||
|
dhcp4: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
storage:
|
||||||
|
config:
|
||||||
|
- type: disk
|
||||||
|
id: nvme0n1
|
||||||
|
path: /dev/nvme0n1
|
||||||
|
ptable: gpt
|
||||||
|
wipe: superblock-recursive
|
||||||
|
- type: partition
|
||||||
|
id: boot-part
|
||||||
|
device: nvme0n1
|
||||||
|
size: 1GiB
|
||||||
|
flag: boot
|
||||||
|
- type: partition
|
||||||
|
id: root-part
|
||||||
|
device: nvme0n1
|
||||||
|
size: -1
|
||||||
|
- type: format
|
||||||
|
id: boot-format
|
||||||
|
volume: boot-part
|
||||||
|
fstype: ext4
|
||||||
|
- type: format
|
||||||
|
id: root-format
|
||||||
|
volume: root-part
|
||||||
|
fstype: ext4
|
||||||
|
- type: mount
|
||||||
|
id: boot-mount
|
||||||
|
device: boot-format
|
||||||
|
path: /boot
|
||||||
|
- type: mount
|
||||||
|
id: root-mount
|
||||||
|
device: root-format
|
||||||
|
path: /
|
||||||
|
swap:
|
||||||
|
size: 0
|
||||||
|
|
||||||
|
packages:
|
||||||
|
- openssh-server
|
||||||
|
- curl
|
||||||
|
- nfs-common
|
||||||
|
- cifs-utils
|
||||||
|
- net-tools
|
||||||
|
- ca-certificates
|
||||||
|
- gnupg
|
||||||
|
- ansible
|
||||||
|
- git
|
||||||
|
|
||||||
|
late-commands:
|
||||||
|
# Prevent cloud-init from stomping hostname on first boot
|
||||||
|
- echo 'preserve_hostname: true' > /target/etc/cloud/cloud.cfg.d/99_preserve_hostname.cfg
|
||||||
|
|
||||||
|
# Add jarvis to sudoers with NOPASSWD
|
||||||
|
- echo 'jarvis ALL=(ALL) NOPASSWD: ALL' > /target/etc/sudoers.d/jarvis
|
||||||
|
- chmod 440 /target/etc/sudoers.d/jarvis
|
||||||
|
|
||||||
|
# Ensure SSH key has correct permissions
|
||||||
|
- chmod 600 /target/home/jarvis/.ssh/authorized_keys
|
||||||
|
- chown -R 1000:1000 /target/home/jarvis/.ssh
|
||||||
|
|
||||||
|
# Auto-reboot after install completes
|
||||||
|
shutdown: reboot
|
||||||
126
autoinstall/ubuntu-autoinstall-fleet.yaml
Normal file
126
autoinstall/ubuntu-autoinstall-fleet.yaml
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# Ubuntu Autoinstall Template — Iron Legion Fleet Standard
|
||||||
|
# Generated May 23, 2026. Targets GMKtec G9 N150, 1TB NVMe (/dev/nvme0n1)
|
||||||
|
# Includes: jarvis user, SSH key, Docker, NFS client, Tailscale (optional)
|
||||||
|
|
||||||
|
version: 1
|
||||||
|
reporting:
|
||||||
|
builtin:
|
||||||
|
type: print
|
||||||
|
|
||||||
|
autoinstall:
|
||||||
|
identity:
|
||||||
|
hostname: ubuntu-fleet-node
|
||||||
|
username: jarvis
|
||||||
|
password: "$6$rounds=5000$fleet$salts$hashedpassword"
|
||||||
|
# ^^^ Generate with: mkpasswd -m sha-512 ubuntu
|
||||||
|
# Or use: python3 -c "import crypt; print(crypt.crypt('ubuntu', crypt.mksalt(crypt.METHOD_SHA512)))"
|
||||||
|
|
||||||
|
ssh:
|
||||||
|
install-server: true
|
||||||
|
authorized-keys:
|
||||||
|
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPSBrRCROUHOiZX9IB3teEK89VFfghbdu7OF5NoJ1Y6g Generated By Termius
|
||||||
|
allow-pw: true
|
||||||
|
|
||||||
|
network:
|
||||||
|
version: 2
|
||||||
|
ethernets:
|
||||||
|
enp4s0:
|
||||||
|
dhcp4: true
|
||||||
|
optional: true
|
||||||
|
enp5s0:
|
||||||
|
dhcp4: true
|
||||||
|
optional: true
|
||||||
|
enp6s0:
|
||||||
|
dhcp4: true
|
||||||
|
optional: true
|
||||||
|
# Note: enp5s0 is the Intel I226-V which drops link on some G9 units.
|
||||||
|
# Fallback to enp4s0 if enp5s0 fails.
|
||||||
|
|
||||||
|
storage:
|
||||||
|
config:
|
||||||
|
- type: disk
|
||||||
|
id: nvme0n1
|
||||||
|
path: /dev/nvme0n1
|
||||||
|
ptable: gpt
|
||||||
|
wipe: superblock-recursive
|
||||||
|
- type: partition
|
||||||
|
id: boot-part
|
||||||
|
device: nvme0n1
|
||||||
|
size: 1GiB
|
||||||
|
flag: boot
|
||||||
|
- type: partition
|
||||||
|
id: root-part
|
||||||
|
device: nvme0n1
|
||||||
|
size: -1
|
||||||
|
- type: format
|
||||||
|
id: boot-format
|
||||||
|
volume: boot-part
|
||||||
|
fstype: ext4
|
||||||
|
- type: format
|
||||||
|
id: root-format
|
||||||
|
volume: root-part
|
||||||
|
fstype: ext4
|
||||||
|
- type: mount
|
||||||
|
id: boot-mount
|
||||||
|
device: boot-format
|
||||||
|
path: /boot
|
||||||
|
- type: mount
|
||||||
|
id: root-mount
|
||||||
|
device: root-format
|
||||||
|
path: /
|
||||||
|
swap:
|
||||||
|
size: 0
|
||||||
|
|
||||||
|
packages:
|
||||||
|
- openssh-server
|
||||||
|
- curl
|
||||||
|
- nfs-common
|
||||||
|
- cifs-utils
|
||||||
|
- net-tools
|
||||||
|
- ca-certificates
|
||||||
|
- gnupg
|
||||||
|
|
||||||
|
late-commands:
|
||||||
|
# Fix hostname preservation (cloud-init stomp bug)
|
||||||
|
- echo 'preserve_hostname: true' > /target/etc/cloud/cloud.cfg.d/99_preserve_hostname.cfg
|
||||||
|
|
||||||
|
# Set hostname explicitly
|
||||||
|
- hostnamectl set-hostname ubuntu-fleet-node
|
||||||
|
|
||||||
|
# Add jarvis to sudoers with NOPASSWD
|
||||||
|
- echo 'jarvis ALL=(ALL) NOPASSWD: ALL' > /target/etc/sudoers.d/jarvis
|
||||||
|
- chmod 440 /target/etc/sudoers.d/jarvis
|
||||||
|
|
||||||
|
# Create SSH directory and inject key (fallback if ssh section fails)
|
||||||
|
- mkdir -p /target/home/jarvis/.ssh
|
||||||
|
- echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPSBrRCROUHOiZX9IB3teEK89VFfghbdu7OF5NoJ1Y6g Generated By Termius' > /target/home/jarvis/.ssh/authorized_keys
|
||||||
|
- chmod 600 /target/home/jarvis/.ssh/authorized_keys
|
||||||
|
- chown -R 1000:1000 /target/home/jarvis/.ssh
|
||||||
|
|
||||||
|
# Install Docker
|
||||||
|
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker.gpg
|
||||||
|
- echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu noble stable" > /target/etc/apt/sources.list.d/docker.list
|
||||||
|
- curtin in-target -- apt-get update
|
||||||
|
- curtin in-target -- apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
|
|
||||||
|
# Add jarvis to docker group
|
||||||
|
- usermod -aG docker jarvis
|
||||||
|
|
||||||
|
# Enable and start services
|
||||||
|
- systemctl enable docker
|
||||||
|
- systemctl enable ssh
|
||||||
|
|
||||||
|
# Install Tailscale (optional — needs auth key for auto-join)
|
||||||
|
# Uncomment and add TAILSCALE_AUTH_KEY to user-data if auto-join desired
|
||||||
|
# - curl -fsSL https://tailscale.com/install.sh | sh
|
||||||
|
# - tailscale up --auth-key=${TAILSCALE_AUTH_KEY}
|
||||||
|
|
||||||
|
# Clone ansible-pull repo (optional — needs git and network)
|
||||||
|
# - mkdir -p /target/var/lib/ansible/local
|
||||||
|
# - cd /target/var/lib/ansible/local && git clone https://gitea.nb.bobbysh.me/Iron-Legion/ansible-pull-deploy.git .
|
||||||
|
|
||||||
|
# Re-enable netplan for actual NIC that comes up
|
||||||
|
- sed -i 's/optional: true/optional: false/g' /target/etc/netplan/00-installer-config.yaml 2>/dev/null || true
|
||||||
|
|
||||||
|
# Shutdown after install (remove for auto-reboot)
|
||||||
|
# shutdown: reboot
|
||||||
27
host_vars/bones.yml
Normal file
27
host_vars/bones.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
# Bones (Mark XLI) — Headless CPU-only node
|
||||||
|
node_type: headless
|
||||||
|
has_gpu: false
|
||||||
|
|
||||||
|
# Headless essentials
|
||||||
|
extra_packages:
|
||||||
|
- cpufrequtils # CPU frequency management
|
||||||
|
- lm-sensors # Temperature monitoring
|
||||||
|
- smartmontools # Disk health monitoring
|
||||||
|
- hdparm # Disk performance tuning
|
||||||
|
- netdata # lightweight monitoring (optional)
|
||||||
|
|
||||||
|
# Services managed on Bones
|
||||||
|
managed_services:
|
||||||
|
- name: jarvis # Paperclip + Ollama + PostgreSQL stack
|
||||||
|
enabled: true
|
||||||
|
- name: ollama # CPU inference only
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# Ollama config (CPU mode, very small models)
|
||||||
|
ollama_models:
|
||||||
|
- gemma3:1b # Ultra-tiny for CPU
|
||||||
|
|
||||||
|
# Node-specific vars
|
||||||
|
bones_storage: "256GB SSD"
|
||||||
|
jvm_heap: "512m"
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=iVentoy PXE Server
|
|
||||||
After=network-online.target
|
|
||||||
Wants=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
WorkingDirectory=/opt/iventoy
|
|
||||||
Environment=LD_LIBRARY_PATH=/opt/iventoy/lib/lin64
|
|
||||||
ExecStart=/opt/iventoy/lib/iventoy
|
|
||||||
Restart=on-failure
|
|
||||||
RestartSec=5
|
|
||||||
StandardOutput=journal
|
|
||||||
StandardError=journal
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
- vim
|
- vim
|
||||||
- python3-pip
|
- python3-pip
|
||||||
- cifs-utils
|
- cifs-utils
|
||||||
|
- nfs-common
|
||||||
state: present
|
state: present
|
||||||
when: ansible_os_family == "Debian"
|
when: ansible_os_family == "Debian"
|
||||||
tags: [baseline]
|
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
|
||||||
49
preseeds/curtin_userdata_fleet_v5.yaml
Normal file
49
preseeds/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']
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
export SUDO_ASKPASS=/tmp/askpass/askpass.sh
|
|
||||||
sudo -A pkill -f "iventoy" || true
|
|
||||||
sleep 2
|
|
||||||
cd /opt/iventoy
|
|
||||||
export LD_LIBRARY_PATH=/opt/iventoy/lib/lin64
|
|
||||||
nohup ./lib/iventoy > /dev/null 2>&1 &
|
|
||||||
sleep 3
|
|
||||||
pgrep -f iventoy
|
|
||||||
160
ssh-config
160
ssh-config
@@ -1,160 +0,0 @@
|
|||||||
# Read more about SSH config files: https://linux.die.net/man/5/ssh_config
|
|
||||||
|
|
||||||
# Core Services
|
|
||||||
# Reverse Proxy
|
|
||||||
#***********#
|
|
||||||
# Local Net #
|
|
||||||
#***********#
|
|
||||||
Host artemis
|
|
||||||
HostName 192.168.15.182
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
Host mark5
|
|
||||||
HostName 192.168.6.5
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
Host mark44
|
|
||||||
HostName 192.168.5.214
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
|
|
||||||
#========================#
|
|
||||||
# Tailscale alternatives #
|
|
||||||
#========================#
|
|
||||||
Host ts-artemis
|
|
||||||
HostName 100.100.97.18
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
Host ts-mark44
|
|
||||||
HostName 100.75.26.83
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
Host ts-mark5
|
|
||||||
HostName 100.118.67.105
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
#======================#
|
|
||||||
# Netbird alternatives #
|
|
||||||
#======================#
|
|
||||||
Host nb-artemis
|
|
||||||
HostName 100.100.97.18
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Host nebuchadnezzar
|
|
||||||
HostName 100.99.123.16
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/id_nebuchadnezzar
|
|
||||||
IdentitiesOnly yes
|
|
||||||
|
|
||||||
Host gitea.nb.bobbysh.me
|
|
||||||
HostName gitea.nb.bobbysh.me
|
|
||||||
User git
|
|
||||||
IdentityFile ~/.ssh/gitea_api_key
|
|
||||||
StrictHostKeyChecking no
|
|
||||||
IdentitiesOnly yes
|
|
||||||
|
|
||||||
Host cinnamint
|
|
||||||
HostName 100.99.65.75
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
IdentitiesOnly yes
|
|
||||||
|
|
||||||
Host shield
|
|
||||||
HostName 192.168.27.205
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
IdentitiesOnly yes
|
|
||||||
|
|
||||||
Host mk7
|
|
||||||
HostName 192.168.7.7
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
Host ts-mk7
|
|
||||||
HostName 100.66.70.51
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
# mk-33 Silver Centurion
|
|
||||||
Host mk33
|
|
||||||
HostName 192.168.0.190
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
Host ts-mk33
|
|
||||||
HostName 100.125.155.41
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
# mk-34 Southpaw
|
|
||||||
Host mk34
|
|
||||||
HostName 192.168.0.123
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
Host ts-mk34
|
|
||||||
HostName 100.94.190.43
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
# mk-39 Gemini
|
|
||||||
Host mk39
|
|
||||||
HostName 192.168.0.106
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
Host ts-mk39
|
|
||||||
HostName 100.125.155.41
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
# mk-42 Extremis
|
|
||||||
Host mk42
|
|
||||||
HostName 192.168.0.196
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
Host ts-mk42
|
|
||||||
HostName 100.94.190.43
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
# Shield (iVentoy PXE Server, formerly Bones)
|
|
||||||
Host ts-shield
|
|
||||||
HostName 100.109.254.36
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
# Igor (ZimaOS NAS, Mark XXXVIII)
|
|
||||||
Host igor
|
|
||||||
HostName 192.168.10.211
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
# TrueNAS (beelink-tns)
|
|
||||||
Host truenas
|
|
||||||
HostName 192.168.16.254
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
Host mission-control
|
|
||||||
HostName 100.96.128.121
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/id_ed25519_windows
|
|
||||||
IdentitiesOnly yes
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
# Read more about SSH config files: https://linux.die.net/man/5/ssh_config
|
|
||||||
|
|
||||||
# Core Services
|
|
||||||
# Reverse Proxy
|
|
||||||
#***********#
|
|
||||||
# Local Net #
|
|
||||||
#***********#
|
|
||||||
Host artemis
|
|
||||||
HostName 192.168.15.182
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
Host mark5
|
|
||||||
HostName 192.168.6.5
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
Host mark44
|
|
||||||
HostName 192.168.5.214
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
|
|
||||||
#========================#
|
|
||||||
# Tailscale alternatives #
|
|
||||||
#========================#
|
|
||||||
Host ts-artemis
|
|
||||||
HostName 100.100.97.18
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
Host ts-mark44
|
|
||||||
HostName 100.75.26.83
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
Host ts-mark5
|
|
||||||
HostName 100.118.67.105
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
#======================#
|
|
||||||
# Netbird alternatives #
|
|
||||||
#======================#
|
|
||||||
Host nb-artemis
|
|
||||||
HostName 100.100.97.18
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Host nebuchadnezzar
|
|
||||||
HostName 100.99.123.16
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/id_nebuchadnezzar
|
|
||||||
IdentitiesOnly yes
|
|
||||||
|
|
||||||
Host gitea.nb.bobbysh.me
|
|
||||||
HostName gitea.nb.bobbysh.me
|
|
||||||
User git
|
|
||||||
IdentityFile ~/.ssh/gitea_api_key
|
|
||||||
StrictHostKeyChecking no
|
|
||||||
IdentitiesOnly yes
|
|
||||||
|
|
||||||
Host cinnamint
|
|
||||||
HostName 100.99.65.75
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
IdentitiesOnly yes
|
|
||||||
|
|
||||||
Host shield
|
|
||||||
HostName 192.168.27.205
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
IdentitiesOnly yes
|
|
||||||
|
|
||||||
# Shield iVentoy PXE Server (Tailscale)
|
|
||||||
Host ts-shield
|
|
||||||
HostName 100.109.254.36
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
Host mk7
|
|
||||||
HostName 192.168.7.7
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
Host ts-mk7
|
|
||||||
HostName 100.66.70.51
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
# mk-33 Silver Centurion
|
|
||||||
Host mk33
|
|
||||||
HostName 192.168.0.190
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
Host ts-mk33
|
|
||||||
HostName 100.125.155.41
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
# mk-34 Southpaw
|
|
||||||
Host mk34
|
|
||||||
HostName 192.168.0.123
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
Host ts-mk34
|
|
||||||
HostName 100.94.190.43
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
# mk-39 Gemini
|
|
||||||
Host mk39
|
|
||||||
HostName 192.168.0.106
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
Host ts-mk39
|
|
||||||
HostName 100.125.155.41
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
# mk-42 Extremis
|
|
||||||
Host mk42
|
|
||||||
HostName 192.168.0.196
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
Host ts-mk42
|
|
||||||
HostName 100.94.190.43
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
# Igor (ZimaOS NAS, Mark XXXVIII)
|
|
||||||
Host igor
|
|
||||||
HostName 192.168.10.211
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
# TrueNAS (beelink-tns)
|
|
||||||
Host truenas
|
|
||||||
HostName 192.168.16.254
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/artemis_key
|
|
||||||
StrictHostKeyChecking accept-new
|
|
||||||
|
|
||||||
Host mission-control
|
|
||||||
HostName 100.96.128.121
|
|
||||||
User jarvis
|
|
||||||
IdentityFile ~/.ssh/id_ed25519_windows
|
|
||||||
IdentitiesOnly yes
|
|
||||||
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