Series MapLesson 03 / 58
Focus mode active/Press Alt+Shift+R to toggle/Esc to exit
Start HereOrdered learning track

Filesystem and Permissions

Path, file, directory, symlink, permission bit, ownership, mount, temporary directory, disk usage, dan filesystem debugging untuk Java/JAX-RS backend engineer.

14 min read2664 words
PrevNext
Lesson 0358 lesson track01–11 Start Here
#linux#filesystem#permissions#debugging+3 more

Part 003 — Filesystem and Permissions

Filesystem dan permission adalah lapisan dasar yang sering terlihat sederhana, tetapi banyak production issue bermula dari sini: Permission denied, file config tidak ditemukan, log tidak bisa ditulis, volume Kubernetes tidak termount, temporary directory penuh, symlink mengarah ke lokasi lama, file ownership berubah setelah Docker image rebuild, atau aplikasi berjalan sebagai non-root tetapi image masih mengasumsikan root.

Untuk senior backend engineer, filesystem bukan sekadar cd, ls, dan chmod. Filesystem adalah contract antara aplikasi, runtime Linux, container image, orchestrator, deployment pipeline, dan operational debugging. Java/JAX-RS service mungkin terlihat berjalan sebagai aplikasi HTTP biasa, tetapi pada runtime ia tetap membaca config, membuka file descriptor, menulis log, memuat truststore, membuat temporary file, membaca certificate, mengakses mounted secret, dan bergantung pada permission model Linux.

Tujuan part ini adalah membuat filesystem dan permission menjadi bagian dari mental model debugging dan PR review, bukan hanya command hafalan.


1. Core Mental Model

Filesystem Linux adalah namespace berbentuk tree yang dimulai dari root path /.

Semua resource yang terlihat oleh process dipetakan ke dalam tree ini: file biasa, directory, symlink, device file, socket file, mount point, procfs, config, secret, volume, dan kadang object virtual seperti /proc, /sys, atau /dev.

Untuk backend engineer, pertanyaan paling penting bukan hanya “file ini ada atau tidak”, tetapi:

  • process melihat path yang mana?
  • user mana yang menjalankan process?
  • permission apa yang dimiliki user tersebut?
  • apakah path itu file, directory, symlink, atau mount point?
  • apakah path itu ada di host, container image, atau volume runtime?
  • apakah file dibuat saat build image, saat container start, atau saat aplikasi running?
  • apakah file bersifat immutable, ephemeral, shared, atau persistent?
  • apakah content-nya sama antara local, CI, staging, dan production?

Dalam konteks Java/JAX-RS service, filesystem biasanya terlibat dalam:

  • membaca application config;
  • membaca TLS truststore/keystore;
  • membaca mounted secret;
  • membaca static resource;
  • menulis log jika tidak full stdout-based;
  • membuat temporary file untuk upload/download/report;
  • membaca generated resource;
  • memuat migration file;
  • menulis cache lokal sementara;
  • membaca artifact JAR/WAR;
  • menjalankan shell script startup;
  • menyimpan dump atau diagnostic artifact.

Dalam Docker/Kubernetes, filesystem makin kompleks karena ada beberapa layer:

  1. host filesystem;
  2. image layer;
  3. container writable layer;
  4. bind mount atau volume;
  5. Kubernetes ConfigMap/Secret mount;
  6. projected volume;
  7. ephemeral volume;
  8. persistent volume;
  9. service account token mount;
  10. read-only root filesystem jika diaktifkan.

Jika engineer tidak membedakan layer-layer ini, debugging biasanya berubah menjadi trial-and-error.


2. Path: Absolute vs Relative

Absolute path dimulai dari /.

Contoh:

/etc/hosts
/app/config/application.yaml
/var/log/my-service/app.log
/tmp/report.csv

Relative path dihitung dari current working directory process.

Contoh:

./config/application.yaml
../secrets/token.txt
logs/app.log

Masalah umum pada Java service adalah asumsi relative path.

Contoh bug:

Path path = Paths.get("config/application.yaml");

Kode ini terlihat aman, tetapi hasilnya bergantung pada working directory process. Di local mungkin project root. Di Docker mungkin /app. Di CI mungkin workspace path. Di Kubernetes bisa bergantung pada WORKDIR Dockerfile atau command startup.

Untuk debugging:

pwd
ls -la
readlink -f config/application.yaml

Untuk process yang sudah berjalan:

pwdx <pid>
ls -l /proc/<pid>/cwd

Di container:

docker exec -it <container> pwd
docker exec -it <container> ls -la

Di Kubernetes:

kubectl exec -n <namespace> <pod> -- pwd
kubectl exec -n <namespace> <pod> -- ls -la /app

Senior rule

Relative path dalam runtime service harus dicurigai kecuali jelas dikontrol oleh startup script, Dockerfile WORKDIR, atau framework config.

Untuk production-ready service, path penting sebaiknya:

  • dikonfigurasi eksplisit;
  • divalidasi saat startup;
  • dicatat dalam log startup tanpa membocorkan secret;
  • tidak bergantung pada IDE working directory;
  • punya error message yang menyebut path resolved final.

File biasa

File biasa menyimpan bytes. Linux tidak peduli apakah file itu .yaml, .json, .log, .jar, atau .pem. Interpretasi dilakukan oleh aplikasi.

Command:

file application.yaml
stat application.yaml
ls -l application.yaml

Directory

Directory adalah mapping nama ke inode. Permission pada directory berbeda maknanya dari permission pada file.

Untuk directory:

  • read berarti bisa list nama file;
  • write berarti bisa membuat/menghapus/rename entry;
  • execute berarti bisa traverse masuk ke directory.

Ini penting. File bisa readable, tetapi tetap tidak bisa dibaca jika salah satu parent directory tidak executable oleh user process.

Debug:

namei -l /app/config/application.yaml

namei -l sangat berguna untuk melihat permission setiap komponen path.

Symlink adalah pointer berbasis path ke target lain.

Contoh:

ln -s /etc/my-service/application.yaml current-config.yaml
ls -l current-config.yaml
readlink current-config.yaml
readlink -f current-config.yaml

Failure mode symlink:

  • target tidak ada;
  • target berubah saat deployment;
  • relative symlink berbeda arti di runtime directory berbeda;
  • symlink menunjuk ke path host yang tidak ada di container;
  • permission target tidak cocok;
  • symlink loop;
  • security issue jika path bisa dikontrol user.

Hard link adalah nama lain untuk inode yang sama.

ln original.txt another-name.txt
ls -li original.txt another-name.txt

Hard link jarang digunakan dalam aplikasi backend modern, tetapi penting untuk memahami bahwa nama file bukan identitas storage sebenarnya. Identitas storage adalah inode.


4. Inode and Metadata Awareness

Setiap file/directory memiliki metadata: inode, owner, group, permission, size, timestamp, link count, device, dan block allocation.

Command:

stat file.txt
ls -li file.txt

Dalam debugging, metadata sering lebih penting dari isi file.

Contoh kasus:

  • file ada, tetapi owner salah;
  • file ada, tetapi permission tidak readable;
  • file size 0 karena generation step gagal;
  • timestamp menunjukkan file lama tidak ter-update;
  • symlink menunjuk ke inode berbeda dari yang diasumsikan;
  • volume mount menutupi file dari image layer.

Volume mount shadowing

Dalam container, volume mount bisa “menutupi” file yang sudah ada di image.

Misalnya Docker image punya:

/app/config/application.yaml

Tetapi Kubernetes mount ConfigMap ke:

/app/config

Isi /app/config dari image bisa tidak terlihat lagi karena tertutup mount. Ini sering menjadi sumber kebingungan.

Debug:

mount | grep /app/config
ls -la /app/config
stat /app/config/application.yaml

5. Permission Bits

Output ls -l:

-rw-r--r-- 1 app app 1234 Jul 10 10:00 application.yaml

Bagian permission:

-rw-r--r--

Karakter pertama:

  • - file biasa;
  • d directory;
  • l symlink;
  • c character device;
  • b block device;
  • s socket;
  • p named pipe.

Tiga triplet berikutnya:

owner group others
rw-   r--   r--

Makna:

  • r read;
  • w write;
  • x execute/traverse;
  • - tidak ada permission.

Numeric mode:

r = 4
w = 2
x = 1

Contoh:

chmod 644 application.yaml
chmod 755 scripts/start.sh
chmod 600 secret.pem

Interpretasi:

  • 644: owner read/write, group read, others read;
  • 755: owner read/write/execute, group read/execute, others read/execute;
  • 600: hanya owner read/write;
  • 700: hanya owner full access.

Senior warning

chmod 777 hampir selalu code smell.

Ia mungkin “memperbaiki” error sementara, tetapi membuka write/execute access ke semua user. Dalam container single-process mungkin terlihat aman, tetapi di shared runner, host, atau volume shared, ini bisa menjadi security incident.


6. Ownership: User and Group

Setiap file memiliki owner user dan group.

Command:

ls -l
id
whoami
groups
stat file.txt

Mengubah ownership:

chown app:app application.yaml
chown -R app:app /app/data

Masalah umum di container:

  • file dibuat saat image build oleh root;
  • container runtime menjalankan process sebagai user non-root;
  • aplikasi mencoba menulis ke directory milik root;
  • volume mount punya UID/GID yang tidak sama dengan user container;
  • CI menghasilkan artifact dengan ownership berbeda;
  • Kubernetes securityContext mengubah UID runtime.

Contoh Dockerfile problem:

FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/service.jar /app/service.jar
RUN mkdir /app/logs
USER 10001
CMD ["java", "-jar", "/app/service.jar"]

Directory /app/logs dibuat oleh root. Process berjalan sebagai UID 10001. Jika aplikasi menulis ke /app/logs, bisa gagal.

Perbaikan:

RUN mkdir -p /app/logs && chown -R 10001:10001 /app
USER 10001

Atau lebih baik: log ke stdout/stderr untuk containerized service.


7. umask

umask menentukan permission default saat file/directory dibuat.

Cek:

umask

Jika umask 022:

  • file default 644;
  • directory default 755.

Jika umask 077:

  • file default 600;
  • directory default 700.

Failure mode:

  • file generated oleh script terlalu terbuka;
  • file generated oleh CI tidak readable oleh later step;
  • secret file tidak cukup ketat;
  • directory output tidak writable oleh process berikutnya.

Dalam automation script, hindari mengandalkan umask default jika file security-sensitive.

Contoh:

umask 077
printf '%s' "$TOKEN" > token.txt
chmod 600 token.txt

8. Sticky Bit, setuid, and setgid Awareness

Sticky bit

Sticky bit biasa terlihat di /tmp:

ls -ld /tmp
# drwxrwxrwt

t berarti user hanya bisa menghapus file miliknya sendiri dalam directory tersebut walaupun directory world-writable.

Ini penting untuk temporary directory shared.

setuid

setuid membuat executable berjalan dengan effective user owner file. Ini sensitif dan jarang perlu disentuh oleh backend application engineer.

setgid

Pada directory, setgid membuat file baru mewarisi group directory tersebut. Kadang digunakan untuk shared team directory.

Senior rule: jika menemukan setuid/setgid dalam service repo, Docker image, atau operational script, review security-nya secara serius.


9. Temporary Directory

Aplikasi Java sering memakai temporary directory lewat:

System.getProperty("java.io.tmpdir")

Biasanya /tmp, tetapi bisa berbeda.

Cek dari process:

java -XshowSettings:properties -version 2>&1 | grep 'java.io.tmpdir'

Failure mode:

  • /tmp penuh;
  • /tmp tidak writable;
  • container root filesystem read-only;
  • temporary file tidak dibersihkan;
  • large upload/report membuat disk penuh;
  • multiple pod menulis ke local temp dan kehilangan data saat restart;
  • security issue karena temporary file predictable name.

Debug:

df -h /tmp
du -sh /tmp/* 2>/dev/null | sort -h | tail
ls -ld /tmp

Untuk Java service yang memproses file upload atau generated reports, temp directory harus masuk design review.

Pertanyaan review:

  • Berapa ukuran maksimum file sementara?
  • Siapa yang membersihkan?
  • Apa yang terjadi jika disk penuh?
  • Apakah temp file mengandung PII/secret?
  • Apakah temp file butuh encryption atau immediate deletion?
  • Apakah container punya writable filesystem?

10. Disk Usage and Disk Full

Command utama:

df -h
 df -i
 du -sh /path
 du -ah /path | sort -h | tail -50

df -h menunjukkan space filesystem.

df -i menunjukkan inode usage. Disk bisa tidak penuh secara bytes, tetapi inode habis karena terlalu banyak file kecil.

Common failure:

No space left on device

Penyebab bisa:

  • log tidak rotate;
  • temp file menumpuk;
  • Maven local repo membesar di CI runner;
  • Docker image/cache menumpuk;
  • test menghasilkan file besar;
  • heap dump/thread dump/core dump banyak;
  • queue/spool lokal tidak dibersihkan;
  • inode exhaustion karena banyak file kecil.

Debug cepat:

df -h
df -i
sudo du -xhd1 / | sort -h

Di container, hati-hati: df menunjukkan filesystem container atau mount tertentu, bukan selalu host secara langsung.

Di Kubernetes:

kubectl exec -n <namespace> <pod> -- df -h
kubectl exec -n <namespace> <pod> -- du -sh /tmp /app 2>/dev/null

11. Mounts and Volumes

Mount adalah mekanisme memasang filesystem lain ke path tertentu.

Cek mount:

mount
findmnt
findmnt /app/config

Dalam container/Kubernetes, mount sering berasal dari:

  • ConfigMap;
  • Secret;
  • projected volume;
  • emptyDir;
  • persistent volume;
  • hostPath;
  • service account token;
  • CSI driver;
  • certificate bundle;
  • cloud secret provider.

Failure mode:

  • mount path salah;
  • file dari image tertutup mount;
  • Secret/ConfigMap belum update;
  • permission mount tidak sesuai user process;
  • volume read-only padahal aplikasi mencoba write;
  • persistent volume penuh;
  • subPath mount tidak update sesuai ekspektasi;
  • container restart melihat data ephemeral hilang.

Kubernetes debugging:

kubectl describe pod -n <namespace> <pod>
kubectl get pod -n <namespace> <pod> -o yaml
kubectl exec -n <namespace> <pod> -- mount
kubectl exec -n <namespace> <pod> -- ls -la /mounted/path

Senior rule: jangan menganggap file di container berasal dari image. Verifikasi apakah path itu mount.


12. Read-only Root Filesystem

Security-hardened container sering menggunakan read-only root filesystem.

Jika aktif, aplikasi hanya bisa menulis ke path yang dimount writable, misalnya /tmp atau /app/data jika disediakan.

Failure mode:

Read-only file system

Design implication:

  • log harus ke stdout/stderr;
  • temp directory harus explicit writable volume;
  • aplikasi tidak boleh menulis ke working directory sembarangan;
  • generated cache harus diarahkan ke writable path;
  • third-party library yang menulis file harus dipahami.

Kubernetes check:

kubectl get pod -n <namespace> <pod> -o yaml | grep -A5 readOnlyRootFilesystem

13. Filesystem and Java/JAX-RS Backend

Java service biasanya berinteraksi dengan filesystem dalam beberapa pola.

Config loading

Framework dapat memuat config dari classpath, file path, env var, atau external config server.

Risk:

  • file path beda antara local dan container;
  • config mounted read-only;
  • config tidak reload;
  • secret ikut tercetak di log;
  • fallback config default tidak sengaja aktif.

TLS keystore/truststore

JVM option umum:

-Djavax.net.ssl.trustStore=/app/certs/truststore.jks
-Djavax.net.ssl.trustStorePassword=...

Risk:

  • file tidak ada;
  • permission tidak readable;
  • password salah;
  • truststore versi lama;
  • path di env berbeda dari actual mount;
  • certificate rotation tidak terlihat oleh running JVM.

File upload/download

Risk:

  • upload besar memenuhi /tmp;
  • filename user menyebabkan path traversal;
  • file tidak dibersihkan;
  • permission terlalu terbuka;
  • PII tertinggal di disk;
  • concurrent request menulis file yang sama.

Migration/resource loading

Risk:

  • migration file tidak ikut package;
  • classpath vs filesystem path tertukar;
  • generated resource tidak committed/terbuild;
  • resource filtering Maven mengubah content.

14. Filesystem Security Concerns

Path traversal

Jangan pernah menggabungkan input user langsung ke path filesystem tanpa validasi.

Contoh berbahaya:

Path file = Paths.get("/app/files/" + userInput);

Jika userInput adalah ../../etc/passwd, hasilnya bisa keluar dari intended directory.

Pattern lebih aman:

Path base = Paths.get("/app/files").toAbsolutePath().normalize();
Path requested = base.resolve(userInput).normalize();
if (!requested.startsWith(base)) {
    throw new SecurityException("Invalid path");
}

Secret file

Secret file harus:

  • permission ketat;
  • tidak committed ke Git;
  • tidak tercetak di log;
  • tidak masuk artifact;
  • tidak tersalin ke evidence bundle tanpa redaction;
  • tidak tersimpan di temp directory tanpa cleanup.

World-writable directory

World-writable directory harus dicurigai, terutama jika script mengeksekusi file dari directory tersebut.


15. Reproducibility Concerns

Filesystem bisa membuat build atau runtime tidak reproducible jika:

  • script bergantung pada current directory;
  • file generated tidak deterministik;
  • timestamp file masuk ke artifact;
  • path absolute mesin developer masuk ke output;
  • line ending beda OS;
  • permission executable tidak tersimpan benar;
  • local-only config terbaca diam-diam;
  • symlink tidak portable;
  • mount path berbeda local/CI/prod.

Review question:

Jika repo fresh clone di mesin baru atau CI runner baru, apakah command yang sama menghasilkan file dan behavior yang sama?


16. Debugging Playbook: File Not Found

Symptom:

java.nio.file.NoSuchFileException
FileNotFoundException
No such file or directory

Langkah:

  1. Ambil path persis dari error.
  2. Tentukan apakah path absolute atau relative.
  3. Cek current working directory process.
  4. Cek apakah file ada di runtime environment, bukan hanya di local repo.
  5. Cek symlink target.
  6. Cek mount yang menutupi path.
  7. Cek packaging Maven/Docker apakah file masuk artifact/image.
  8. Cek config env yang menentukan path.

Command:

pwd
ls -la <path>
readlink -f <path>
namei -l <path>
stat <path>
mount | grep <parent-path>

Untuk Kubernetes:

kubectl exec -n <namespace> <pod> -- pwd
kubectl exec -n <namespace> <pod> -- ls -la <path>
kubectl describe pod -n <namespace> <pod>

17. Debugging Playbook: Permission Denied

Symptom:

Permission denied
AccessDeniedException
EACCES

Langkah:

  1. Cek user runtime.
  2. Cek owner/group file.
  3. Cek permission file.
  4. Cek permission semua parent directory.
  5. Cek apakah filesystem read-only.
  6. Cek mount option.
  7. Cek container securityContext.
  8. Cek SELinux/AppArmor jika relevan.

Command:

whoami
id
ls -l <file>
namei -l <file>
stat <file>
mount | grep <path>

Di Kubernetes:

kubectl exec -n <namespace> <pod> -- id
kubectl get pod -n <namespace> <pod> -o yaml | grep -A20 securityContext

Do not immediately do:

chmod -R 777 <path>

Itu bukan debugging. Itu menghapus evidence dan bisa membuat security issue.


18. Debugging Playbook: Disk Full

Symptom:

No space left on device
Disk quota exceeded

Langkah:

  1. Cek filesystem usage.
  2. Cek inode usage.
  3. Identifikasi directory terbesar.
  4. Bedakan log/temp/cache/artifact/data.
  5. Jangan hapus file production tanpa memahami owner dan lifecycle.
  6. Capture evidence sebelum cleanup.
  7. Pastikan cleanup tidak menghapus file yang sedang dipakai process.

Command:

df -h
df -i
du -xhd1 /path | sort -h
lsof | grep deleted

lsof | grep deleted berguna ketika file sudah dihapus tetapi masih dipegang process, sehingga space belum kembali sampai file descriptor ditutup.


19. PR Review Checklist for Filesystem Changes

Gunakan checklist ini saat PR menyentuh Dockerfile, script, config path, file upload, local setup, logging, Kubernetes volume, Maven resource, atau operational tooling.

Correctness

  • Apakah path absolute/relative jelas?
  • Apakah working directory dikontrol?
  • Apakah file benar-benar tersedia di runtime artifact/container?
  • Apakah error message menyebut path final yang gagal?
  • Apakah symlink/mount behavior dipahami?

Security

  • Apakah secret file tidak terbuka permission-nya?
  • Apakah ada risiko path traversal?
  • Apakah script menghindari chmod 777?
  • Apakah file sensitive tidak masuk log/evidence?
  • Apakah container berjalan sebagai non-root jika policy mewajibkan?

Reproducibility

  • Apakah path bekerja di local, CI, dan container?
  • Apakah generated file deterministic?
  • Apakah file permission executable tersimpan benar di Git?
  • Apakah line ending/symlink portable?
  • Apakah mount tidak menutupi file penting tanpa disadari?

Production readiness

  • Apa yang terjadi jika file tidak ada?
  • Apa yang terjadi jika disk penuh?
  • Apa yang terjadi jika directory tidak writable?
  • Apakah aplikasi tetap bisa start dengan config missing?
  • Apakah failure fail-fast atau silent fallback?

Observability

  • Apakah startup log cukup menunjukkan config path?
  • Apakah error log cukup spesifik?
  • Apakah correlation/evidence bisa dikumpulkan tanpa secret leakage?
  • Apakah temporary file cleanup terlihat/terukur?

20. Internal Verification Checklist

Karena detail internal CSG/team tidak boleh diasumsikan, verifikasi hal berikut di repo, pipeline, runbook, atau diskusi dengan senior/platform/SRE/security:

  • Struktur direktori service Java/JAX-RS.
  • Path config, secret, certificate, truststore, keystore.
  • Apakah service menulis file lokal atau full stdout-based.
  • Apakah aplikasi memakai /tmp untuk upload/report/cache.
  • Dockerfile WORKDIR, USER, COPY, RUN chown, dan permission executable script.
  • Kubernetes volume mount, ConfigMap, Secret, projected volume, PVC, emptyDir.
  • Apakah root filesystem read-only.
  • securityContext: runAsUser, runAsGroup, fsGroup, readOnlyRootFilesystem.
  • Policy non-root container.
  • Runbook untuk disk full, permission denied, missing config, certificate file issue.
  • Log redaction policy untuk file path dan secret.
  • CI behavior terkait generated files dan file permissions.
  • Cross-platform notes untuk macOS/Windows/WSL jika ada.

21. Practical Command Reference

# Identify current user and groups
whoami
id
groups

# Inspect files and directories
pwd
ls -la
ls -li
stat <path>
file <path>
namei -l <path>

# Resolve symlink
readlink <path>
readlink -f <path>

# Permissions and ownership
chmod 644 <file>
chmod 755 <script>
chmod 600 <secret>
chown app:app <path>
chown -R app:app <directory>

# Disk usage
df -h
df -i
du -sh <path>
du -xhd1 <path> | sort -h

# Mounts
mount
findmnt
findmnt <path>

# Process working directory
pwdx <pid>
ls -l /proc/<pid>/cwd

# Kubernetes file inspection
kubectl exec -n <namespace> <pod> -- pwd
kubectl exec -n <namespace> <pod> -- ls -la /app
kubectl exec -n <namespace> <pod> -- id
kubectl describe pod -n <namespace> <pod>

22. Senior Engineer Summary

Filesystem issue jarang berdiri sendiri. Ia biasanya muncul sebagai kombinasi dari:

  • path assumption;
  • user/permission mismatch;
  • container image layering;
  • Kubernetes volume mount;
  • CI/local difference;
  • secret/config injection;
  • disk/temp lifecycle;
  • unsafe script;
  • missing observability.

Senior engineer harus mampu melihat error Permission denied atau File not found sebagai entry point ke runtime model yang lebih luas: siapa process-nya, path apa yang terlihat olehnya, filesystem layer mana yang sedang dibaca, permission apa yang berlaku, dan apakah behavior itu reproducible di environment lain.

Filesystem discipline adalah bagian dari production readiness.

Lesson Recap

You just completed lesson 03 in start here. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.