編輯設定檔
#vi /etc/sysctl.conf
在檔案最後加下以下三行
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
重啟服務, 不必重開機
#sysctl -p
驗證是否成功 (確認輸出為 1)
#cat /proc/sys/net/ipv6/conf/all/disable_ipv6
半畝方塘一鑑開,天光雲影共徘徊;問渠那得清幾許,為有源頭活水來。
編輯設定檔
#vi /etc/sysctl.conf
在檔案最後加下以下三行
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
重啟服務, 不必重開機
#sysctl -p
驗證是否成功 (確認輸出為 1)
#cat /proc/sys/net/ipv6/conf/all/disable_ipv6
要在menu.sh 裡增加功能 1. scp功能, 但要讓user自己設定引數與參數 2. ssh 到特定主機, 但要user自己設定登入的帳號名稱
可以,但千萬不要直接讓使用者在 menu.sh 裡「自由輸入整串 scp/ssh 參數」然後丟進 shell 執行(會被用 ;、$( )、-oProxyCommand 等方式繞出去)。正確做法是:
下面給你可直接套用的 SOP。
sudo mkdir -p /home/optim/transfer
sudo chown optim:optim /home/optim/transfer
sudo chmod 700 /home/optim/transfer
/usr/local/bin/optim_scpsudo vi /usr/local/bin/optim_scp
貼上(把 ALLOWED_HOSTS 改成你允許的目的主機):
#!/bin/bash
set -euo pipefail
ALLOWED_HOSTS=("10.0.0.10" "server1.internal" "server2.internal")
BASE_DIR="/home/optim/transfer"
is_allowed_host() {
local h="$1"
for x in "${ALLOWED_HOSTS[@]}"; do
[[ "$h" == "$x" ]] && return 0
done
return 1
}
valid_user() {
[[ "$1" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]]
}
# 僅允許相對路徑(避免 /etc/passwd 之類)
valid_rel_path() {
local p="$1"
[[ "$p" != /* ]] && [[ "$p" != *".."* ]] && [[ "$p" =~ ^[A-Za-z0-9._/-]+$ ]]
}
echo "=== SCP (受控) ==="
echo "僅允許檔案位於: $BASE_DIR"
echo "允許主機: ${ALLOWED_HOSTS[*]}"
echo
read -r -p "方向 (1=上傳到遠端, 2=從遠端下載): " mode
read -r -p "遠端主機: " host
read -r -p "遠端登入帳號: " ruser
if ! is_allowed_host "$host"; then
echo "ERR: 主機不在白名單"
exit 1
fi
if ! valid_user "$ruser"; then
echo "ERR: 帳號格式不合法"
exit 1
fi
# 固定 SSH 選項:禁用危險玩法
SSH_OPTS=(
-o "BatchMode=no"
-o "PermitLocalCommand=no"
-o "ClearAllForwardings=yes"
-o "ProxyCommand=none"
-o "RequestTTY=no"
)
case "$mode" in
1)
# 上傳:BASE_DIR/相對路徑 -> 遠端:目的路徑
read -r -p "本機檔案(相對於 transfer/): " local_rel
read -r -p "遠端目的路徑(例如 /tmp 或 /home/user/transfer ): " remote_path
if ! valid_rel_path "$local_rel"; then
echo "ERR: 本機路徑必須是相對路徑且不可含 .."
exit 1
fi
if [[ "$remote_path" != /* ]] || [[ "$remote_path" == *".."* ]]; then
echo "ERR: 遠端目的路徑必須是絕對路徑且不可含 .."
exit 1
fi
src="$BASE_DIR/$local_rel"
if [[ ! -f "$src" ]]; then
echo "ERR: 找不到檔案: $src"
exit 1
fi
/usr/bin/scp "${SSH_OPTS[@]}" -- "$src" "${ruser}@${host}:${remote_path%/}/"
echo "OK: 上傳完成"
;;
2)
# 下載:遠端:檔案 -> BASE_DIR/目的檔名
read -r -p "遠端檔案完整路徑(例如 /tmp/a.log): " remote_file
read -r -p "本機目的檔名(放在 transfer/ 下): " local_name
if [[ "$remote_file" != /* ]] || [[ "$remote_file" == *".."* ]]; then
echo "ERR: 遠端檔案路徑必須是絕對路徑且不可含 .."
exit 1
fi
if ! [[ "$local_name" =~ ^[A-Za-z0-9._-]+$ ]]; then
echo "ERR: 本機目的檔名不合法"
exit 1
fi
dst="$BASE_DIR/$local_name"
/usr/bin/scp "${SSH_OPTS[@]}" -- "${ruser}@${host}:${remote_file}" "$dst"
/bin/chown optim:optim "$dst"
/bin/chmod 600 "$dst"
echo "OK: 下載完成 -> $dst"
;;
*)
echo "ERR: 模式錯誤"
exit 1
;;
esac
sudo chown root:root /usr/local/bin/optim_scp
sudo chmod 755 /usr/local/bin/optim_scp
/usr/local/bin/optim_sshsudo vi /usr/local/bin/optim_ssh
貼上(把 TARGET_HOST 改成你指定的那台):
#!/bin/bash
set -euo pipefail
TARGET_HOST="10.0.0.10"
valid_user() {
[[ "$1" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]]
}
echo "=== SSH (受控) ==="
echo "目的主機固定: $TARGET_HOST"
read -r -p "請輸入登入帳號: " ruser
if ! valid_user "$ruser"; then
echo "ERR: 帳號格式不合法"
exit 1
fi
exec /usr/bin/ssh \
-o "ClearAllForwardings=yes" \
-o "PermitLocalCommand=no" \
-o "ProxyCommand=none" \
-o "ServerAliveInterval=30" \
-o "ServerAliveCountMax=3" \
-l "$ruser" \
"$TARGET_HOST"
sudo chown root:root /usr/local/bin/optim_ssh
sudo chmod 755 /usr/local/bin/optim_ssh
menu.sh在你原本的 case 裡新增選項,例如:
echo "5) SCP 檔案傳輸 (受控)"
echo "6) SSH 到指定主機 (帳號自填)"
並加入:
5)
/usr/local/bin/optim_scp
;;
6)
/usr/local/bin/optim_ssh
;;
你現在用的是 sshd_config 的 ForceCommand /opt/optim-menu/menu.sh,所以:
scp optim@你的主機:... 直接拉檔:通常會失敗(因為 scp 需要走 ssh 的 scp 子系統/命令,而你強制進選單)如果你也需要「外部 scp 到這台主機」那要改成用 Match 區分 Subsystem sftp 或針對 sftp/scp 做不同規則(但那是另一套設計)。
如果你告訴我:
/home/*/upload)我可以把 wrapper 再加上「遠端路徑白名單」與「大小/副檔名限制」避免被拿去搬敏感檔。
信心指數:92%
以下是一套在 Ubuntu 24.04 中,限制使用者 optim 以「密碼登入 SSH 後,只能透過選單執行指定指令」的完整 SOP。
此作法適合維運帳號、代管帳號、委外人員,避免任意下指令。
cdvi / nanobashssh 到別台Ctrl+C 跳出sudo useradd -m optim
sudo passwd optim
sudo mkdir -p /opt/optim-menu
sudo chown root:root /opt/optim-menu
sudo chmod 755 /opt/optim-menu
sudo vi /opt/optim-menu/menu.sh
內容如下(可依需求調整指令):
#!/bin/bash
while true; do
clear
echo "================================="
echo " Optim Maintenance Menu"
echo "================================="
echo "1) 查看系統時間"
echo "2) 查看磁碟使用量"
echo "3) 查看服務狀態 (nginx)"
echo "4) 查看系統負載"
echo "0) 登出"
echo "================================="
read -p "請選擇操作: " choice
case "$choice" in
1)
/usr/bin/date
;;
2)
/usr/bin/df -h
;;
3)
/usr/bin/systemctl status nginx --no-pager
;;
4)
/usr/bin/uptime
;;
0)
exit 0
;;
*)
echo "無效選項"
;;
esac
echo
read -p "按 Enter 繼續..."
done
sudo chown root:root /opt/optim-menu/menu.sh
sudo chmod 755 /opt/optim-menu/menu.sh
⚠️ 使用絕對路徑(/usr/bin/df),避免 PATH 被濫用。
sudo vi /etc/ssh/sshd_config
在檔案最下方加入:
Match User optim
ForceCommand /opt/optim-menu/menu.sh
PasswordAuthentication yes
AllowTcpForwarding no
X11Forwarding no
PermitTTY yes
sudo systemctl restart ssh
sudo chown root:root /home/optim/.bashrc /home/optim/.profile
sudo chmod 644 /home/optim/.bashrc /home/optim/.profile
或直接清空:
sudo truncate -s 0 /home/optim/.bashrc
sudo truncate -s 0 /home/optim/.profile
sudo visudo
確認 沒有:
optim ALL=(ALL) ALL
請實際測試以下行為:
| 測試項目 | 預期結果 |
|---|---|
| SSH 密碼登入 | ✅ 可登入 |
| 登入後看到 bash | ❌ 不可 |
| 出現選單 | ✅ |
嘗試輸入 bash | ❌ 無效 |
| Ctrl + C | ❌ 不可中斷 |
| 選 0 | ✅ 正常登出 |
| scp 傳檔 | ❌ 失敗 |
👉 解法:
chmod +x /opt/optim-menu/menu.sh
👉 一律改成:
/usr/bin/xxx
此 SOP 能達成:
✅ 密碼登入
✅ 僅能選單操作
✅ 指令白名單
✅ 無 Shell、無逃逸
✅ 符合實務維運安全
若你需要:
可直接說明使用情境,我可幫你客製化。
信心指數:96%
apt update && apt upgrade -y
apt install openssl ca-certificates
mkdir -p ~/certwork
chmod 700 ~/certwork
cd ~/certwork
certwork#vi prod.abc.com.csr
再將CSR金鑰內容貼入存檔
-----BEGIN CERTIFICATE REQUEST-----
MIIC9TCCAd0CAQAwczELMAkGA1UEBhMCREUxHDAaBgNVBAoTE1NBUCBUcnVzdCBD
b21tdW5pdHkxEzARBgNVBAsTClNBUCBXZWIgQVMxFDASBgNVBAsTC0kwMDIwMjQw
AQUAA4IBDwAwggEKAoIBAQCmnIOUc+3Ht9r736NZH6J/PCUiGCUBtqwo3ggoz+Dd
QdaqnoJrEwxAvzKhWMfgEQ5m2X1MxhrMj1fGoCK9TZejLbGZycOscR5qyxUOmfBS
XeeL7pWZKn+NG1W5XE9vDlS6IdcDMRZSd7PT+sM1UG+3VvV2dI7iLgqyp+ROt5YD
jtbumGbbwtoxlQ7LEmBaHoirfCKJnETH8kjxSUCOp14JkZo/PO2MqVn2rO8WEOm1
Yn+SZEfrvFjXPJiTPqfXymRhsyWA1EpqRjWQ/DtRP+bC5l3kV32V2pd0K6xWunAA
BVXpi5tr0iWNZiDVh93+YTIMeERr/H267C0WIipZmJixAgMBAAGgPTA7BgkqhkiG
9w0BCQ4xLjAsMCoGA1UdEQQjMCGCEnBzE2Rldi5wcm9tYXRlLmNvbYILcHJvbWF0
ZS5jb20wDQYJKoZIhvcNAQEFBQADggEBAAyDOGsH3zl/ld87iJyfHr9/yms5FjLf
HW8Fqvy2kel8liLkxCq07UMotkXSrt+vhUVLyJKa93yYjlnz3i9j/HJ5lIQUd+AE
7UNF7mNObFL154gxV2udiWc0y7DnILBvoErNosJYK0oh1w2MmVWlFbKcifV8uXAI
g6fenm0i47JLMj+YSJkJsqstXX3XyOcM/FXFekkJifrtcGf2dO6Z+YR8Fd0IaXVc
tKh+aCQ3co3dQrXCvENVS851G8GrcgyVOBo7L2pinWLQrVggivCQ7GdjbFbszSFZ
UPf7jkaAfsC4bVyJMtzbfiDm/2roV8FLDQR/G+fHmIblK3hCYzLiyWk=
-----END CERTIFICATE REQUEST-----
certwork#ll
prod.abc.com.csr
certwork#openssl req -in ~/ca/prod.abc.com.csr -noout -text
openssl genrsa -out rootCA.key 4096
openssl genrsa -out rootCA.key 2048openssl genrsa -out rootCA.key 1024 (此次成功)
三個擇一執行(看是要用什麼等級)
openssl req -x509 -new -nodes -key rootCA.key -sha-1 -days 7300 \
-subj "/C=TW/O=PSC/CN=PSC Root CA" \
-addext "basicConstraints=critical,CA:true,pathlen:1" \
-addext "keyUsage=critical,keyCertSign,cRLSign" \
-out rootCA.crt
export DOMAIN=”prod.abc.com”
export BASE=”abc.com”
export DAYS=3650 # 10年,可改短一點以符合內規
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = prod.abc.com
DNS.2 = abc.com
openssl x509 -req -in pscdev.csr \
-CA rootCA.crt -CAkey rootCA.key -CAcreateserial \
-out prod.abc.com.crt -days 3650 -sha-1 \
-extfile ./ext.cnf -extensions v3_req
openssl x509 -req -in server1.csr \
-CA rootCA.crt -CAkey rootCA.key -CAcreateserial \
-out prod.abc.com.crt -days 730 -sha256 \
-extfile ./ext.cnf -extensions v3_req
#!/bin/bash
cat prod.abc.com.crt rootCA.crt > chain.pem
openssl crl2pkcs7 -nocrl -certfile chain.pem -out prod.abc.com.p7b -outform DER
openssl pkcs7 -in prod.abc.com.p7b -inform DER -print_certs -text -noout | less
以docker-compose.yml來建置wordpress服務於8031 port,並且加上 phpMyAdmin,讓你用瀏覽器管理同一個 MySQL。WordPress 仍然以 8031 對外,phpMyAdmin 另外用 8032。
version: '3.9'
services:
wordpress:
image: wordpress:latest
container_name: wordpress_site
ports:
- "8031:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppassword
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress_data:/var/www/html
depends_on:
- db
restart: always
db:
image: mysql:5.7
container_name: wordpress_db
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppassword
volumes:
- db_data:/var/lib/mysql
restart: always
phpmyadmin:
image: phpmyadmin:latest
container_name: wp_phpmyadmin
depends_on:
- db
environment:
PMA_HOST: db
PMA_PORT: 3306
PMA_USER: root
PMA_PASSWORD: rootpassword
UPLOAD_LIMIT: 512M
ports:
- "8032:80"
restart: always
volumes:
wordpress_data:
db_data:
docker-compose.yml。docker-compose up -dhttp://localhost:8031http://localhost:8032(使用 root / rootpassword 或 wpuser / wppassword 登入)建議:上線前把
rootpassword / wppassword換成強密碼;若要改用 MariaDB 也可以把db的 image 換成mariadb:10.11,其餘設定相同。
可以用以下 Bash 腳本,在 Ubuntu 24.04 上自動安裝 Docker CE 與 docker-compose(最新版 Compose Plugin)。
這個腳本會自動處理官方 Docker GPG Key、套件庫設定,以及安裝與啟動服務。
#!/bin/bash
set -e
echo "=== 更新套件列表 ==="
sudo apt-get update -y
sudo apt-get upgrade -y
echo "=== 安裝必要套件 ==="
sudo apt-get install -y ca-certificates curl gnupg lsb-release
echo "=== 新增 Docker 官方 GPG Key ==="
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "=== 新增 Docker 套件來源 ==="
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
echo "=== 更新套件列表 (Docker) ==="
sudo apt-get update -y
echo "=== 安裝 Docker CE 與 Compose Plugin ==="
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
echo "=== 啟動並設定 Docker 開機自動啟動 ==="
sudo systemctl enable docker
sudo systemctl start docker
echo "=== 將目前使用者加入 docker 群組 (免 sudo) ==="
sudo usermod -aG docker $USER
echo "=== 安裝完成 ==="
docker --version
docker compose version
echo "⚠️ 請重新登入帳號以套用群組變更"
install_docker.shchmod +x install_docker.sh ./install_docker.shdocker 與 docker compose 指令。