feat: skrip auto pasang NIC macvlan + DHCP untuk instance incus

This commit is contained in:
oxpoe committed 2026-10-17 14:08:44 +07:00
commit 4a86be23c8
3 files changed
+234

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 oxpoe
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+85
View File
@@ -0,0 +1,85 @@
# auto-macvlan
Skrip untuk memasang **NIC macvlan** ke instance **Incus** dan mengaktifkan **DHCP di guest**, sehingga instance langsung mendapat IP di LAN (mis. `192.168.200.x`) — bukan IP NAT internal incus.
Cocok untuk instance yang perlu diakses langsung dari LAN (web, SSH, service yang di-publish podman/docker di dalamnya).
## Pemakaian
```bash
./incus-macvlan.sh <instance> [device] [network]
```
Contoh:
```bash
# pakai default: device eth1, network eth200
./incus-macvlan.sh web1
# tentukan device & network
./incus-macvlan.sh web1 eth1 eth200
# buat network macvlan bila belum ada
./incus-macvlan.sh web1 --create --parent enp2s0
```
## Opsi
| Opsi | Default | Keterangan |
|---|---|---|
| `--parent <iface>` | dari default route host | Interface fisik host untuk parent macvlan |
| `--network <name>` | `eth200` | Nama network macvlan di incus |
| `--device <name>` | `eth1` | Nama NIC di dalam guest |
| `--create` | mati | Buat network macvlan kalau belum ada |
| `-h, --help` | | Bantuan |
Argumen posisional: `$1` instance, `$2` device, `$3` network. Opsi mengalahkan posisional.
## Yang dilakukan
1. Cek instance ada dan berjalan (start bila perlu).
2. Pastikan network macvlan ada (buat dengan `--create` bila perlu).
3. Pasang NIC `nic network=<network> name=<device>` ke instance (hotplug).
4. Aktifkan DHCP di guest:
- **NetworkManager** → `nmcli`
- **systemd-networkd** → `/etc/systemd/network/20-<device>.network`
- fallback: `dhcpcd` / `dhclient` / `udhcpc`
5. Tunggu dan tampilkan IP LAN.
Idempotent — aman dijalankan ulang.
## Persiapan network macvlan (sekali)
```bash
incus network create eth200 --type=macvlan parent=enp2s0
```
Atau biarkan skrip membuatnya dengan `--create`.
## Syarat
- Host Incus punya NIC fisik yang terhubung ke LAN (parent, mis. `enp2s0`).
- **DHCP server di LAN** aktif (IP instance berasal dari router LAN, bukan incus).
- Guest punya salah satu DHCP client di atas.
## Catatan penting
- **Host Incus tidak bisa menghubungi instance lewat IP LAN macvlan** (batasan macvlan). Dari host, kelola lewat `incus exec`; instance tetap bisa diakses dari perangkat LAN lain.
- Bila instance punya **dua NIC** (incusbr0 + macvlan) dan default route dari incusbr0 menang, balasan ke LAN bisa keluar lewat NIC yang salah. Solusi: jadikan macvlan default dengan metrik lebih rendah, mis.:
```ini
# /etc/systemd/network/20-eth1.network
[Match]
Name=eth1
[Network]
DHCP=yes
[DHCPv4]
RouteMetric=50
```
atau nonaktifkan NIC incusbr0 bila tidak diperlukan.
- IP yang didapat dinamis dari LAN. Kalau butuh statis, atur DHCP reservation di router atau konfigurasi statis di guest.
## Lisensi
MIT — lihat [LICENSE](LICENSE).
+128
View File
@@ -0,0 +1,128 @@
#!/usr/bin/env bash
#
# incus-macvlan.sh — pasang NIC macvlan + aktifkan DHCP di guest incus,
# supaya instance langsung dapat IP di LAN (mis. 192.168.200.x).
#
# Pakai:
# ./incus-macvlan.sh <instance> [device] [network]
# ./incus-macvlan.sh web1
# ./incus-macvlan.sh web1 eth1 eth200
#
# Opsi:
# --parent <iface> interface fisik host (default: dari default route host)
# --network <name> nama network macvlan (default: eth200)
# --device <name> nama NIC di guest (default: eth1)
# --create buat network macvlan kalau belum ada
# -h, --help bantuan
#
# Idempotent: aman dijalankan berulang. Kalau instance sudah punya NIC
# di network macvlan (termasuk dari profile), NIC tidak ditambahkan lagi.
set -euo pipefail
INSTANCE=""; DEVICE="eth1"; NETWORK="eth200"; PARENT=""; CREATE=0
POS=()
say(){ printf '%s\n' "$*"; }
die(){ printf 'error: %s\n' "$*" >&2; exit 1; }
while [ $# -gt 0 ]; do
case "$1" in
--parent) PARENT="${2:?--parent butuh nilai}"; shift 2 ;;
--network) NETWORK="${2:?--network butuh nilai}"; shift 2 ;;
--device) DEVICE="${2:?--device butuh nilai}"; shift 2 ;;
--create) CREATE=1; shift ;;
-h|--help) sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
-*) die "opsi tak dikenal: $1" ;;
*) POS+=("$1"); shift ;;
esac
done
INSTANCE="${POS[0]:-}"
[ -n "${POS[1]:-}" ] && DEVICE="${POS[1]}"
[ -n "${POS[2]:-}" ] && NETWORK="${POS[2]}"
[ -n "$INSTANCE" ] || die "pakai: $(basename "$0") <instance> [device] [network]"
[[ "$DEVICE" =~ ^[A-Za-z0-9_.-]+$ ]] || die "nama device tidak valid: $DEVICE"
[[ "$NETWORK" =~ ^[A-Za-z0-9_.-]+$ ]] || die "nama network tidak valid: $NETWORK"
command -v incus >/dev/null 2>&1 || die "perintah 'incus' tidak ditemukan"
incus info "$INSTANCE" </dev/null >/dev/null 2>&1 || die "instance '$INSTANCE' tidak ditemukan"
if [ -z "$PARENT" ]; then
PARENT="$(ip route show default 2>/dev/null | awk '{print $5; exit}')"
PARENT="${PARENT:-enp2s0}"
fi
# ---- network macvlan ----
if incus network list -c n -f csv </dev/null 2>/dev/null | grep -qx "$NETWORK"; then
say "network '$NETWORK' sudah ada"
else
[ "$CREATE" = 1 ] || die "network '$NETWORK' belum ada (pakai --create, parent $PARENT)"
say "membuat network macvlan '$NETWORK' (parent $PARENT)"
incus network create "$NETWORK" --type=macvlan parent="$PARENT" </dev/null
fi
# ---- pastikan running ----
STATE="$(incus info "$INSTANCE" </dev/null | awk -F': ' '/^Status:/{print $2}')"
if [ "$STATE" != "RUNNING" ]; then
say "menyalakan instance '$INSTANCE'"
incus start "$INSTANCE" </dev/null
sleep 3
fi
# ---- pasang NIC (kalau belum ada NIC di network ini, termasuk dari profile) ----
if incus config show --expanded "$INSTANCE" </dev/null 2>/dev/null | grep -q "network: $NETWORK"; then
say "instance sudah punya NIC di network '$NETWORK' (dilewati)"
elif incus config device list "$INSTANCE" </dev/null | grep -qx "$DEVICE"; then
say "device '$DEVICE' sudah ada (dilewati)"
else
if incus config device add "$INSTANCE" "$DEVICE" nic network="$NETWORK" name="$DEVICE" </dev/null; then
say "NIC '$DEVICE' (network $NETWORK) ditambahkan"
else
say "peringatan: gagal menambah NIC '$DEVICE' (lanjut cek DHCP)"
fi
fi
# ---- aktifkan DHCP di guest untuk semua NIC tanpa IPv4 ----
say "mengaktifkan DHCP di guest"
incus exec "$INSTANCE" -- sh -c '
configure_one() {
dev="$1"
if systemctl is-active --quiet NetworkManager 2>/dev/null; then
nmcli device connect "$dev" >/dev/null 2>&1 || true
elif systemctl is-active --quiet systemd-networkd 2>/dev/null || [ -d /etc/systemd/network ]; then
printf "[Match]\nName=%s\n[Network]\nDHCP=yes\n" "$dev" > "/etc/systemd/network/20-$dev.network"
elif command -v dhcpcd >/dev/null 2>&1; then dhcpcd "$dev" >/dev/null 2>&1 || true
elif command -v dhclient >/dev/null 2>&1; then dhclient "$dev" >/dev/null 2>&1 || true
elif command -v udhcpc >/dev/null 2>&1; then udhcpc -i "$dev" >/dev/null 2>&1 || true
fi
}
for i in /sys/class/net/*; do
dev=$(basename "$i"); [ "$dev" = lo ] && continue
ip -4 addr show "$dev" 2>/dev/null | grep -q "inet " && continue
configure_one "$dev"
done
systemctl restart systemd-networkd >/dev/null 2>&1 || true
' </dev/null || say "peringatan: konfigurasi DHCP di guest gagal (lanjut)"
# ---- tunggu IP LAN ----
IP=""
for _ in $(seq 1 20); do
IP="$(incus list "$INSTANCE" -c 4 -f csv </dev/null 2>/dev/null \
| tr ',' '\n' | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' \
| grep -vE '^(10\.|127\.)' | head -1 || true)"
[ -n "$IP" ] && break
sleep 1
done
say ""
if [ -n "$IP" ]; then
say "instance : $INSTANCE"
say "network : $NETWORK (parent $PARENT)"
say "IP LAN : $IP"
say "SSH : ssh root@$IP"
else
say "instance : $INSTANCE"
say "IP LAN : belum terdeteksi"
say "cek : incus exec $INSTANCE -- ip -4 addr"
exit 1
fi