Trong bài viết trước, chúng ta đã tìm hiểu cách quản lý cloud với CLI tools. Bây giờ, chúng ta sẽ bàn về security — một chủ đề quan trọng nhưng thường bị bỏ qua khi viết Bash script.
Script Bash chạy tự động khắp nơi — từ CI/CD pipeline đến cron jobs trên server. Nhưng nếu không bảo mật, chúng có thể trở thành lỗ hổng lớn: lộ API keys, bị injection attack, hay bị lợi dụng để chiếm quyền kiểm soát hệ thống.
Bài viết này sẽ hướng dẫn bạn cách quản lý secrets, validate input, set permissions đúng cách, và scan script với shellcheck để xây dựng Bash scripts an toàn trong DevOps.
Tại sao cần bảo mật script?
Rủi ro phổ biến
Rủi ro
Mô tả
Hậu quả
Hardcoded secrets
API key, password trong code
Leaked khi share/repo
Command injection
Input không validate chạy thẳng
Server bị chiếm quyền
Insecure permissions
Script world-executable
Ai cũng chạy được
Unquoted variables
$var thay vì "$var"
Unexpected behavior
Best practices overview
Không bao giờ hardcode secrets — dùng env vars hoặc vault
Luôn quote variables — "$var" thay vì $var
Validate input — kiểm tra format trước khi dùng
Set restrictive permissions — chmod 700 cho script
Scan với shellcheck —找出潜在 bugs
Quản lý secrets
Không hardcode secrets
1
2
3
4
5
6
7
8
# BAD — không bao giờ làm thế nàyAPI_KEY="sk-1234567890abcdef"DB_PASSWORD="mysecretpassword"./deploy.sh "$API_KEY"# GOOD — dùng environment variableexportAPI_KEY="${API_KEY:?API_KEY is required}"./deploy.sh "$API_KEY"
# Tạo file .env (không commit vào git)cat > .env << 'EOF'
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
EOF# Bảo vệ filechmod 600 .env
# Load trong scriptif[[ -f ".env"]];thenset -a # Tự động exportsource .env
set +a
elseecho"ERROR: .env file not found" >&2exit1fi# Kiểm tra bắt buộc: "${AWS_ACCESS_KEY_ID:?AWS_ACCESS_KEY_ID is required}": "${DATABASE_URL:?DATABASE_URL is required}"
# Lấy secret từ Vaultget_vault_secret(){localpath="$1"localkey="$2"local secret
secret=$(vault kv get -field="$key""$path" 2>/dev/null)if[[ -z "$secret"]];thenecho"ERROR: Failed to get secret from $path/$key" >&2return1fiecho"$secret"}# Sử dụngDB_PASS=$(get_vault_secret "secret/data/database""password")
# Khi nào cần quote?# Luôn quote khi expand variables để tránh word splitting và globbing# BAD — unquotedfile=$1# Nếu $1 có spaces → breakrm $file# Nguy hiểm!echo$var# Có thể bị glob *# GOOD — quotedfile="$1"rm "$file"echo"$var"# Ví dụ thực tếprocess_file(){localfilepath="$1"# Luôn quote parameter expansionif[[ ! -f "$filepath"]];thenecho"File not found: $filepath" >&2return1fi# Command substitution cũng nên quotelocal line_count
line_count=$(wc -l < "$filepath")echo"File has $line_count lines"}
Khi nào KHÔNG quote?
1
2
3
4
5
6
7
8
9
10
# Khi intentionally wanting word splitting# Ví dụ: iterate qua listfor file in *.log;doecho"Processing: $file"done# Khi dùng [[ ]] (bash-specific, safer than [ ])if[[$var=="pattern"]];thenecho"Matched"fi
# SC2086: Double quote to prevent globbing and word splitting# BADecho$1# GOODecho"$1"# SC2046: Quote this to prevent word splitting# BADcd$(dirname $0)# GOODcd"$(dirname "$0")"# SC2034: Variable appears unused# WARNING — có thể là typo hoặc forgotten to use# SC2155: Declare and assign separately# BADlocaloutput=$(command)# GOODlocal output
output=$(command)
#!/usr/bin/env bash
# ==============================================================================# Script : secure-deploy.sh# Mục đích: Deploy application với security best practices# ==============================================================================set -euo pipefail
# ===== SECURITY CHECKS =====# Kiểm tra script permissionsSCRIPT_PERMS=$(stat -f "%Lp""$0" 2>/dev/null || stat -c "%a""$0" 2>/dev/null)if[["$SCRIPT_PERMS" !="700"&&"$SCRIPT_PERMS" !="755"]];thenecho"WARNING: Script has loose permissions ($SCRIPT_PERMS). Recommended: 700" >&2fi# ===== ENVIRONMENT SETUP =====# Load secrets từ .envif[[ -f ".env"]];thenset -a
source .env
set +a
elseecho"ERROR: .env file not found" >&2exit1fi# Validate required variablesfor var in APP_NAME DEPLOY_ENV SSH_HOST SSH_KEY;do : "${!var:?ERROR: $var is not set in environment or .env}"done# Validate SSH key fileif[[ ! -f "$SSH_KEY"]];thenecho"ERROR: SSH key not found: $SSH_KEY" >&2exit1fiSSH_KEY_PERMS=$(stat -f "%Lp""$SSH_KEY" 2>/dev/null || stat -c "%a""$SSH_KEY" 2>/dev/null)if[["$SSH_KEY_PERMS" !="600"]];thenecho"ERROR: SSH key has insecure permissions ($SSH_KEY_PERMS). Expected: 600" >&2echo"Fix with: chmod 600 $SSH_KEY" >&2exit1fi# ===== INPUT VALIDATION =====DEPLOY_VERSION="${1:-}"if[[ -z "$DEPLOY_VERSION"]];thenecho"Usage: $0 <version>" >&2echo"Example: $0 1.2.3" >&2exit1fi# Validate version format (semver-ish)if[[ ! "$DEPLOY_VERSION"=~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]];thenecho"ERROR: Invalid version format: $DEPLOY_VERSION (expected X.Y.Z)" >&2exit1fi# ===== DEPLOYMENT =====log(){echo"[$(date '+%Y-%m-%d %H:%M:%S')] $1"}log "Starting deployment of $APP_NAME v$DEPLOY_VERSION to $DEPLOY_ENV"# Deploy qua SSHssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "deploy@${SSH_HOST}"\
"cd /opt/$APP_NAME && git pull && git checkout v$DEPLOY_VERSION && systemctl restart $APP_NAME"log "Deployment completed successfully"
Ghi chú triển khai
Secrets management: Trong production, không dùng .env file — dùng dedicated secrets manager (Vault, AWS Secrets Manager, GCP Secret Manager). .env chỉ phù hợp cho development.
Audit logging: Log tất cả thay đổi configuration và secrets access. Đáp ứng compliance requirements (SOC 2, PCI DSS).
Secret rotation: Định kỳ thay đổi secrets. Script nên hỗ trợ reload secrets mà không cần restart.
Minimal privileges: Tạo dedicated service accounts với minimal permissions cho mỗi script. Không dùng root khi không cần thiết.
Code review: Luôn review security-focused khi review Bash scripts. Check cho hardcoded secrets, missing validation, và insecure patterns.
Lời kết
Bash và Security là chủ đề quan trọng trong DevOps. Từ việc quản lý secrets an toàn, validate input, quote variables, đến scan với shellcheck — mỗi bước đều giúp giảm thiểu rủi ro bảo mật.
Một script an toàn không chỉ bảo vệ dữ liệu nhạy cảm mà còn ngăn chặn các cuộc tấn công injection và đảm bảo hệ thống ổn định.
Ở bài tiếp theo, chúng ta sẽ tổng kết series với Best Practice trong Bash: shebang, set -euo pipefail, modular functions, logging, và test với bats.