Skip to content

Instantly share code, notes, and snippets.

@win3zz
Created October 16, 2025 19:32
Show Gist options
  • Save win3zz/7fe828be189c5bb5bd8d06c9207e511d to your computer and use it in GitHub Desktop.
Save win3zz/7fe828be189c5bb5bd8d06c9207e511d to your computer and use it in GitHub Desktop.
Container-Optimized OS (COS) guest audit script
#!/usr/bin/env bash
# cos_audit.sh
# Container-Optimized OS (COS) guest audit script (read-only)
# Produces a PASS / FAIL / INFO style report for many guest-side hardening checks.
#
# Usage:
# sudo ./cos_audit.sh | tee cos_audit_$(date +%F_%T).log
#
# Author: Generated by ChatGPT for Bipin Jitiya (auditor)
set -u
SCRIPT_NAME="$(basename "$0")"
DATE="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
# Helpers
info() { printf "INFO: %s\n" "$*"; }
ok() { printf "PASS: %s\n" "$*"; }
warn() { printf "FAIL: %s\n" "$*"; }
note() { printf "------ %s ------\n" "$*"; }
sep() { printf "\n"; }
run_cmd() {
# run command; print a short snippet of output (first N lines)
local cmd="$*"
if ! command -v bash >/dev/null 2>&1; then
# fallback - shouldn't happen on COS
echo "COMMAND-UNAVAILABLE: $cmd"
return 1
fi
eval "$cmd" 2>/dev/null | head -n 20
}
check_file_contains() {
# $1 file, $2 pattern
local file="$1" pattern="$2"
if [ -r "$file" ]; then
if grep -qi -- "$pattern" "$file"; then
return 0
else
return 2
fi
else
return 1
fi
}
print_header() {
cat <<EOF
COS GUEST AUDIT REPORT
Host: $(hostname -f 2>/dev/null || hostname)
Date: $DATE
Script: $SCRIPT_NAME
Scope: Guest (root shell)
Note: Read-only checks. Capture output to keep evidence.
EOF
sep
}
# Start report
print_header
################################################################
# Section: Basic system identity
################################################################
note "System Identity"
echo " /etc/os-release:"
run_cmd "cat /etc/os-release || true"
echo
echo " uname -a:"
run_cmd "uname -a || true"
echo
echo " /proc/cmdline:"
run_cmd "cat /proc/cmdline || true"
sep
################################################################
# Section: Boot & Integrity Chain
################################################################
note "Boot & Integrity Chain Checks"
# dm-verity evidence (dmesg & cmdline & lsblk)
if dmesg | grep -i -E 'dm-verity|verity' >/dev/null 2>&1; then
ok "dm-verity messages present in dmesg (verity/verity hash evidence)"
echo " dmesg verity lines:"
dmesg | grep -i -E 'dm-verity|verity' | head -n 10
else
warn "No dm-verity messages in dmesg"
fi
sep
# root backing device and read-only root
ROOT_MOUNT_LINE="$(mount | grep ' on / ' || true)"
if echo "$ROOT_MOUNT_LINE" | grep -q ' ro,'; then
ok "Root filesystem mounted read-only"
echo " mount line: $ROOT_MOUNT_LINE"
else
warn "Root filesystem NOT mounted read-only (expected ro)"
echo " mount line: $ROOT_MOUNT_LINE"
fi
sep
# check /proc/cmdline for dm-verity, module.sig_enforce, firmware_class.path, loadpin
CMDLINE="$(cat /proc/cmdline 2>/dev/null || true)"
if [ -n "$CMDLINE" ]; then
echo " /proc/cmdline: $CMDLINE"
echo
# module.sig_enforce
if echo "$CMDLINE" | grep -q 'module.sig_enforce=1'; then
ok "module.sig_enforce=1 present (unsigned kernel modules blocked)"
else
warn "module.sig_enforce not set to 1 (kernel modules may be allowed unsigned)"
fi
# loadpin
if echo "$CMDLINE" | grep -q 'modules-load=loadpin_trigger\|loadpin'; then
ok "loadpin is configured in cmdline (kernel code load restriction)"
else
# also check sysfs
if [ -r /sys/kernel/security/loadpin/enabled ] && grep -q '^1$' /sys/kernel/security/loadpin/enabled; then
ok "loadpin enabled in sysfs"
else
warn "loadpin not enabled"
fi
fi
# firmware_class.path
if echo "$CMDLINE" | grep -q 'firmware_class.path='; then
ok "firmware_class.path restricted in kernel cmdline"
else
warn "firmware_class.path not restricted via kernel cmdline"
fi
# dm-verity present check in cmdline
if echo "$CMDLINE" | grep -q 'verity\|dm-mod.create'; then
ok "dm-verity appears in kernel cmdline"
else
warn "dm-verity not found in kernel cmdline (but dmesg earlier may show verity)"
fi
else
warn "Could not read /proc/cmdline"
fi
sep
# Lockdown
if [ -r /sys/kernel/security/lockdown ]; then
LOCKDOWN="$(cat /sys/kernel/security/lockdown 2>/dev/null || true)"
if [ -n "$LOCKDOWN" ]; then
ok "Kernel lockdown: $LOCKDOWN"
else
warn "Kernel lockdown present but empty"
fi
else
warn "/sys/kernel/security/lockdown not present"
fi
sep
# kexec disabled
if [ -r /proc/sys/kernel/kexec_load_disabled ]; then
KEXEC="$(cat /proc/sys/kernel/kexec_load_disabled 2>/dev/null || true)"
if [ "$KEXEC" = "1" ]; then
ok "kexec_load_disabled = 1 (kexec disabled)"
else
warn "kexec_load_disabled != 1 (kexec may be allowed)"
fi
else
warn "/proc/sys/kernel/kexec_load_disabled not present"
fi
sep
################################################################
# Section: Kernel & runtime hardening
################################################################
note "Kernel & Runtime Hardening"
# AppArmor
if [ -r /sys/module/apparmor/parameters/enabled ]; then
AA="$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null || true)"
if echo "$AA" | grep -qi 'Y'; then
ok "AppArmor enabled"
else
warn "AppArmor not enabled"
fi
else
info "AppArmor sysfs path not present (aa-status may still not be installed)"
if command -v aa-status >/dev/null 2>&1; then
aa-status --enabled 2>/dev/null && ok "AppArmor reports enabled" || warn "AppArmor reports disabled"
fi
fi
sep
# Seccomp: check /proc/1/status Seccomp
if grep -q '^Seccomp:' /proc/1/status 2>/dev/null; then
SC="$(awk '/^Seccomp:/ {print $2}' /proc/1/status 2>/dev/null || true)"
if [ "$SC" = "2" ]; then
ok "PID 1 seccomp status = 2 (strict seccomp)"
else
warn "PID 1 seccomp status = $SC (expected 2 for enforced seccomp)"
fi
else
info "No Seccomp info for PID 1"
fi
sep
# unprivileged_bpf_disabled
if [ -r /proc/sys/kernel/unprivileged_bpf_disabled ]; then
UBPF="$(cat /proc/sys/kernel/unprivileged_bpf_disabled 2>/dev/null || true)"
if [ "$UBPF" = "1" ]; then
ok "unprivileged_bpf_disabled = 1 (unprivileged eBPF disabled)"
else
warn "unprivileged_bpf_disabled != 1"
fi
else
info "/proc/sys/kernel/unprivileged_bpf_disabled not present"
fi
sep
# ASLR
if [ -r /proc/sys/kernel/randomize_va_space ]; then
ASLR="$(cat /proc/sys/kernel/randomize_va_space 2>/dev/null || true)"
if [ "$ASLR" = "2" ]; then
ok "ASLR randomize_va_space = 2 (full ASLR)"
else
warn "ASLR randomize_va_space = $ASLR (expected 2)"
fi
else
warn "/proc/sys/kernel/randomize_va_space not readable"
fi
sep
# CPU vulnerabilities
if [ -d /sys/devices/system/cpu/vulnerabilities ]; then
ok "CPU vulnerability files present; listing (first 20):"
for f in /sys/devices/system/cpu/vulnerabilities/*; do
printf " %s: " "$(basename "$f")"
awk '{print substr($0,1,200)}' "$f" 2>/dev/null | sed -n '1p'
done | sed -n '1,20p'
else
info "No CPU vulnerability directory at /sys/devices/system/cpu/vulnerabilities"
fi
sep
# unprivileged_userns_clone
if [ -r /proc/sys/kernel/unprivileged_userns_clone ]; then
UUSERNS="$(cat /proc/sys/kernel/unprivileged_userns_clone 2>/dev/null || true)"
if [ "$UUSERNS" = "0" ]; then
ok "unprivileged_userns_clone = 0 (userns disabled)"
else
warn "unprivileged_userns_clone = $UUSERNS (recommended 0 to mitigate escapes)"
fi
else
info "/proc/sys/kernel/unprivileged_userns_clone not present"
fi
sep
################################################################
# Section: Filesystem & storage controls
################################################################
note "Filesystem & Storage Checks"
# Find writable in /usr /bin /sbin
WIE="$(find /usr /bin /sbin -xdev -type f -writable 2>/dev/null | head -n 5 || true)"
if [ -z "$WIE" ]; then
ok "No writable files found under /usr, /bin, /sbin (first 5 checked)"
else
warn "Writable files found under system paths (first 5 shown):"
echo "$WIE"
fi
sep
# /tmp mount flags
TMPLINE="$(mount | grep ' on /tmp ' || true)"
if [ -n "$TMPLINE" ]; then
if echo "$TMPLINE" | grep -qE 'noexec|nodev|nosuid'; then
ok "/tmp mounted with recommended flags: $TMPLINE"
else
warn "/tmp mount lacks recommended flags (noexec,nodev,nosuid): $TMPLINE"
fi
else
info "/tmp not separately mounted (check if tmpfs or not present)"
fi
sep
# /var/log
VLOG="$(mount | grep ' on /var/log ' || true)"
if [ -n "$VLOG" ]; then
ok "/var/log mount line: $VLOG"
else
info "/var/log not separately mounted; writable as expected for logging"
fi
sep
# overlay mounts (containers)
if mount | grep -i overlay >/dev/null 2>&1; then
ok "Overlay mounts present (container runtime overlays detected). Example lines:"
mount | grep -i overlay | head -n 5
else
info "No overlay mounts detected"
fi
sep
################################################################
# Section: Identity, privilege & access
################################################################
note "Identity & Access Checks"
# SSH presence and sshd_config (may be absent on COS)
if command -v ss >/dev/null 2>&1; then
echo " Open listening sockets (ss -tulpn):"
ss -tulpn | sed -n '1,40p'
else
info "ss not present; skipping listening socket enumeration"
fi
sep
if [ -r /etc/ssh/sshd_config ]; then
echo " /etc/ssh/sshd_config (selected relevant lines):"
grep -Ei 'PermitRootLogin|PasswordAuthentication|ChallengeResponseAuthentication|PubkeyAuthentication' /etc/ssh/sshd_config || true
if grep -qEi 'PermitRootLogin\s+yes' /etc/ssh/sshd_config; then
warn "PermitRootLogin is set to yes in sshd_config"
else
ok "SSH root login not enabled in sshd_config (or config absent/unreadable)"
fi
else
info "No /etc/ssh/sshd_config found or unreadable (COS often doesn't run sshd)"
fi
sep
# Sudoers
if [ -r /etc/sudoers ] || [ -d /etc/sudoers.d ]; then
echo " /etc/sudoers and /etc/sudoers.d (sanity list):"
( [ -r /etc/sudoers ] && sed -n '1,80p' /etc/sudoers ) || true
ls -l /etc/sudoers.d 2>/dev/null || true
else
info "No sudoers present or not readable"
fi
sep
# Root .ssh
if [ -d /root/.ssh ]; then
echo " root authorized_keys:"
ls -l /root/.ssh || true
[ -r /root/.ssh/authorized_keys ] && sed -n '1,20p' /root/.ssh/authorized_keys || true
else
info "/root/.ssh not present"
fi
sep
################################################################
# Section: Container runtime & image controls
################################################################
note "Container runtime (containerd) Checks"
# Check for containerd binary / version
if command -v containerd >/dev/null 2>&1; then
CV="$(containerd --version 2>/dev/null | head -n1 || true)"
ok "containerd present: $CV"
else
# try ctr
if command -v ctr >/dev/null 2>&1; then
CV="$(ctr version 2>/dev/null | head -n1 || true)"
ok "containerd 'ctr' present: $CV"
else
warn "containerd/ctr not found"
fi
fi
sep
# list images (if ctr available)
if command -v ctr >/dev/null 2>&1; then
echo " ctr images ls (first 20):"
ctr images ls | sed -n '1,20p' || true
else
info "ctr not available; cannot list container images locally"
fi
sep
# containerd journal logs
if command -v journalctl >/dev/null 2>&1; then
echo " containerd journalctl (last 200 lines):"
journalctl -u containerd --no-pager -n 200 2>/dev/null | sed -n '1,200p' || true
else
info "journalctl not available"
fi
sep
# seccomp profile existence for containerd config
if [ -r /etc/containerd/config.toml ]; then
echo " /etc/containerd/config.toml (snippet):"
sed -n '1,160p' /etc/containerd/config.toml || true
else
info "/etc/containerd/config.toml not present/readable"
fi
sep
################################################################
# Section: Logging & auditing
################################################################
note "Logging & Auditing Checks"
# auditd presence
if systemctl list-unit-files | grep -q '^auditd' 2>/dev/null; then
if systemctl is-enabled auditd >/dev/null 2>&1; then
ok "auditd service present and enabled"
else
warn "auditd present but not enabled"
fi
else
info "auditd service not present (COS often relies on journald and cloud logging)"
fi
sep
# journalctl - recent kernel errors
if command -v journalctl >/dev/null 2>&1; then
echo " Recent kernel log (dmesg tail):"
dmesg | tail -n 40
else
info "journalctl/dmesg may be unavailable"
fi
sep
################################################################
# Section: Virtualization & device isolation
################################################################
note "Virtualization & Device Isolation Checks"
# hypervisor detection
if dmesg | grep -i -E 'Hypervisor detected|hypervisor|kvm-clock|kvm' >/dev/null 2>&1; then
echo " dmesg hypervisor lines:"
dmesg | grep -i -E 'Hypervisor detected|kvm-clock|kvm|hypervisor' | head -n 20
ok "Hypervisor-related messages found"
else
info "No obvious hypervisor messages in dmesg (unexpected for a guest)"
fi
sep
# /dev/kvm
if [ -e /dev/kvm ]; then
warn "/dev/kvm exists inside guest (nested virtualization or misconfiguration); device details:"
ls -l /dev/kvm || true
else
ok "/dev/kvm absent (normal for guest without nested virtualization)"
fi
sep
# lspci virtio devices
if command -v lspci >/dev/null 2>&1; then
if lspci | grep -Ei 'virtio|virtual|vmware|qemu|Red Hat, Inc.' >/dev/null 2>&1; then
ok "Paravirtualized devices reported by lspci (virtio/Red Hat virtio)"
lspci | grep -Ei 'virtio|virtual|vmware|qemu|Red Hat, Inc.' | sed -n '1,20p'
else
info "No virtio/vm vendor strings in lspci output"
fi
else
info "lspci not available"
fi
sep
# /proc/interrupts virtio lines
if grep -E 'virtio|xen|vmw|hv_' /proc/interrupts >/dev/null 2>&1; then
ok "VirtIO interrupts present (paravirtualized devices present)"
grep -E 'virtio|xen|vmw|hv_' /proc/interrupts | sed -n '1,40p'
else
info "No virtio/xen/vmw/hv_ interrupts present"
fi
sep
# check for passthrough devices (PCI with vendor that is not virtio)
if command -v lspci >/dev/null 2>&1; then
echo " Full lspci (first 40 lines):"
lspci | sed -n '1,40p'
else
info "lspci not present"
fi
sep
################################################################
# Section: Optional & host isolation
################################################################
note "Optional: EFI vars, efivarfs, and host mounts"
# efivars
if mount | grep -q efivarfs; then
ok "efivarfs mounted"
mount | grep efivarfs
else
info "efivarfs not mounted"
fi
sep
# /proc/xen and /sys/hypervisor
[ -d /proc/xen ] && warn "/proc/xen exists (xen guest?)" || ok "/proc/xen absent"
if [ -d /sys/hypervisor ]; then
ok "/sys/hypervisor exists (virtualization hypervisor info available)"
else
info "/sys/hypervisor absent"
fi
sep
################################################################
# Section: Final summary hint lines
################################################################
note "Summary Hints (quick reading)"
echo " - If many PASS entries in Boot & Integrity and Kernel Lockdown sections => guest is strongly protected against host-impacting tampering."
echo " - If /dev/kvm present, or module.sig_enforce not set, or root is writable => escalate findings (host-risk)."
echo " - Collect logs produced by this script as evidence and attach relevant dmesg/journal snippets."
sep
echo "Audit completed. Save the log file for reporting."
# End
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment