Uji HTTP API Tanpa Utilitas Eksternal
Container berbasis hardened atau minimal base image sering kali memangkas utilitas jaringan standar seperti curl, wget, atau netcat demi mereduksi attack surface dan ukuran image. Kondisi ini menyulitkan eksekusi validasi kontrak API, pipeline sanity-check, atau konfigurasi HEALTHCHECK lokal.
Jika image container menyertakan Bash yang dikompilasi dengan flag --enable-net-redirections, manipulasi raw socket TCP dapat dilakukan langsung melalui virtual path /dev/tcp/HOST/PORT. Pendekatan ini mengeksekusi I/O jaringan murni via kernel syscall tanpa dependensi binary tambahan.
1. Inisialisasi Socket via File Descriptor
Bash merepresentasikan koneksi TCP dua arah dengan mengaitkan /dev/tcp ke File Descriptor (FD) tertentu menggunakan command exec.
# Membuka koneksi read-write pada File Descriptor 3
exec 3<>/dev/tcp/api.internal.local/8080
# Menutup File Descriptor setelah selesai
exec 3>&-
exec 3<&-Perintah exec 3<>... menginstruksikan shell untuk memanggil syscall socket() dan connect(). Angka 3 dipilih untuk menghindari konflik dengan standard stream: 0 (stdin), 1 (stdout), dan 2 (stderr).
2. Konstruksi Payload HTTP/1.1 Mentah
Berkomunikasi langsung dengan raw socket mengharuskan kepatuhan mutlak terhadap spesifikasi RFC 7230 (HTTP/1.1). Dua hal kritis yang wajib dipenuhi:
- Line Terminator CRLF: Setiap baris header harus diakhiri dengan
. Perintahechodefault Linux hanya mengirim(LF), yang ditolak oleh HTTP server ketat (seperti Gonet/httpatau Nginx). Gunakanprintf. - Header Connection: close: HTTP/1.1 secara default mengaktifkan persistent connection (Keep-Alive). Tanpa header
Connection: close, server tidak akan mengirim sinyal TCP FIN, menyebabkan loop pembacaan di Bash mengalami blocking/hang selamanya.
Contoh konstruksi request POST JSON dengan autentikasi:
PAYLOAD='{"event":"ping","status":"active"}'
CONTENT_LEN=${#PAYLOAD}
printf "POST /v1/webhook HTTP/1.1\r\n" >&3
printf "Host: api.internal.local:8080\r\n" >&3
printf "Authorization: Bearer sec-token-xyz\r\n" >&3
printf "Content-Type: application/json\r\n" >&3
printf "Content-Length: %d\r\n" "$CONTENT_LEN" >&3
printf "Connection: close\r\n" >&3
printf "\r\n" >&3
printf "%s" "$PAYLOAD" >&33. Parsing Status Code dan Response Body Streaming
Response HTTP terdiri dari Status Line, Response Headers, dan Response Body yang dipisahkan oleh baris kosong tunggal (
). Parsing dilakukan secara linear menggunakan built-in read.
# 1. Baca Status Line
read -r -u 3 PROTOCOL STATUS_CODE STATUS_TEXT
# Hapus carriage return (CR) dari status text jika ada
STATUS_TEXT=$(printf '%s' "$STATUS_TEXT" | tr -d '\r')
# 2. Skip Headers hingga bertemu baris kosong
while IFS= read -r -u 3 line; do
clean_line=$(printf '%s' "$line" | tr -d '\r')
[[ -z "$clean_line" ]] && break
done
# 3. Stream Response Body
RESPONSE_BODY=""
while IFS= read -r -u 3 -n 4096 chunk; do
RESPONSE_BODY+="$chunk"
done4. Mitigasi Network Timeout
Koneksi socket mentah rentan hang jika backend mengalami deadlock atau paket TCP hilang. Bash menyediakan flag timeout bawaan pada builtin read melalui opsi -t <detik>.
Gunakan read -t untuk membatasi durasi tunggu per baris data guna mencegah container healthcheck hang tanpa batas.TIMEOUT_SEC=3
if ! read -t "$TIMEOUT_SEC" -r -u 3 PROTOCOL STATUS_CODE STATUS_TEXT; then
echo "ERR: Connection timed out setelah ${TIMEOUT_SEC}s" >&2
exec 3>&-; exec 3<&-
exit 1
fi5. Skrip Lengkap: Validator Kontrak API & Healthcheck
Skrip berikut mengeksekusi uji kontrak POST webhook internal, memverifikasi HTTP response code, dan mengevaluasi JSON response body.
#!/usr/bin/env bash
set -euo pipefail
TARGET_HOST="127.0.0.1"
TARGET_PORT="8080"
TARGET_PATH="/healthz"
AUTH_TOKEN="secret-audit-key"
PAYLOAD='{"check":"liveness"}'
# Buka TCP socket via FD 3
exec 3<>"/dev/tcp/${TARGET_HOST}/${TARGET_PORT}"
# Kirim HTTP Request
printf "POST %s HTTP/1.1\r\n" "$TARGET_PATH" >&3
printf "Host: %s:%s\r\n" "$TARGET_HOST" "$TARGET_PORT" >&3
printf "Authorization: Bearer %s\r\n" "$AUTH_TOKEN" >&3
printf "Content-Type: application/json\r\n" >&3
printf "Content-Length: %d\r\n" "${#PAYLOAD}" >&3
printf "Connection: close\r\n" >&3
printf "\r\n" >&3
printf "%s" "$PAYLOAD" >&3
# Evaluasi status line dengan timeout 5 detik
if ! read -t 5 -r -u 3 PROTOCOL STATUS_CODE STATUS_MSG; then
echo "FAIL: Request timeout ke ${TARGET_HOST}:${TARGET_PORT}" >&2
exec 3>&-; exec 3<&-
exit 2
fi
# Lewati response header
while IFS= read -t 2 -r -u 3 line; do
clean_line=$(printf '%s' "$line" | tr -d '\r')
[[ -z "$clean_line" ]] && break
done
# Ambil response body
BODY=""
while IFS= read -t 2 -r -u 3 line; do
BODY+="$line"
done
# Tutup socket
exec 3>&-; exec 3<&-
# Verifikasi Kontrak HTTP Status
echo "HTTP Status: $STATUS_CODE"
if [[ "$STATUS_CODE" -ne 200 ]]; then
echo "Contract Violation: Expected 200, got $STATUS_CODE" >&2
echo "Response: $BODY" >&2
exit 1
fi
echo "API Contract Valid: Endpoint merespons 200 OK."Batasan Teknis
Mekanisme /dev/tcp memiliki batasan struktural:
- Tanpa Dukungan TLS/HTTPS: Stream
/dev/tcpberoperasi pada raw Layer 4 TCP. Handshake enkripsi TLS tidak dapat ditangani tanpa bantuan wrapper sepertiopenssl s_client. Gunakan metode ini untuk komunikasi intra-pod, internal mesh, service-to-service tanpa TLS termination, atau direct loopback healthcheck. - Ketergantungan Shell: Fitur ini spesifik Bash (bukan
/bin/shstandar POSIX atauashdefault Alpine) yang dikompilasi dengan net-redirections aktif. Pastikan shell target adalahbash.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!