3 minggu lalu saya menjalankan Asahi Linux (Fedora) di Mac Mini M2 (via asahi-fedora-builder) dan menjadikannya anggota baru di cluster jaringan rumah a.k.a homelab. Server ini menjalankan aplikasi-aplikasi krusial seperti database dan beberapa aplikasi yang cukup rakus dengan memory dan CPU seperti PeerTube.
Update: Per tulisan ini diterbitkan, Asahi Linux sudah dapat dipasang di Mac Mini M2 melalui "cara resmi" yang dapat dilihat disini.
Aplikasi-aplikasi tersebut berjalan di VM menggunakan libvirt. Dan, yes, menggunakan KVM.
Untuk konfigurasi jaringan di libvirt, bawaannya adalah menggunakan NAT-based network, yang singkatnya, si libvirt server bertindak sebagai router, dan, ya, mungkin sudah bosan ya (gw) membahas tentang NAT?
Jenis jaringan bawaan si libvirt yang NAT-based ini umumnya menggunakan subnet 192.168.122.0/24, dan traffic masuk dari/ke subnet tersebut terjadi di host tersebut.
Saya ada 2 subnet yang satu 192.168.122.0/24 dan yang satu 192.168.123.0/24, yang mana berada di 2 jaringan berbeda, lalu bagaimana cara menghubungkannya?
NAT!
Cara paling mudah adalah dengan menggunakan Tailscale:
- Setup subnet router untuk kedua subnet tersebut
- Aktifkan
--accept-routes
- ...
- Profit
Ini ideal untuk setup "remote access" ke mesin yang tidak menjalankan Tailscale node. Tapi proses routing tetap melalui device tailscale0
yang entah bagaimana sering terjadi masalah khususnya pasca reboot dan satu-satunya solusinya adalah menjalankan ulang tailscale daemon sehingga subnet 122 dan 123 bisa kembali berkomunikasi.
Dan, perlu konfigurasi lagi di router (harusnya di bagian "next hop") untuk memastikan si subnet router dapat bertukar paket, dan ini relatif mudah.
Tapi karena cukup frustasi karena harus menjalankan ulang tersebut (yang mana tidak bisa dilakukan secara remote) akhirnya tulisan ini dibuat.
Subnet subnet something
Komputer berkomunikasi dengan komputer lainnya menggunakan alamat IP, dan mari kita kesampingan pembahasan untuk media lain seperti bluetooth dan inframerah (pernah kirim lagu dengan menempelkan 2 hp?).
Komputer terhubung melalui sebuah "modular connector" yang umumnya disebut sebagai "kabel LAN/RJ/UTP" jika bertanya ke toko listrik. Kabel tersebut menghubungkan sebuah komponen yang bernama Network Interface Controller/Card (NIC) yang nantinya digunakan untuk bertukar paket/informasi.
Secara teknis, menghubungkan dua komputer dengan 1 "kabel LAN" yang saling terhubung saja sudah cukup selama tidak ada konflik di penggunaan alamat IP dan Media Access Control (MAC) Address. Bagiamana bila komputer yang ingin berhubungan ada 3 atau 30 atau 300?
Media itu beragam, ada Hub; Switch, dan Router. Media atau perangkat tersebut umumnya menyediakan lebih dari 1 port dan yang cukup favorit untuk skala rumah adalah 5, yang mana dapat menghubungkan 4 komputer sekaligus melalui kabel. Dan mari kita kesampingkan pembahasan tentang Router vs Switch.
Dalam membuat jaringan, biasanya kita melakukan segmentasi untuk sebuah alasan. Misal, dengan membuat jaringan per-lantai, sehingga semua perangkat di lantai dua tidak perlu tersambung semuanya ke lantai satu menggunakan kabel sepanjang 20 meter. Dalam praktiknya, tujuan melakukan segmentasi adalah untuk alasan keamanan dan efisiensi.
Subnet (subnetwork) gampangnya adalah jaringan dalam jaringan. Secara teori, total alamat IP (v4) yang bisa digunakan adalah 4,294,967,296 (2^32) dan bisa dikategorikan berdasarkan "network mask" dan "prefix size" nya (IP class). Paling umum di skala jaringan yang kecil adalah menggunakan prefix /24, ada total 256 (2^(32-24)) alamat IP yang bisa digunakan.
Seharusnya 2 alamat IP sudah "dipesan" untuk alamat broadcast, jadi tersisa hanya 254. Jika melihat subnet 192.168.122.0/24, berarti total alamat IP yang bisa digunakan oleh komputer pada jaringan tersebut adalah 192.168.122.1 s/d 192.168.122.254.
Yang mana 192.168.122.0 dan 192.168.122.255 adalah alamat broadcast.
Singkatnya, membuat 300 VM di jaringan dengan subnet /24 tidak akan ideal bila setiap VM memiliki 1 alamat IP dari subnet tersebut.
DHCP DHCP something
Mungkin ada pertanyaan: siapa yang memberikan alamat IP kepada komputer-komputer saya? Setiap komputer pada dasarnya dapat mengatur alamat IP nya secara mandiri, namun dalam praktiknya, umumnya menggunakan sebuah teknologi bernama DHCP.
Model komunikasi DHCP adalah client-server: si dhcp client akan "mem-broadcast" paket, si dhcp server akan menawarkan alamat IP, si dhcp client akan mengajukan ip tersebut, lalu si dhcp server akan mengakui jika alamat IP tersebut adalah milik si X, yang mana nilai X adalah MAC address. Alur ini terkenal dengan Discover, Offer, Request, Acknowledge (DORA).
Bagaimna jika ada 2 atau lebih DHCP server? Bagiamana jika yang menawarkan alamat IP tersebut bukanlah DHCP server yang seharusnya? Well, itu pembahasan lain.
Setiap router umumnya menjalankan DHCP server, dan jika di lingkungan GNU/Linux umumnya melalui program bernama dnsmasq
.
Saat menjalankan DHCP server, seharusnya kita perlu mengkonfigurasi dari subnet yang akan digunakan; jangkauan alamat IP yang bisa digunakan, sampai ke DNS server yang harus digunakan.
Routing antar subnet melalui Tailscale
Baiklah cukup sudah computer networking crash course nya dan kita kembali ke topik utama. Gambaran sekarang dari jaringan homelab kita misal seperti ini:
Untuk saat ini, routing terjadi di overlay network via Tailscale, jadi bila vm3 di server2 melakukan ping ke vm1 di server1, alur nya adalah seperti ini di perspektif server2:
- vnet20 P IP 192.168.122.4 > 192.168.123.2: ICMP echo request
- virbr0 In IP 192.168.122.4 > 192.168.123.2: ICMP echo request
- tailscale0 Out IP 100.80.2.61 > 192.168.123.2: ICMP echo request
- tailscale0 In IP 192.168.123.2 > 100.80.2.61: ICMP echo reply
- virbr0 Out IP 192.168.123.2 > 192.168.122.4: ICMP echo reply
- vnet20 Out IP 192.168.123.2 > 192.168.122.4: ICMP echo reply
Dan seperti ini di perspektif si server1:
- tailscale0 In IP 100.80.2.61 > 192.168.123.2 ICMP echo request
- virbr0 Out IP 192.168.123.1 > 192.168.123.2: ICMP echo request
- vnet1 Out IP 192.168.123.1 > 192.168.123.2: ICMP echo request
- vnet1 P IP 192.168.123.2 > 192.168.123.1: ICMP echo reply
- virbr0 In IP 192.168.123.2 > 192.168.123.1: ICMP echo reply
- tailscale0 Out IP 192.168.123.2 > 100.80.2.61: ICMP echo reply
Jika tidak melakukan "rewrite" source IP nya (lihat di poin nomor 2), berarti seperti ini:
- tailscale0 In IP 100.80.2.61 > 192.168.123.2: ICMP echo request
- virbr0 Out IP 100.80.2.61 > 192.168.123.2: ICMP echo request
- vnet1 Out IP 100.80.2.61 > 192.168.123.2: ICMP echo request
- vnet1 P IP 192.168.123.2 > 100.80.2.61: ICMP echo reply
- virbr0 In IP 192.168.123.2 > 100.80.2.61: ICMP echo reply
- tailscale0 Out IP 192.168.123.2 > 100.80.2.61: ICMP echo reply
Setup bridge device
Bridged Network hanya possible bila si libvirt server (host) tersambung ke LAN melalui Ethernet. Jika tidak (misal seperti menggunakan Wi-Fi), tetaplah menggunakan NAT-based network (default).
Anyway, ini kita perlu sedikit pengetahuan terkait ehm per sysadmin-an. Biasanya, mesin dengan sistem operasi GNU/Linux menggunakan antara NetworkManager atau systemd-networkd untuk mengatur jaringan.
Disini anggap kita menggunakan NetworkManager, dan tools favorit saya adalah nmcli(1)
.
First thing first, kita perlu membuat "bridge devices". Untuk memastikan (iseng) apakah sudah ada bridge devices di komputer mu, bisa jalankan nmcli dev status
yang misal output nya seperti ini:
$ nmcli device status
DEVICE TYPE STATE CONNECTION
eno1 ethernet connected Wired connection 1
tailscale0 tun connected (externally) tailscale0
virbr0 bridge connected (externally) virbr0
vnet20 tun connected (externally) vnet20
vnet24 tun connected (externally) vnet24
vnet28 tun connected (externally) vnet28
Jika menggunakan Docker, kemungkinan sudah ada bridge devices yang dibuat doi. fwiw.
Untuk mulai membuatnya, bisa menjalankan nmcli conn add
seperti ini:
$ nmcli conn add type bridge con-name br0 ifname br0
Penggunaan br0
hanyalah preferensi. Bisa menggunakan nama lain seperti br00
atau b00135
misalnya.
Setelah itu, kita perlu membuat device type Ethernet
baru yang nantinya digunakan sebagai "jembatan" dengan device fisik yang ada di komputer (yang dalam kasus saya adalah eno1
):
$ nmcli conn add type ethernet slave-type bridge con-name bridge-br0 ifname eno1 master br0
Penggunaan nama bridge-br0
adalah preferensi, sekali lagi.
Setelah itu, kita bisa melihat device baru kita menggunakan nmcli conn show
seperti ini:
$ nmcli conn show
NAME UUID TYPE DEVICE
br0 836ce290-afee-4354-a1e2-ee2b5b4cd145 bridge br0
netplan-eno1 10838d80-caeb-349e-ba73-08ed16d4d666 ethernet eno1
tailscale0 59f3e6fa-ce8b-4670-973f-bcd1310ef928 tun tailscale0
virbr0 1e834475-b336-454d-a30a-dd26300fdb36 bridge virbr0
vnet20 5ede6630-c754-49cc-a82f-2a5a6a268243 tun vnet20
vnet24 96555b9a-363c-466d-b22e-c7aa4b16a279 tun vnet24
vnet28 94be3f7f-79da-4f01-a42c-f3647a78c610 tun vnet28
Lalu kita aktifkan:
$ nmcli conn up br0
Connection successfully activated (master waiting for slaves) (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/69
Dan kita matikan Ethernet devices yang sudah berjalan, karena you know, master waiting for slaves.
$ nmcli conn down netplan-eno1
Connection 'netplan-eno1' successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/60
Nama netplan-eno1
diatas bisa disesuaikan dengan nama device lama dengan type Ethernet di keluaran nmcli conn show
sebelumnya.
Jika tersambung melalui SSH, lalu terputus, tenang, jangan panik. Berdiri dulu selama 15 detik, tarik napas, ambil air mineral di kulkas, bikin kopi, lihat pemandangan di jendela, dan relax. Nikmati hidup sebentar setelah cosplay menjadi sysadmin.
Koneksi SSH terputus karena alamat IP kemungkinan berubah (khususnya jika MAC Address nya somehow berubah). Lihat di router untuk alamat IP barunya, dan sambung kembali melalui SSH.
Daan device br0
kita sudah muncul!
$ nmcli dev status
DEVICE TYPE STATE CONNECTION
br0 bridge connected br0
tailscale0 tun connected (externally) tailscale0
virbr0 bridge connected (externally) virbr0
vnet20 tun connected (externally) vnet20
vnet24 tun connected (externally) vnet24
vnet28 tun connected (externally) vnet28
eno1 ethernet connected bridge-br0
Hell yeah.
Setup bridged network di libvirt
Ini bagian yang paling singkat. Dan saya menganggap anda menggunakan virsh(1)
juga. Disini saya ingin mengubah konfigurasi jaringan VM yang sebelumnya menggunakan bridge virbr0
menjadi br0
yang telah kita buat tadi.
First thing first, kita perlu membuat "network" baru di virsh. Buat file dengan nama bridge.xml dengan konten seperti ini:
<network>
<name>bridge-host</name>
<forward mode="bridge"/>
<bridge name="br0"/>
</network>
Bagian bridge-host
dan br0
bisa disesuaikan sesuai kebutuhan dan selera. Selanjutnya, kita import konfigurasi tersebut:
$ virsh net-define bridge.xml
Network bridge-host defined from bridge.xml
Sip. Lalu kita jalankan:
$ virsh net-start bridge-host
Network bridge-host started
Dan cek untuk memastikan:
$ virsh net-list
Name State Autostart Persistent
------------------------------------------------
bridge-host active no yes
default active yes yes
$ virsh net-info bridge-host
Name: bridge-host
UUID: 28b5ee7b-a258-4cf4-b64a-4d5231d7ffcf
Active: yes
Persistent: yes
Autostart: no
Bridge: br0
Sip. Kita buat tu network "autostart" dengan perintah virsh net-autostart bridge-host
.
Lalu kita network interfaces di VM yang digunakan menggunakan virsh edit <name>
. Bisa jalankan virsh list
jika lupa name
nya apa.
Cari bagian <interface>
:
- <interface type='bridge'>
+ <interface type='network'>
- <source bridge='virbr0'/>
+ <source network='bridge-host'/>
</interface>
Alternatif lain jika tidak ingin mendefinisikan jaringan, bisa langsung seperti ini:
<interface type='bridge'>
- <source bridge='virbr0'/>
+ <source bridge='br0'/>
</interface>
Dan reboot menggunakan virsh reboot <name>
atau virsh shutdown <name>
dan virsh start <name>
. Untuk memastikan vm sudah menggunakan DHCP server baru, jalankan ip a
dan pastikan sudah menggunakan subnet yang seharusnya.
Bagaimana jika VM tiba-tiba tidak mendapatkan IP??? Santai. Jalankan dhclient
untuk yang menggunakan Debian atau /etc/init.d/networking restart
untuk Alpine enjoyer dan relaxxx.
Routing antar komputer dalam komputer
Sekarang seharusnya routing tidak berada di overlay network lagi (so long, Tailscale!). Masih dengan kasus yang sama (server2 melakukan ping ke vm1 di server1), berikut sekarang gambarannya di server2:
- end0 P IP 192.168.100.45 > 192.168.100.47: ICMP echo request
- vnet4 Out IP 192.168.100.45 > 192.168.100.47: ICMP echo request
- vnet4 P IP 192.168.100.47 > 192.168.100.45: ICMP echo reply
- end0 Out IP 192.168.100.47 > 192.168.100.45: ICMP echo reply
Daan di server1:
- vnet31 P IP 192.168.100.45 > 192.168.100.47: ICMP echo request
- eno1 Out IP 192.168.100.45 > 192.168.100.47: ICMP echo request
- eno1 P IP 192.168.100.47 > 192.168.100.45: ICMP echo reply
- vnet31 Out IP 192.168.100.47 > 192.168.100.45: ICMP echo reply
Setup VLAN
Ini bonus dan opsional. Karena Access Point (router "Wi-Fi") saya tersambung ke router utama, berarti doi menjadi "gateway" terhadap perangkat-perangkat yang berada di 1 subnet yang sama yakni 192.168.100.0/24.
Tentu saja ini bisa diatasi dengan membuat firewall rules, tapi karena saya menggunakan Switch, saya bisa melakukan isolasi secara "virtual" menggunakan 802.1Q alias dot1q:
Sip.
Dengan begini, aplikasi-aplikasi yang terpasang di perangkat nirkabel tidak bisa iseng menjelajahi LAN saya.
Dan jika harus kembali berurusan dengan firewall/packet filter, untuk apa tulisan ini diterbitkan?
Mengawinkan antar subnet
Remote access ke vm sudah bisa dilakukan darimanapun, namun ada sedikit kendala saat melakukan remote connect dari rumah. Jaringan 192.168.1.0/24 (AP) dan 192.168.100.0/24 (homelab) logically isolated melalui VLAN sehingga "direct connect" alias peer-to-peer hampir tidak bisa terjadi: koneksi selalu "di relay" melalui DERP yang meskipun saya sudah menjalankan DERP sendiri di Jakarta, tempat homelab saya berada sekarang — bebas untuk digunakan siapapun btw.
Artinya, saat saya mengakses vm water7 di server nuc dari steam deck yang jaraknya hanya 5cm dari tempat saya duduk, paket akan dibawa terlebih dahulu ke router saya; lalu ke internet, lalu ke vm yang berjalan di aws, balik lagi ke router saya, ke server nuc, dan barulah sampai.
Solusinya relatif sederhana: jalankan tailscale yang satu member dengan si VLAN, yang dalam kasus ini adalah ehm si Edge Router.
Problem solved. Di perangkat yang tidak terpasang Tailscale tetap tidak bisa mengakses 192.168.100.0/24 dan setiap perangkat di 192.168.100.0/24 tetap menggunakan routing di default interface.
Lalu kita memiliki masalah lagi (lmao): Kita tidak bisa direct connect ke host antar tailscale node (diluar LAN) karena 1) host menggunakan subnet yang sama via bridge network dan 2) kita tidak mengaktifkan --accept-routes
di host.
Solusinya yang paling ideal nya adalah... dengan uninstall tailscale di host tersebut. Tapi jika begitu saya tidak bisa menggunakan fitur Tailscale SSH ke si host, jadi saya biarkan koneksi ke host melalui jaringan rumah melalui DERP, karena jarang banget juga remote connect ke host selain untuk keperluan yang langka (seperti provision vm baru i guess)
Cloudflare Tunnel
5 hari sebelum tulisan ini diterbitkan ada kendala dengan setup Cloudflare Tunnel di jaringan saya dan setelah melakukan trial-error dari tcpdump sampai port mirroring, baru ketemu masalah utama nya: TUNNEL_TRANSPORT_PROTOCOL
.
Konfigurasi ini untuk menentukan protokol yang digunakan untuk membuat koneksi antara cloudflared
alias agent yang berjalan di mesin saya dan jaringan Cloudflare. Tanpa konfigurasi khusus, nilai dari TUNNEL_TRANSPORT_PROTOCOL
adalah auto
alias menggunakan QUIC lalu berpindah menjadi http2
bila koneksi UDP tidak bisa terbuat.
Berbeda dengan HTTP/1.1 ataupun HTTP/2 yang menggunakan TCP, QUIC (yang digunakan di HTTP/3) menggunakan UDP sebagai transport layer nya (meskipun QUIC itu sendiri pun adalah transport layer).
Masalahnya, di lingkungan virtualisasi khususnya di network driver yang menggunakan "virtio" besar kemungkinan akan menggunakan module "vhost-net" yang gampangnya untuk mengurangi overhead virtualisasi dengan memindahkan tugas pemrosesan paket virtio di "userspace" (proses qemu) ke "kernel space" (driver vhost-net). Yang singkatnya, ini akan bermasalah saat berurusan khususnya dengan traffic UDP dari host ke guest, yang long story short, akan membuat banyak packet drop.
Solusinya adalah dengan men-disable module vhost-net di guest, dan expect CPU akan lebih sibuk dari biasanya.
Solusi yang lain adalah (yang saya gunakan) dengan mengatur nilai TUNNEL_TRANSPORT_PROTOCOL
menjadi http2
. Dan pada akhirnya pun di perspektif end users akan menggunakan HTTP/3 (jika komputer mereka mendukung) saat tersambung ke jaringan si Cloudflare.
Dan akses pun sekarang sudah tidak intermiten lagi.
Disable Netfilter di bridge
Ini bagian dari "kernel tuning" ala-ala yang direkomendasikan juga saat menggunakan bridge network di libvirt.
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0
Jika merujuk ke halaman ini issue tersebut dibuat pada tahun 2009. Dan jika tidak yakin dengan nilai net.bridge.bridge-nf-call-iptables
jalankan sysctl -a | grep net.bridge.bridge-nf-call
di host.
Jika nilainya something selain 0
, mungkin bisa dipertimbangkan untuk mengubahnya menjadi 0
.
Penutup
Setup bridged network mungkin cukup ideal untuk setup yang sederhana.
Untuk setup yang lebih kompleks, sepertinya bisa mempertimbangkan menggunakan VLAN, VXLAN, atau apapun solusi lain yang biasanya menggunakan overlay network.
Sebagai penutup, jika ada pertanyaan "bagaimana cara menghubungkan banyak VMs di hosts yang berbeda?" salah satu jawabannya adalah dengan membuat bridged network ("physical device sharing") yang baru saja dibahas disini.
Top comments (0)