Struktur dari Bagian 1 masih terkunci di dalam server: folder dokumen, media, dan backup sudah berdiri dengan grup dan mode yang benar, tetapi belum satu pun klien yang bisa melihatnya. Bagian ini membuka pintunya lewat dua protokol sekaligus — Samba untuk klien Windows dan ponsel, NFS untuk klien Linux — dengan satu aturan desain: konfigurasi jaringan tidak boleh bertentangan dengan permission di disk. Karena Samba dan NFS tidak terpasang di lingkungan latihan, kita menulis kedua berkas konfigurasinya persis seperti yang akan ditempatkan di server, membedahnya sebaris-sebaris, lalu memvalidasi isinya dengan skrip pemeriksa berbasis grep dan awk.
Menulis smb.conf #
Seluruh tingkah laku Samba dikendalikan satu berkas: /etc/samba/smb.conf, format INI dengan bagian dalam tanda kurung siku. Anatomi dasarnya sudah dibedah di Chapter Samba
; proyek ini menambahkan tiga hal yang chapter itu belum butuhkan: pembatasan antarmuka jaringan, daftar baca/tulis per grup untuk share yang boleh dilihat tamu, dan mask yang konsisten dengan mode di disk. Berkas berikut ditulis persis seperti yang akan disalin ke /etc/samba/smb.conf — versi lengkapnya tersedia sebagai config/smb.conf pada bagian Berkas Sumber di bawah halaman ini:
mkdir -p config
cat > config/smb.conf <<'SMB'
# smb.conf — file server rumahan: share dokumen, media, dan backup.
#
# Penempatan di server:
# sudo cp smb.conf /etc/samba/smb.conf
# sudo testparm # validasi sintaks + daftar share
# sudo systemctl reload smbd # terapkan tanpa memutus koneksi
#
# Uji cepat dari klien:
# smbclient -L //server-rumah -U budi
[global]
workgroup = RUMAH
server string = Server File Rumah
server role = standalone server
log file = /var/log/samba/log.%m
max log size = 1000
interfaces = 192.0.2.0/24 127.0.0.1
bind interfaces only = yes
[dokumen]
comment = Dokumen kerja dan catatan keluarga
path = /srv/share/dokumen
valid users = @keluarga
read only = no
browseable = yes
create mask = 0660
directory mask = 2770
[media]
comment = Foto dan video keluarga
path = /srv/share/media
valid users = @keluarga @tamu
read only = no
read list = @tamu
write list = @keluarga
browseable = yes
create mask = 0664
directory mask = 2775
[backup]
comment = Arsip backup laptop
path = /srv/share/backup
valid users = budi
read only = no
browseable = no
create mask = 0600
directory mask = 2700
SMB
cat config/smb.confOutput yang Dihasilkan:
# smb.conf — file server rumahan: share dokumen, media, dan backup.
#
# Penempatan di server:
# sudo cp smb.conf /etc/samba/smb.conf
# sudo testparm # validasi sintaks + daftar share
# sudo systemctl reload smbd # terapkan tanpa memutus koneksi
#
# Uji cepat dari klien:
# smbclient -L //server-rumah -U budi
[global]
workgroup = RUMAH
server string = Server File Rumah
server role = standalone server
log file = /var/log/samba/log.%m
max log size = 1000
interfaces = 192.0.2.0/24 127.0.0.1
bind interfaces only = yes
[dokumen]
comment = Dokumen kerja dan catatan keluarga
path = /srv/share/dokumen
valid users = @keluarga
read only = no
browseable = yes
create mask = 0660
directory mask = 2770
[media]
comment = Foto dan video keluarga
path = /srv/share/media
valid users = @keluarga @tamu
read only = no
read list = @tamu
write list = @keluarga
browseable = yes
create mask = 0664
directory mask = 2775
[backup]
comment = Arsip backup laptop
path = /srv/share/backup
valid users = budi
read only = no
browseable = no
create mask = 0600
directory mask = 2700Mari kita bedah directive demi directive yang baru atau berbeda dari chapter dasar:
interfaces = 192.0.2.0/24 127.0.0.1danbind interfaces only = yes: smbd hanya mendengarkan di segmen LAN rumah dan loopback — tidak di interface lain sekalipun ada. Server rumahan yang juga terhubung ke jaringan tamu atau VPN tanpa sengaja ikut membuka share di sana.log file = /var/log/samba/log.%m: satu berkas log per nama klien —%mdiganti nama mesinnya. Berkas-berkas inilah bahan audit di Bagian 3.valid users = @keluarga: tanda@berarti keanggotaan grup. User Samba di luar grup ditolak bahkan sebelum menyentuh permission disk — sharedokumendengan demikian punya dua pintu: daftar user di sini, lalu mode2770di disk.read list = @tamudanwrite list = @keluargapada[media]: kombinasi inilah yang membuat satu share punya dua tingkat akses.read only = nomembuka tulis secara umum,read listmenurunkan tamu ke hanya-baca, danwrite listmemastikan keluarga tetap bisa menulis — permission berlapis yang tidak bisa dinyatakan mode disk saja, karena di disk tamu memang membaca lewat kelas other.browseable = nopada[backup]: share tidak tampil di daftar penelusuran, tetapi tetap bisa dibuka bila namanya diketahui. Share backup memang bukan urusan klien sehari-hari.create mask = 0660dandirectory mask = 2770: permission yang diterapkan Samba pada berkas dan folder baru yang dibuat lewat share — angka ini sengaja sama dengan mode disk dari Bagian 1. Perhatikandirectory mask = 2770: folder baru yang dibuat klien Windows pun tetap membawa bit setgid, sehingga skema grup tidak perlahan rusak oleh klien yang tidak tahu apa-apa soal Linux.
Menyusun /etc/exports #
Klien Linux tidak membutuhkan bahasa Windows — mereka bicara NFS, dan seluruh konfigurasinya muat dalam /etc/exports: satu baris per direktori, diikuti klien beserta opsinya dalam kurung. Rancangan kita membagi ketiga share dengan tingkat kepercayaan yang berbeda — versi lengkapnya tersedia sebagai config/exports pada bagian Berkas Sumber di bawah halaman ini:
mkdir -p config
cat > config/exports <<'NFS'
# /etc/exports — share NFS untuk klien Linux di jaringan rumah.
#
# Penempatan di server:
# sudo cp exports /etc/exports
# sudo exportfs -ra # segarkan seluruh entri
# sudo exportfs -v # tampilkan tabel ekspor aktif
#
# Format: <direktori> <klien>(<opsi>)
/srv/share/dokumen 192.0.2.0/24(rw,sync,no_subtree_check)
/srv/share/media 192.0.2.0/24(ro,sync,no_subtree_check)
/srv/share/backup 192.0.2.10(rw,sync,no_subtree_check)
NFS
cat config/exportsOutput yang Dihasilkan:
# /etc/exports — share NFS untuk klien Linux di jaringan rumah.
#
# Penempatan di server:
# sudo cp exports /etc/exports
# sudo exportfs -ra # segarkan seluruh entri
# sudo exportfs -v # tampilkan tabel ekspor aktif
#
# Format: <direktori> <klien>(<opsi>)
/srv/share/dokumen 192.0.2.0/24(rw,sync,no_subtree_check)
/srv/share/media 192.0.2.0/24(ro,sync,no_subtree_check)
/srv/share/backup 192.0.2.10(rw,sync,no_subtree_check)Bedah tiap keputusan dan opsinya:
/srv/share/dokumen 192.0.2.0/24(rw,...): seluruh subnet boleh memasang share ini secara baca-tulis — tetapi “rw” di sini hanya izin tingkat protokol. Kernel tetap memeriksa permission di disk: klien yang login sebagai user non-anggota grupkeluargaakan menabrak mode2770dan gagal menulis. NFS memercayai uid/gid yang diklaim klien, dan pemeriksaan grup inilah yang menjaga pintunya./srv/share/media ... (ro,...): media diekspor hanya-baca untuk semua klien NFS — cukup untuk streaming dan menyalin foto ke ponsel. Penambahan isi baru terjadi lewat Samba (atau langsung di server), bukan lewat NFS, sehingga koleksi lama tidak bisa berubah oleh salah ketik di klien Linux mana pun./srv/share/backup 192.0.2.10(rw,...): hanya satu klien —192.0.2.10, laptop-budi — yang boleh memasang share backup. Klien lain bahkan tidak ditawari entri ini. laptop-budi-lah yang mendorong arsip backup ke server, kebalikan dari pola biasa; alih-alih server menarik data, klien tepercaya yang mendorong.sync: server menuliskan perubahan ke disk sebelum menjawab sukses ke klien — lebih lambat daripadaasync, tetapi data tidak menguap saat listrik padam mendadak.no_subtree_check: mematikan pemeriksaan “apakah berkas ini masih di bawah direktori yang diekspor” yang lambat dan sering salah kirim peringatan; hampir semua panduan modern menuliskannya.
Membuka Port di Firewall #
Firewall yang baik tetap pelit meski jaringannya hanya rumah: semuanya ditolak kecuali yang kita izinkan, dan izinnya sebatas subnet LAN. Empat port yang dibutuhkan:
| Port | Protokol | Layanan | Diperlukan oleh |
|---|---|---|---|
| 445 | TCP | smbd — SMB modern | semua klien Samba |
| 139 | TCP | smbd — SMB lama atas NetBIOS | klien sangat tua |
| 137–138 | UDP | nmbd — penemuan nama NetBIOS | opsional |
| 2049 | TCP | nfsd — NFSv4 | klien Linux |
Perintah ufw-nya dijalankan sekali di server — perhatikan bahwa sumbernya dibatasi from 192.0.2.0/24, bukan terbuka untuk Anywhere:
sudo ufw allow from 192.0.2.0/24 to any port 445 proto tcp
sudo ufw allow from 192.0.2.0/24 to any port 139 proto tcp
sudo ufw allow from 192.0.2.0/24 to any port 137:138 proto udp
sudo ufw allow from 192.0.2.0/24 to any port 2049 proto tcp
sudo ufw status numberedsudo ufw status numbered menampilkan daftar berurut untuk diperiksa ulang. Port 139 serta 137–138 hanya perlu dibuka bila masih ada klien tua yang menemukan share lewat penelusuran jaringan; klien modern cukup 445. NFSv4 memang dirancang dengan satu port 2049 — salah satu alasan kita memakainya, karena NFSv3 menuntut portmapper 111 dan serangkaian port acak yang menyulitkan firewall, sebagaimana dibandingkan di Chapter NFS
.
Validasi dengan verifikasi-share.sh #
Di server, sudo testparm memvalidasi sintaks smb.conf dan sudo exportfs -v menampilkan tabel ekspor aktif — keduanya sudah dibahas di chapter masing-masing. Namun keduanya baru bisa berjalan setelah paket terpasang di server. Pemeriksaan berikut sengaja lebih awal dan lebih sempit: sebelum berkas sampai ke server, apakah semua directive yang kita rancang benar-benar ada, dan apakah format exports sah? Skrip verifikasi-share.sh — tersedia sebagai deploy/verifikasi-share.sh pada bagian Berkas Sumber — menjawabnya dengan grep untuk directive literal dan awk untuk format; blok berikut menulis ulang kedua berkas konfigurasi dan skripnya agar mandiri, lalu mengujinya pada konfigurasi lengkap dan pada versi yang sengaja dibolongi:
mkdir -p config
cat > config/smb.conf <<'SMB'
# smb.conf — file server rumahan: share dokumen, media, dan backup.
#
# Penempatan di server:
# sudo cp smb.conf /etc/samba/smb.conf
# sudo testparm # validasi sintaks + daftar share
# sudo systemctl reload smbd # terapkan tanpa memutus koneksi
#
# Uji cepat dari klien:
# smbclient -L //server-rumah -U budi
[global]
workgroup = RUMAH
server string = Server File Rumah
server role = standalone server
log file = /var/log/samba/log.%m
max log size = 1000
interfaces = 192.0.2.0/24 127.0.0.1
bind interfaces only = yes
[dokumen]
comment = Dokumen kerja dan catatan keluarga
path = /srv/share/dokumen
valid users = @keluarga
read only = no
browseable = yes
create mask = 0660
directory mask = 2770
[media]
comment = Foto dan video keluarga
path = /srv/share/media
valid users = @keluarga @tamu
read only = no
read list = @tamu
write list = @keluarga
browseable = yes
create mask = 0664
directory mask = 2775
[backup]
comment = Arsip backup laptop
path = /srv/share/backup
valid users = budi
read only = no
browseable = no
create mask = 0600
directory mask = 2700
SMB
cat > config/exports <<'NFS'
# /etc/exports — share NFS untuk klien Linux di jaringan rumah.
#
# Penempatan di server:
# sudo cp exports /etc/exports
# sudo exportfs -ra # segarkan seluruh entri
# sudo exportfs -v # tampilkan tabel ekspor aktif
#
# Format: <direktori> <klien>(<opsi>)
/srv/share/dokumen 192.0.2.0/24(rw,sync,no_subtree_check)
/srv/share/media 192.0.2.0/24(ro,sync,no_subtree_check)
/srv/share/backup 192.0.2.10(rw,sync,no_subtree_check)
NFS
cat > verifikasi-share.sh <<'BASH'
#!/usr/bin/env bash
# verifikasi-share.sh — periksa directive wajib smb.conf dan format exports.
# Pemakaian: bash verifikasi-share.sh <folder-config>
# Kode keluar = jumlah pemeriksaan yang gagal.
set -u
if [ $# -ne 1 ]; then
printf 'pemakaian: verifikasi-share.sh <folder-config>\n' >&2
exit 1
fi
SMB="$1/smb.conf"
EXP="$1/exports"
gagal=0
for f in "$SMB" "$EXP"; do
if [ ! -f "$f" ]; then
printf 'verifikasi-share.sh: berkas tidak ditemukan: %s\n' "$f" >&2
exit 1
fi
done
ok() { printf 'OK %s\n' "$1"; }
hilang() { printf 'HILANG %s\n' "$1"; gagal=$((gagal + 1)); }
# --- smb.conf: section dan directive wajib --------------------------------
for pola in \
'[global]' \
'[dokumen]' \
'[media]' \
'[backup]' \
'path = /srv/share/dokumen' \
'path = /srv/share/media' \
'path = /srv/share/backup' \
'valid users = @keluarga' \
'read only = no' \
'create mask = 0660' \
'directory mask = 2770' \
'browseable = no' \
'bind interfaces only = yes'
do
if grep -qF -- "$pola" "$SMB"; then
ok "smb.conf: $pola"
else
hilang "smb.conf: $pola"
fi
done
# --- exports: format dan opsi keamanan -----------------------------------
# 1. Setiap entri aktif memuat kurung opsi di kolom klien.
n=$(awk '!/^[[:space:]]*#/ && NF && $2 !~ /\(/ { c++ } END { print c + 0 }' "$EXP")
if [ "$n" -eq 0 ]; then
ok 'exports: semua entri memuat opsi klien'
else
hilang 'exports: ada entri tanpa opsi klien'
fi
# 2. Setiap entri memuat sync dan no_subtree_check.
n=$(awk '
!/^[[:space:]]*#/ && NF && ($2 !~ /sync/ || $2 !~ /no_subtree_check/) { c++ }
END { print c + 0 }' "$EXP")
if [ "$n" -eq 0 ]; then
ok 'exports: semua entri memuat sync + no_subtree_check'
else
hilang 'exports: ada entri tanpa sync / no_subtree_check'
fi
# 3. root_squash tidak boleh dimatikan.
if grep -q 'no_root_squash' "$EXP"; then
hilang 'exports: no_root_squash terdeteksi'
else
ok 'exports: root_squash tetap aktif'
fi
# 4. dokumen rw, media ro.
mode_dokumen=$(awk '$1 == "/srv/share/dokumen" {
print ($2 ~ /\(rw/) ? "rw" : "ro" }' "$EXP")
if [ "$mode_dokumen" = "rw" ]; then
ok 'exports: dokumen dibagikan rw'
else
hilang 'exports: dokumen bukan rw'
fi
mode_media=$(awk '$1 == "/srv/share/media" {
print ($2 ~ /\(ro/) ? "ro" : "rw" }' "$EXP")
if [ "$mode_media" = "ro" ]; then
ok 'exports: media dibagikan ro'
else
hilang 'exports: media bukan ro'
fi
# 5. backup hanya untuk satu klien tepercaya.
klien=$(awk '$1 == "/srv/share/backup" {
k = $2; sub(/\(.*/, "", k); print k }' "$EXP")
if [ "$klien" = "192.0.2.10" ]; then
ok 'exports: backup terbatas ke 192.0.2.10'
else
hilang 'exports: klien backup bukan 192.0.2.10'
fi
exit "$gagal"
BASH
echo "== konfigurasi lengkap =="
bash verifikasi-share.sh config; echo "exit=$?"
echo "== konfigurasi rusak =="
mkdir -p rusak
printf '[global]\n[dokumen]\npath = /srv/share/dokumen\nread only = no\n' \
> rusak/smb.conf
printf '/srv/share/dokumen 192.0.2.0/24(rw,async)\n/srv/share/backup 192.0.2.10(rw,sync,no_subtree_check,no_root_squash)\n' \
> rusak/exports
bash verifikasi-share.sh rusak; echo "exit=$?"Output yang Dihasilkan:
== konfigurasi lengkap ==
OK smb.conf: [global]
OK smb.conf: [dokumen]
OK smb.conf: [media]
OK smb.conf: [backup]
OK smb.conf: path = /srv/share/dokumen
OK smb.conf: path = /srv/share/media
OK smb.conf: path = /srv/share/backup
OK smb.conf: valid users = @keluarga
OK smb.conf: read only = no
OK smb.conf: create mask = 0660
OK smb.conf: directory mask = 2770
OK smb.conf: browseable = no
OK smb.conf: bind interfaces only = yes
OK exports: semua entri memuat opsi klien
OK exports: semua entri memuat sync + no_subtree_check
OK exports: root_squash tetap aktif
OK exports: dokumen dibagikan rw
OK exports: media dibagikan ro
OK exports: backup terbatas ke 192.0.2.10
exit=0
== konfigurasi rusak ==
OK smb.conf: [global]
OK smb.conf: [dokumen]
HILANG smb.conf: [media]
HILANG smb.conf: [backup]
OK smb.conf: path = /srv/share/dokumen
HILANG smb.conf: path = /srv/share/media
HILANG smb.conf: path = /srv/share/backup
HILANG smb.conf: valid users = @keluarga
OK smb.conf: read only = no
HILANG smb.conf: create mask = 0660
HILANG smb.conf: directory mask = 2770
HILANG smb.conf: browseable = no
HILANG smb.conf: bind interfaces only = yes
OK exports: semua entri memuat opsi klien
HILANG exports: ada entri tanpa sync / no_subtree_check
HILANG exports: no_root_squash terdeteksi
OK exports: dokumen dibagikan rw
HILANG exports: media bukan ro
OK exports: backup terbatas ke 192.0.2.10
exit=12Empat keputusan di dalamnya:
grep -qF -- "$pola": opsi-Fmencari teks literal, bukan regex, sehingga pola seperti[media]aman dari tafsiran karakter kurung. Daftar pola menjadi data murni — menambah directive wajib berarti menambah satu baris, bukan menyunting logika.- Pola awk
! /^[[:space:]]*#/ && NF: hanya baris aktif yang dihitung;c + 0di blokENDmemastikan hasil berupa angka meski tak ada yang cocok — trik yang sama dengan audit format di Chapter NFS . sub(/\(.*/, "", k): nama klien dipenggal dari opsinya (192.0.2.10(rw,...)menjadi192.0.2.10) sebelum dibandingkan — pembandingan string setelahnya menjadi ketat, bukan sekadar “mengandung”.exit "$gagal": jumlah pelanggaran menjadi kode keluar, sehingga test suite cukup memeriksa angka itu. Konfigurasi rusak di atas tertangkap dua belas kali sekaligus: sembilan directivesmb.confhilang, satu entri tanpasync,no_root_squashmenyala, danmediatidak dieksporro—testparmmungkin masih meloloskan sintaksnya karena formatnya sah, tetapi desain kita jelas rusak. Daftar lengkap parameter ada di smb.conf(5) dan exports(5) ; panduan tingkat server lengkap tersedia di dokumentasi Samba .
Kedua protokol kini terpasang di atas kertas: smb.conf memetakan tiga share dengan user dan mask yang sejajar dengan mode disk, exports membatasi NFS per klien, dan firewall membuka empat port untuk subnet rumah. Yang belum kita kerjakan adalah lapisan penghuninya — user mana masuk grup mana, bagaimana setgid menjaga grup tetap terwarisi, dan bagaimana membuktikan tamu memang ditolak. Itu seluruh isi Bagian 3.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
deploy/buat-struktur.sh
Unduh berkas#!/usr/bin/env bash
# buat-struktur.sh — bangun struktur penyimpanan file server.
#
# Membuat tiga share (dokumen, media, backup), mengatur group dan
# mode permission-nya, lalu memverifikasi hasil akhirnya dengan stat.
#
# Dua mode eksekusi:
# - sebagai root di server : membuat group sungguhan (groupadd bila
# belum ada), folder di bawah TARGET, dan chgrp ke group aslinya.
# - sebagai user biasa : simulasi — folder tetap dibuat, chgrp
# digantikan ke group utama kita, dan rencana group dicatat ke
# TARGET/etc/group sebagai fixture yang bisa dibaca ulang.
#
# Pemakaian:
# sudo bash buat-struktur.sh /srv # di server sungguhan
# bash buat-struktur.sh latihan # latihan lokal tanpa root
#
# Kode keluar:
# 0 sukses 1 argumen tidak valid
# 2 struktur gagal dibuat/tidak sesuai
set -u
TARGET="${1:-latihan}"
# Rencana share: nama:group:mode (mode 4 digit oktal, setgid aktif).
RENCANA="dokumen:keluarga:2770 media:keluarga:2775 backup:backup:2770"
if [ -z "$TARGET" ] || [ "$TARGET" = "/" ]; then
printf 'buat-struktur.sh: target tidak valid: %s\n' "$TARGET" >&2
exit 1
fi
if [ "$TARGET" = "/srv" ] && [ "$(id -u)" -ne 0 ]; then
printf 'buat-struktur.sh: /srv milik root; jalankan dengan sudo\n' >&2
exit 1
fi
mkdir -p "$TARGET" || exit 1
SHARE_DIR="$TARGET/share"
# --- 1. Siapkan group ----------------------------------------------------
if [ "$(id -u)" -eq 0 ]; then
printf 'mode : server (root)\n'
for grup in keluarga tamu backup; do
if ! getent group "$grup" > /dev/null; then
groupadd "$grup" || exit 2
fi
done
else
printf 'mode : simulasi tanpa root\n'
mkdir -p "$TARGET/etc" || exit 2
cat > "$TARGET/etc/group" <<'GRUP'
keluarga:x:1100:budi,siti
tamu:x:1101:tamu1
backup:x:1102:budi
GRUP
printf 'group : keluarga, tamu, backup dicatat di %s/etc/group\n' "$TARGET"
fi
# --- 2. Buat folder share beserta permission-nya -------------------------
printf 'struktur: %s/{dokumen,media,backup}\n' "$SHARE_DIR"
gagal=0
for entri in $RENCANA; do
nama=${entri%%:*}
sisa=${entri#*:}
grup=${sisa%%:*}
mode=${sisa##*:}
dir="$SHARE_DIR/$nama"
mkdir -p "$dir" || { gagal=$((gagal + 1)); continue; }
if [ "$(id -u)" -eq 0 ]; then
chgrp "$grup" "$dir" || { gagal=$((gagal + 1)); continue; }
else
# Tanpa root kita hanya boleh memindahkan group ke group sendiri;
# di server, baris ini menjadi: chgrp "$grup" "$dir"
chgrp "$(id -gn)" "$dir" || { gagal=$((gagal + 1)); continue; }
fi
chmod "$mode" "$dir"
# --- 3. Verifikasi hasil dengan stat -------------------------------
nyata=$(stat -c '%a' "$dir")
if [ "$nyata" != "$mode" ]; then
printf 'GAGAL %s mode %s, harapan %s\n' "$dir" "$nyata" "$mode"
gagal=$((gagal + 1))
else
printf 'OK %s %s\n' "$mode" "$dir"
fi
done
if [ "$gagal" -gt 0 ]; then
printf 'GAGAL: %d pemeriksaan tidak sesuai\n' "$gagal"
exit 2
fi
printf 'selesai tanpa error\n'
deploy/verifikasi-share.sh
Unduh berkas#!/usr/bin/env bash
# verifikasi-share.sh — periksa directive wajib smb.conf dan format exports.
#
# Pengganti testparm/exportfs di lingkungan tanpa Samba dan NFS: menerima
# folder yang memuat smb.conf dan exports, lalu memeriksa directive
# keamanan yang kita rancang satu per satu dengan grep dan awk.
#
# Pemakaian:
# bash verifikasi-share.sh <folder-config>
#
# Kode keluar = jumlah pemeriksaan yang gagal.
set -u
if [ $# -ne 1 ]; then
printf 'pemakaian: verifikasi-share.sh <folder-config>\n' >&2
exit 1
fi
SMB="$1/smb.conf"
EXP="$1/exports"
gagal=0
for f in "$SMB" "$EXP"; do
if [ ! -f "$f" ]; then
printf 'verifikasi-share.sh: berkas tidak ditemukan: %s\n' "$f" >&2
exit 1
fi
done
ok() { printf 'OK %s\n' "$1"; }
hilang() { printf 'HILANG %s\n' "$1"; gagal=$((gagal + 1)); }
# --- smb.conf: section dan directive wajib --------------------------------
for pola in \
'[global]' \
'[dokumen]' \
'[media]' \
'[backup]' \
'path = /srv/share/dokumen' \
'path = /srv/share/media' \
'path = /srv/share/backup' \
'valid users = @keluarga' \
'read only = no' \
'create mask = 0660' \
'directory mask = 2770' \
'browseable = no' \
'bind interfaces only = yes'
do
if grep -qF -- "$pola" "$SMB"; then
ok "smb.conf: $pola"
else
hilang "smb.conf: $pola"
fi
done
# --- exports: format dan opsi keamanan -----------------------------------
# 1. Setiap entri aktif memuat kurung opsi di kolom klien.
n=$(awk '!/^[[:space:]]*#/ && NF && $2 !~ /\(/ { c++ } END { print c + 0 }' "$EXP")
if [ "$n" -eq 0 ]; then
ok 'exports: semua entri memuat opsi klien'
else
hilang 'exports: ada entri tanpa opsi klien'
fi
# 2. Setiap entri memuat sync dan no_subtree_check.
n=$(awk '
!/^[[:space:]]*#/ && NF && ($2 !~ /sync/ || $2 !~ /no_subtree_check/) { c++ }
END { print c + 0 }' "$EXP")
if [ "$n" -eq 0 ]; then
ok 'exports: semua entri memuat sync + no_subtree_check'
else
hilang 'exports: ada entri tanpa sync / no_subtree_check'
fi
# 3. root_squash tidak boleh dimatikan.
if grep -q 'no_root_squash' "$EXP"; then
hilang 'exports: no_root_squash terdeteksi'
else
ok 'exports: root_squash tetap aktif'
fi
# 4. dokumen rw, media ro.
mode_dokumen=$(awk '$1 == "/srv/share/dokumen" {
print ($2 ~ /\(rw/) ? "rw" : "ro" }' "$EXP")
if [ "$mode_dokumen" = "rw" ]; then
ok 'exports: dokumen dibagikan rw'
else
hilang 'exports: dokumen bukan rw'
fi
mode_media=$(awk '$1 == "/srv/share/media" {
print ($2 ~ /\(ro/) ? "ro" : "rw" }' "$EXP")
if [ "$mode_media" = "ro" ]; then
ok 'exports: media dibagikan ro'
else
hilang 'exports: media bukan ro'
fi
# 5. backup hanya untuk satu klien tepercaya.
klien=$(awk '$1 == "/srv/share/backup" {
k = $2; sub(/\(.*/, "", k); print k }' "$EXP")
if [ "$klien" = "192.0.2.10" ]; then
ok 'exports: backup terbatas ke 192.0.2.10'
else
hilang 'exports: klien backup bukan 192.0.2.10'
fi
exit "$gagal"
tests/run-tests.sh
Unduh berkas#!/usr/bin/env bash
# run-tests.sh — test suite untuk proyek server file lokal.
# Suite menyiapkan folder kerja terisolasi (mktemp), lalu menguji:
# struktur share hasil deploy/buat-struktur.sh (folder, mode, fixture
# group), directive wajib pada config/smb.conf dan config/exports
# lewat deploy/verifikasi-share.sh, entri config/fstab-share, pewarisan
# setgid pada folder share, serta penolakan tulis pada file read-only.
# Jalankan dari mana saja: bash tests/run-tests.sh
set -u
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
PROJ_DIR=$(dirname "$SCRIPT_DIR")
PASS=0
FAIL=0
WORK=""
# assert_eq <harapan> <nyata> <pesan>: bandingkan dua nilai.
assert_eq() {
if [ "$1" = "$2" ]; then
PASS=$((PASS + 1))
printf 'OK %s\n' "$3"
else
FAIL=$((FAIL + 1))
printf 'GAGAL %s: harapan %s, nyata %s\n' "$3" "$1" "$2"
fi
}
bersihkan() {
if [ -n "$WORK" ]; then
rm -rf "$WORK"
fi
}
trap bersihkan EXIT
WORK=$(mktemp -d)
cd "$WORK" || exit 1
# --- uji 1: buat-struktur.sh membangun struktur yang sesuai ---------------
bash "$PROJ_DIR/deploy/buat-struktur.sh" latihan > /dev/null 2>&1
assert_eq 0 "$?" "buat-struktur.sh keluar 0"
for nama in dokumen media backup; do
[ -d "latihan/share/$nama" ]
assert_eq 0 "$?" "share $nama dibuat"
done
assert_eq 2770 "$(stat -c '%a' latihan/share/dokumen)" \
"mode dokumen 2770"
assert_eq 2775 "$(stat -c '%a' latihan/share/media)" \
"mode media 2775"
assert_eq 2770 "$(stat -c '%a' latihan/share/backup)" \
"mode backup 2770"
# --- uji 2: fixture group tercatat, skrip idempoten ------------------------
grep -q '^keluarga:x:1100:budi,siti$' latihan/etc/group
assert_eq 0 "$?" "fixture group memuat keluarga:budi,siti"
grep -q '^tamu:x:1101:tamu1$' latihan/etc/group
assert_eq 0 "$?" "fixture group memuat tamu"
bash "$PROJ_DIR/deploy/buat-struktur.sh" latihan > /dev/null 2>&1
assert_eq 0 "$?" "buat-struktur.sh idempoten (dijalankan dua kali)"
# --- uji 3: konfigurasi bawaan lolos verifikasi-share.sh -------------------
bash "$PROJ_DIR/deploy/verifikasi-share.sh" "$PROJ_DIR/config" \
> /dev/null 2>&1
assert_eq 0 "$?" "config/ lolos verifikasi-share.sh"
# --- uji 4: konfigurasi rusak tertangkap -----------------------------------
mkdir -p rusak
printf '[global]\n[dokumen]\npath = /srv/share/dokumen\nread only = no\n' \
> rusak/smb.conf
printf '/srv/share/dokumen 192.0.2.0/24(rw,async)\n' > rusak/exports
bash "$PROJ_DIR/deploy/verifikasi-share.sh" rusak > /dev/null 2>&1
if [ "$?" -gt 0 ]; then
PASS=$((PASS + 1))
printf 'OK konfigurasi rusak tertangkap (exit bukan 0)\n'
else
FAIL=$((FAIL + 1))
printf 'GAGAL konfigurasi rusak tertangkap: verifikasi-share.sh keluar 0\n'
fi
# --- uji 5: entri fstab-share memuat nofail dan enam field -----------------
grep -q 'nofail' "$PROJ_DIR/config/fstab-share"
assert_eq 0 "$?" "fstab-share memuat nofail"
jumlah=$(awk '!/^[[:space:]]*#/ && NF { print NF }' \
"$PROJ_DIR/config/fstab-share" | sort -u)
assert_eq 6 "$jumlah" "entri fstab-share berisi enam field"
mnt=$(awk '$2 == "/srv/share" { print $2 }' "$PROJ_DIR/config/fstab-share")
assert_eq "/srv/share" "$mnt" "mountpoint fstab-share /srv/share"
# --- uji 6: setgid diwarisi oleh isi baru folder share ---------------------
mkdir -p latihan/share/dokumen/laporan
touch latihan/share/dokumen/laporan/catatan.txt
assert_eq "$(stat -c '%G' latihan/share/dokumen)" \
"$(stat -c '%G' latihan/share/dokumen/laporan/catatan.txt)" \
"file baru mewarisi group folder share"
mode_sub=$(stat -c '%a' latihan/share/dokumen/laporan)
if [ "$(( 8#$mode_sub & 8#2000 ))" -ne 0 ]; then
PASS=$((PASS + 1))
printf 'OK folder baru mewarisi bit setgid (%s)\n' "$mode_sub"
else
FAIL=$((FAIL + 1))
printf 'GAGAL folder baru mewarisi bit setgid: mode %s tanpa bit 2000\n' \
"$mode_sub"
fi
# --- uji 7: tulis ditolak pada file read-only ------------------------------
printf 'isi awal\n' > latihan/kunci.txt
chmod 444 latihan/kunci.txt
if (echo 'tambahan' >> latihan/kunci.txt) 2> /dev/null; then
FAIL=$((FAIL + 1))
printf 'GAGAL tulis ke file ro ditolak: append justru berhasil\n'
else
PASS=$((PASS + 1))
printf 'OK tulis ke file ro ditolak\n'
fi
assert_eq 'isi awal' "$(cat latihan/kunci.txt)" "isi file ro tidak berubah"
chmod 644 latihan/kunci.txt
echo 'tambahan' >> latihan/kunci.txt
assert_eq 0 "$?" "tulis diizinkan kembali setelah mode rw"
# --- ringkasan ----------------------------------------------------------------
if [ "$FAIL" -eq 0 ]; then
printf 'PASSED: %d/%d test lulus\n' "$PASS" "$PASS"
else
printf 'FAILED: %d/%d test lulus\n' "$PASS" "$((PASS + FAIL))"
fi
[ "$FAIL" -eq 0 ]