在menu.sh 裡增加功能

要在menu.sh 裡增加功能 1. scp功能, 但要讓user自己設定引數與參數 2. ssh 到特定主機, 但要user自己設定登入的帳號名稱

可以,但千萬不要直接讓使用者在 menu.sh 裡「自由輸入整串 scp/ssh 參數」然後丟進 shell 執行(會被用 ;$( )-oProxyCommand 等方式繞出去)。正確做法是:

  • 只讓 user 填「你允許的欄位」(目的主機、帳號、來源檔名、目的路徑…)
  • 其餘 SSH/SCP 參數由你固定寫死
  • 白名單主機 + 路徑限制(只允許在特定資料夾內操作)
  • 用 wrapper script(root 擁有)承接,menu 只呼叫 wrapper

下面給你可直接套用的 SOP。


0) 先做一個專用交換目錄(限制檔案範圍)

sudo mkdir -p /home/optim/transfer
sudo chown optim:optim /home/optim/transfer
sudo chmod 700 /home/optim/transfer

1) 建立 scp wrapper(允許 user 填「引數/參數」但被你控住)

1-1 建立 /usr/local/bin/optim_scp

sudo 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

1-2 權限

sudo chown root:root /usr/local/bin/optim_scp
sudo chmod 755 /usr/local/bin/optim_scp

2) 建立 ssh wrapper(固定主機,但 user 可自填登入帳號)

2-1 建立 /usr/local/bin/optim_ssh

sudo 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"

2-2 權限

sudo chown root:root /usr/local/bin/optim_ssh
sudo chmod 755 /usr/local/bin/optim_ssh

3) 把功能加到 menu.sh

在你原本的 case 裡新增選項,例如:

echo "5) SCP 檔案傳輸 (受控)"
echo "6) SSH 到指定主機 (帳號自填)"

並加入:

5)
  /usr/local/bin/optim_scp
  ;;
6)
  /usr/local/bin/optim_ssh
  ;;

4) 重要提醒(避免你以為「外部 scp 也能用」)

你現在用的是 sshd_configForceCommand /opt/optim-menu/menu.sh,所以:

  • 使用者登入後在選單內執行 scp/ssh(由 server 端發起):可行
  • 從使用者電腦用 scp optim@你的主機:... 直接拉檔:通常會失敗(因為 scp 需要走 ssh 的 scp 子系統/命令,而你強制進選單)

如果你也需要「外部 scp 到這台主機」那要改成用 Match 區分 Subsystem sftp 或針對 sftp/scp 做不同規則(但那是另一套設計)。


如果你告訴我:

  • 你要允許的遠端主機清單(多台?一台?)
  • scp 只允許上傳?只允許下載?或都要?
  • 遠端允許的目錄範圍(例如只准 /home/*/upload

我可以把 wrapper 再加上「遠端路徑白名單」與「大小/副檔名限制」避免被拿去搬敏感檔。

信心指數:92%

ubuntu 24.04 限制使用者登入後僅能使用選單中的指令

以下是一套在 Ubuntu 24.04 中,限制使用者 optim 以「密碼登入 SSH 後,只能透過選單執行指定指令」的完整 SOP
此作法適合維運帳號、代管帳號、委外人員,避免任意下指令。


一、設計原則(先說清楚邏輯)

  1. SSH 可用密碼登入
  2. 登入後 不給 Shell
  3. 只執行一個「選單程式(Menu Script)」
  4. 選單內只呼叫 白名單指令
  5. 使用者無法:
    • cd
    • vi / nano
    • bash
    • ssh 到別台
    • Ctrl+C 跳出

二、建立使用者 optim(若已存在可略過)

sudo useradd -m optim
sudo passwd optim

三、準備「受控選單腳本」

1️⃣ 建立目錄

sudo mkdir -p /opt/optim-menu
sudo chown root:root /opt/optim-menu
sudo chmod 755 /opt/optim-menu

2️⃣ 建立選單腳本

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

3️⃣ 設定權限(非常重要)

sudo chown root:root /opt/optim-menu/menu.sh
sudo chmod 755 /opt/optim-menu/menu.sh

⚠️ 使用絕對路徑(/usr/bin/df),避免 PATH 被濫用。


四、限制 optim 使用者的 Shell

方法一(最推薦):使用 ForceCommand

編輯 SSH 設定

sudo vi /etc/ssh/sshd_config

在檔案最下方加入:

Match User optim
    ForceCommand /opt/optim-menu/menu.sh
    PasswordAuthentication yes
    AllowTcpForwarding no
    X11Forwarding no
    PermitTTY yes

重新啟動 SSH

sudo systemctl restart ssh

五、(加強)防止逃逸技巧

1️⃣ 禁用 .bashrc / .profile

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

2️⃣ 限制 sudo(若不需要)

sudo visudo

確認 沒有

optim ALL=(ALL) ALL

六、測試驗證清單(必做)

請實際測試以下行為:

測試項目預期結果
SSH 密碼登入✅ 可登入
登入後看到 bash❌ 不可
出現選單
嘗試輸入 bash❌ 無效
Ctrl + C❌ 不可中斷
選 0✅ 正常登出
scp 傳檔❌ 失敗

七、常見錯誤與排查

🔴 登入直接斷線

  • menu.sh 沒有執行權限
  • ForceCommand 路徑錯誤

👉 解法:

chmod +x /opt/optim-menu/menu.sh

🔴 指令顯示 command not found

  • 沒用「絕對路徑」

👉 一律改成:

/usr/bin/xxx

八、進階強化(可選)

  • 搭配 auditd 記錄所有執行行為
  • 將 menu.sh 改為 Python / whiptail UI
  • 搭配 PAM + 時段限制
  • SSH 僅允許指定 IP

九、總結

此 SOP 能達成:

✅ 密碼登入
✅ 僅能選單操作
✅ 指令白名單
✅ 無 Shell、無逃逸
✅ 符合實務維運安全

若你需要:

  • 含審計版(auditd)
  • 多層子選單
  • 一鍵部署 Script
  • 搭配 sudo 精準放權

可直接說明使用情境,我可幫你客製化。


信心指數:96%

建立多主機多帳號的操作白名單

以下是你要的 「主機 × 可登入帳號 白名單對應版」optim_ssh,此版本具備:

  • 多台 SSH 目標主機
  • 每台主機有各自允許的登入帳號清單
  • ❌ 使用者無法自行指定任意主機
  • ❌ 使用者無法輸入未授權帳號
  • ❌ 無 Port Forward / ProxyCommand / 跳板逃逸

一、最終版檔案:/usr/local/bin/optim_ssh

#!/bin/bash
set -euo pipefail

# ========= 主機 × 帳號 白名單 =========
# 格式:
# "主機|帳號1,帳號2,帳號3"

TARGETS=(
  "10.0.0.10|root,admin,ops"
  "10.0.0.11|ops"
  "server1.internal|deploy,ops"
  "server2.internal|admin"
)

# ====================================

valid_user() {
  [[ "$1" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]]
}

print_menu() {
  echo "=== SSH 受控連線選單 ==="
  echo
  local i=1
  for entry in "${TARGETS[@]}"; do
    host="${entry%%|*}"
    users="${entry##*|}"
    echo "  $i) $host   (允許帳號: $users)"
    ((i++))
  done
  echo "  0) 返回"
  echo
}

while true; do
  clear
  print_menu
  read -r -p "請選擇主機: " pick

  if [[ "$pick" == "0" ]]; then
    exit 0
  fi

  if ! [[ "$pick" =~ ^[0-9]+$ ]]; then
    echo "ERR: 請輸入數字"
    read -r -p "按 Enter 繼續..." _
    continue
  fi

  idx=$((pick - 1))
  if (( idx < 0 || idx >= ${#TARGETS[@]} )); then
    echo "ERR: 選項超出範圍"
    read -r -p "按 Enter 繼續..." _
    continue
  fi

  entry="${TARGETS[$idx]}"
  host="${entry%%|*}"
  allowed_users="${entry##*|}"

  read -r -p "請輸入登入帳號: " ruser

  if ! valid_user "$ruser"; then
    echo "ERR: 帳號格式不合法"
    read -r -p "按 Enter 繼續..." _
    continue
  fi

  # 驗證帳號是否在該主機白名單
  if ! echo ",$allowed_users," | grep -q ",$ruser,"; then
    echo "ERR: 帳號 [$ruser] 不允許登入 $host"
    read -r -p "按 Enter 繼續..." _
    continue
  fi

  echo
  echo "即將連線:$ruser@$host"
  echo "(結束遠端連線請輸入 exit)"
  echo

  /usr/bin/ssh \
    -o "ClearAllForwardings=yes" \
    -o "PermitLocalCommand=no" \
    -o "ProxyCommand=none" \
    -o "ServerAliveInterval=30" \
    -o "ServerAliveCountMax=3" \
    -l "$ruser" \
    "$host"

  read -r -p "已離開 $host,按 Enter 回到主選單..." _
done

二、權限設定(不可省略)

sudo chown root:root /usr/local/bin/optim_ssh
sudo chmod 755 /usr/local/bin/optim_ssh

三、安全特性總覽(你現在已做到)

項目狀態
主機白名單
每台主機帳號白名單
使用者自行輸入 host
任意 ssh 參數
Port Forward / Tunnel
ProxyCommand / 跳板
離開後回選單
搭配 ForceCommand

四、進階強化(如果你要更硬)

你現在已經是 「企業級 Jump Menu」 等級了,還可以再加:

  1. auditd 記錄連線行為
  2. 限制某些主機只能在特定時段連
  3. 依 optim 本地帳號 → 映射遠端帳號(禁止手填)
  4. 禁止 root,只能透過 sudo 登入遠端
  5. 連線前二次確認(防誤連 production)

如果你要其中任何一項,我可以直接給你「可上線版本」。


信心指數:97%

使用ubuntu2404來自簽安全憑證

佈署環境(ubuntu 2404)

安裝必要套件

apt update && apt upgrade -y
apt install openssl ca-certificates

建立工作目錄

mkdir -p ~/certwork
chmod 700 ~/certwork
cd ~/certwork

將CSR放到工作資料夾中

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

檢視CSR

certwork#openssl req -in ~/ca/prod.abc.com.csr -noout -text

產 Root CA 私鑰

openssl genrsa -out rootCA.key 4096
openssl genrsa -out rootCA.key 2048
openssl genrsa -out rootCA.key 1024 (此次成功)
三個擇一執行(看是要用什麼等級)

建 Root CA 憑證(自簽的 CA)

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年,可改短一點以符合內規

建立 ext.cnf:

[ 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

由CSR產出crt 憑證 ( sha-1演算法, 十年)

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

由CSR產出crt 憑證(2年 ,sha256演算法)

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

將crt憑證轉為p7b憑證
#!/bin/bash

建立鏈檔:leaf + root(若你有中繼 CA,順序應為 leaf → intermediate → root)

cat prod.abc.com.crt rootCA.crt > chain.pem

產 DER 格式的 .p7b

openssl crl2pkcs7 -nocrl -certfile chain.pem -out prod.abc.com.p7b -outform DER

檢視 p7b 內容

openssl pkcs7 -in prod.abc.com.p7b -inform DER -print_certs -text -noout | less

[linux][nginx]nginx反向代理 webdav服務

反向代理一個完整 WebDAV 伺服器

# 假設後端 WebDAV 在 http://192.168.0.111:8080/dav/
server {
    listen 443 ssl;
    server_name dav.example.com;

    # ... SSL 設定略 ...

    client_max_body_size 0;           # 不限制上傳大小(視需求)
    proxy_http_version 1.1;           # WebDAV/長連線建議用 HTTP/1.1
    keepalive_requests 1000;

    location / {
        proxy_pass http://192.168.0.111:8080;

        # 讓反代更「透明」
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # 重要:保留 WebDAV 特有標頭(部分客戶端很依賴)
        proxy_set_header Destination   $http_destination;
        proxy_set_header Depth         $http_depth;
        proxy_set_header Overwrite     $http_overwrite;
        proxy_set_header If-None-Match $http_if_none_match;
        proxy_set_header If-Match      $http_if_match;
        proxy_set_header If-Modified-Since $http_if_modified_since;
        proxy_set_header If-Unmodified-Since $http_if_unmodified_since;
        proxy_set_header Lock-Token     $http_lock_token;
        proxy_set_header Timeout        $http_timeout;
        proxy_set_header Range          $http_range;
        proxy_set_header If-Range       $http_if_range;
        proxy_set_header Expect         $http_expect;  # 100-continue

        # 大檔/串流上傳(避免先寫到磁碟)
        proxy_request_buffering off;
        proxy_buffering off;

        # 某些 Windows WebDAV 客戶端相容性
        chunked_transfer_encoding off;  # 視後端/客戶端而定
    }
}

小重點

  • 不要用 limit_except GET { ... } 這類把非 GET/HEAD/POST 擋掉的設定。
  • 若前端做 Basic/Auth,確定與後端認證邏輯不互相衝突(雙重 401 會讓部分客戶端混亂)。
  • 如果需要讓後端知道真正的 Host/Proto,務必帶 X-Forwarded-*
  • 檔案很大或想節省 Nginx 磁碟 I/O,關 proxy_request_buffering
  • 某些客戶端(特別是 Windows WebClient)對 HTTPS、OPTIONSExpect: 100-continue、是否使用 chunked encoding 特別敏感,上面參數可改善相容性。

快速檢查清單

  • 後端 WebDAV 功能完整(含 LOCK/UNLOCK/PROPFIND)。
  • Nginx 沒擋 WebDAV 方法(未使用不當 limit_except)。
  • 保留並轉送 WebDAV 標頭(Destination/Depth/Overwrite/If-* 等)。
  • proxy_http_version 1.1、必要時關 proxy_request_buffering
  • 設好上傳大小(client_max_body_size)。
  • 與客戶端相容性調整(HTTPS、OPTIONS 回應、100-continue、chunked)。

結論

  • 要「完整轉送服務」:用 Nginx 當反代、把細節調好 → ✅ 沒問題。
  • 要「自己提供完整 WebDAV」只靠 Nginx 原生模組 → ❌ 不建議,功能不全。

docker-compose來快速建置wordpress

以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:

使用方式

  1. 將上面內容存成 docker-compose.yml
  2. 啟動: docker-compose up -d
  3. 開啟:
    • WordPress:http://localhost:8031
    • phpMyAdmin:http://localhost:8032(使用 root / rootpasswordwpuser / wppassword 登入)

建議:上線前把 rootpassword / wppassword 換成強密碼;若要改用 MariaDB 也可以把 db 的 image 換成 mariadb:10.11,其餘設定相同。

Ubuntu 24.04 上自動安裝 Docker CE 與 docker-compose

可以用以下 Bash 腳本,在 Ubuntu 24.04 上自動安裝 Docker CEdocker-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 "⚠️ 請重新登入帳號以套用群組變更"

使用方式

  1. 將上述內容儲存為檔案,例如 install_docker.sh
  2. 執行: chmod +x install_docker.sh ./install_docker.sh
  3. 安裝完成後,登出並重新登入,就可以直接使用 dockerdocker compose 指令。

Chrome無法開啟內網IP的站台服務

[問題]
昨天收到RD的反應, 無法開啟內部git主機的https服務,但ssh可以正常

[使用環境]
1. RD使用的PC為linux系統
2. 在他自帶的IP分享器後端
3. windows中測試可以正常開啟
4. 後來發現在同一台Linux PC上,firefox也可以正常開啟

[排除測試]
1. 確認DNS為公司內部的DNS(指向private IP)
2. 測試過程中一度可以正常開啟
3. 研判為chrome自帶的 dns over https 所造成,因為解析到外部IP則無法正常開啟

[結果]
經關閉chrome的 dns over https 的選項後,恢復正常

nginx報錯nginx: [emerg] unknown “connection_upgrade” variable的解決辦法

nginx在啟動的時候報錯

nginx: [emerg] unknown “connection_upgrade” variable

報錯原因是加了proxy_set_header Connection $connection_upgrade這一行導致。

解決辦法:

在http追加如下:

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

重啟nginx之後會發現報錯沒了!

使用ubuntu 22.04 手動安裝 MySQL 5.7

1、下載安裝檔:mysql-server_5.7.31-1ubuntu18.04_amd64.deb-bundle.tar

歷史版本下載地址:https://downloads.mysql.com/archives/community/

2、解壓縮安裝檔

#tar zxvf mysql-server_5.7.31-1ubuntu18.04_amd64.deb-bundle.tar

3、先安裝相依性套件

#apt-get update
#apt-get upgrade

#apt-get install ./libmysql*
#apt-get install libtinfo5

4、安裝client 與 Server

#apt-get install ./mysql-community-client_5.7.31-1ubuntu18.04_amd64.deb
#apt-get install ./mysql-client_5.7.31-1ubuntu18.04_amd64.deb
#apt-get install ./mysql-community-server_5.7.31-1ubuntu18.04_amd64.deb
#apt-get install ./mysql-server_5.7.31-1ubuntu18.04_amd64.deb 

安裝Server的過程中, 會請你輸入MySQL Server的管理者(root)密碼

5、安裝完畢後可用以下指令檢查

systemctl status mysql.service
systemctl start  mysql.service
systemctl stop   mysql.service

[linux]umount 時 出現 “Device is busy”

當任何目錄有 mount, 然後有程式 使用/掛 在那個目錄上的話, 就沒有辦法 umount 掉, 於 umount 時會出現 Device is busy 的訊息.

要怎麼找出是哪個程式掛在那個目錄上? 然後去把那個程式砍掉呢?

假設現在 mount 起來的目錄是 /opt/data

  • 查詢: fuser -m /opt/data
  • 顯示: /opt/data: 698434c 699200c 699220c

數字代表程序, 而後面c的代表意思參考下列

  • c: current directory.
  • e: executable being run.
  • f: open file. f is omitted in default display mode.
  • F: open file for writing. F is omitted in default display mode.
  • r: root directory.
  • m: mmap’ed file or shared library.

再用 #kill -9 698434 將程序刪除後即可umount

#umount /opt/data

[docker][bitnami/redmine]版本升級5.0.2升級至6.0.2

由於原本安裝的dockr-compose.yml 是在/root 下啟動的, 因此也要回到/root下執行 #docker-compose down

以下為原

version: '3.1'
services:
  mariadb:
    image: mariadb:10.9.2
    container_name: db
    ports:
      - '3306:3306'
    volumes:
      - 'mariadb_data:/var/lib/mysql'
    environment:
      - MARIADB_USER=test_redmine
      - MARIADB_PASSWORD=test_password
      - MARIADB_DATABASE=bitnami_redmine
      - MARIADB_ROOT_PASSWORD= '638d5c15-6d94-4115-b9e5-961a9eb5c972'
  redmine:
    image: docker.io/bitnami/redmine:5.0.2
    container_name: redmine
    ports:
      - '80:3000'
    volumes:
      - 'redmine_data:/bitnami/redmine'
    depends_on:
      - mariadb
    environment:
      - REDMINE_DATABASE_HOST=mariadb
      - REDMINE_DATABASE_PORT_NUMBER=3306
      - REDMINE_DATABASE_USER=test_redmine
      - REDMINE_DATABASE_PASSWORD=test_password
      - REDMINE_DATABASE_NAME=bitnami_redmine
volumes:
  mariadb_data:
    driver: local
  redmine_data:
    driver: local

之後修改/root/docker-compose.yml 將指定版本拿掉, 讓它下載最新的images來啟動

如下列所示

version: '3.1'
services:
  mariadb:
    image: mariadb
    container_name: db
    ports:
      - '3306:3306'
    volumes:
      - 'mariadb_data:/var/lib/mysql'
    environment:
      - MARIADB_USER=test_redmine
      - MARIADB_PASSWORD=test_password
      - MARIADB_DATABASE=bitnami_redmine
      - MARIADB_ROOT_PASSWORD= '638d5c15-6d94-4115-b9e5-961a9eb5c972'
  redmine:
    image: docker.io/bitnami/redmine
    container_name: redmine
    ports:
      - '80:3000'
    volumes:
      - 'redmine_data:/bitnami/redmine'
    depends_on:
      - mariadb
    environment:
      - REDMINE_DATABASE_HOST=mariadb
      - REDMINE_DATABASE_PORT_NUMBER=3306
      - REDMINE_DATABASE_USER=test_redmine
      - REDMINE_DATABASE_PASSWORD=test_password
      - REDMINE_DATABASE_NAME=bitnami_redmine
volumes:
  mariadb_data:
    driver: local
  redmine_data:
    driver: local

再於/root 下將其啟動 #docker-compose up -d

其依照當下最近的版本來下載安裝並啟動, 而當下的最新版本即6.0.2 , 因此即升級至 6.0.2 的版本

【問題排除】遇到有人使用mac的瀏覽器(Safari)無法預覽PDF 文件, 但是在 chrome 可以正常

使用者環境:iOS 11.3.1;Safari 14.1

問題原因:PMS 於附加檔案的 HTTP Header 增加的新式參數,導致 Safari 舊版無法直接預覽檔案。

解決方式參考來源:

https://github.com/bitnami/vms/issues/1439

解決方法:

將 PMS (Redmine) Docker Volumn 中的

/opt/bitnami/redmine/app/controllers/attachments_controller.rb

接近檔案結尾處:

headers[‘content-security-policy’] = “default-src ‘none’; style-src ‘unsafe-inline’; sandbox”

將 sandbox 移除,即可於舊版 Safari 正常預覽 PDF。

Docker 指令紀錄:

#先檢查檔案是否存在,以及確切的檔案路徑# 檢視 Docker Volumn (目前 Volumn Name 為 pms)docker ps
# 以 BASH 進入 Docker Volumndocker exec -it pms /bin/bash
# 進入 Volumn 確認檔案位置後,離開 Dockerexit
# 將 attachments_controller.rb 檔案 copy 至 Docker Volumn 外docker cp pms:/opt/bitnami/redmine/app/controllers/attachments_controller.rb ~
# 修改檔案 attachments_controller.rbnano attachments_controller.rb
# 檔案修正完成並儲存後,將檔案複製回 Docker Volumndocker cp attachments_controller.rb pms:/opt/bitnami/redmine/app/controllers/
# 複製完成後,會即時生效# 除非重新掛載 Docker Image,該修改應當會一直存在不會消失