如何在Azure Pipeline透過地端Agent使用SSH連線地端Ubuntu Server進行sudo作業
我們想在地端原有的 Azure DevOps Agent(Based on Linux)對地端的另一台 Linux VM 下一些指令,但某些指令需要使用 sudo 來提權。大致架構如下:
Azure Pipeline(Cloud) ←-> Azure Agent (on-premise,Linux VM) ←-> SSH - sudo commands(on-premise,Linux VM)
研究之後,解法有好幾種:
- 在要操作的 Linux VM 安裝 Azure DevOps Agent,那麼 Azure DevOps Agent 就能直接與此 Linux VM 溝通。(最好,但會增加費用)
- 某些作業需要有
root權限,一般我們會用sudo來提權,但提權過程需要輸入密碼。但在 Azure Pipeline 的 CD task 環境中,沒有發現能這樣操作的可能性。因此想過,開放root身份進行 SSH 遠端登入。雖然在內網環境,開放root身份會增加資安風險。 - 順著第二點,還有一種
sudoers的設定方式,可以為解決「sudo提權需要輸入密碼」這件事。
當然,如果能花錢解決,第一點是快又有效。那第3點和第2點有何不同,簡單說,root 這名子太危險,大家都想取得這個身分權限,因此能不動到它就不動它。而 sudoers 方法, 我們可以設定一組「帳、密」都含有一定複雜度的身份,給 SSH 連線專用。這樣就算被攻擊,光花在猜「帳號名稱」就必須花費不少時間,更不要說是「帳、密」都亂亂的情況下,要在內網發動「長時間」的攻擊實在不太容易。我們決定採用第三點來嘗試解決我們的問題。
sudo adduser <user_name>
sudo adduser <user_name> sudo
sudo passwd <user_name>
id <user_name>
這裡設定了一組含 sudo 身分的帳號,最簡單可以利用 sudo apt update 來測試提權(輸入密碼)的過程。 這也是地端 Agent 進行 SSH 連線後,需要 sudo 操作時會碰到的問題。如果 sudo 的執行沒問題,那接下來設定無碼密執行 sudo,在 Ubuntu Server 執行以下指令:
echo "<user_name> ALL=(ALL:ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/<user_name>
設定很直覺,我們希望這位 <user_name> 可以在進行 sudo 操作時不需要輸入密碼。設定好重開機一下,一樣可以用 sudo apt update 來驗證,沒有問密碼就往下執行就是設定沒問題。
- 這裡沒有針對
sudoers細節多做介紹,這裡有篇不錯的 sudoers 參考文件,連如何開給 AI / MCP Server 的sudoers權限都有寫。- 另外注意一下,範例是懶人設定法,是所有指令的
sudo操作都不需要密碼,等於權限和root是一樣了。比較好的方式是參考文件中的設定方式,例如NOPASSWD: /usr/bin/systemctl reload nginx去限定此帳號只有在特定指令下可以進行無密碼的sudo操作。
處理好帳號之後,我們還要先處理 SSH Key,這是準備要提供 Azure DevOps 使用的。
這裡一樣不會針對 SSH Key 多做介紹。可以參考這篇 詳細步驟:在 Azure 中建立和管理對 Linux VM 進行驗證所需的 SSH 金鑰 來學習相關知識。
# ssh-keygen -t ed25519 -f $HOME/.ssh/id_ed25519 -P ""
$ ssh-keygen -t rsa -b 4096 -f $HOME/.ssh/id_rsa -P ""
Generating public/private rsa key pair.
Your identification has been saved in /home/<user_name>/.ssh/id_rsa
Your public key has been saved in /home/<user_name>/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:hash <user_name>@hostname
The key's randomart image is:
+---[RSA 4096]----+
| .++.....+++.. |
| .+ ... .+o+ |
| . o oo o. o |
| o + oE B .. |
| = o.SB + |
| . . + B |
| @ = |
| . X +. |
| oo+ |
+----[SHA256]-----+
ed25519是較新的演算法,不用特別指定-b長度。但這裡我們以常見的rsa為例。-P ""等於產生無密碼的私鑰。(一般不建議,但你知道的,寫文就是個 PoC,讓我們降低一些標準!)
$ cat .ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
Your RSA Hash Code
-----END OPENSSH PRIVATE KEY-----
一般是提供 id_rsa.pub,但這裡我們把 SSH 私鑰複制出來。接著我們開啟 Azure DevOps 的 Service Connection 選擇 SSH 依照要求填入所需資訊,剛剛拿到的 id_rsa 可以直接貼到 Private Key 裡。
以下複製自上面的文件:
| Parameter | Description |
|---|---|
| Host name | Required. The name of the remote host machine or the IP address. |
| Port number | Required. The port number of the remote host machine. The default is port 22. |
| Private Key | The entire contents of the private key file if using this type of authentication. |
| Username | Required. The username to use when connecting to the remote host machine. |
| Password/Passphrase | The password or passphrase for the specified username if using a keypair as credentials. |
| Connection name | Required. The name you use to refer to the service connection in task properties. If you're using YAML, use the name as the azureSubscription or the equivalent subscription name value in the script. |
| Description | Optional. The description of the service connection. |
| Security | Optional. Select Grant access permission to all pipelines to allow all pipelines to use this connection. If you don't select this option, you must explicitly authorize the service connection for each pipeline that uses it. |
有了 SSH service connection,我們就能在 SSH Task 裡去使用剛剛建置的 SSH 端點。
steps:
- task: SSH@0
displayName: 'Run shell commands on remote machine'
inputs:
sshEndpoint: kkbrucelab
commands: |
echo "Hostname is $(hostname)"
sudo service nginx reload
但接下來就能碰到一連串的錯誤 XD
2025-10-02T15:28:05.8512173Z ##[section]Starting: Run shell inline on remote machine
2025-10-02T15:28:05.8635781Z ==============================================================================
2025-10-02T15:28:05.8637318Z Task : SSH
2025-10-02T15:28:05.8637807Z Description : Run shell commands or a script on a remote machine using SSH
2025-10-02T15:28:05.8638233Z Version : 0.259.0
2025-10-02T15:28:05.8638654Z Author : Microsoft Corporation
2025-10-02T15:28:05.8639080Z Help : https://docs.microsoft.com/azure/devops/pipelines/tasks/deploy/ssh
2025-10-02T15:28:05.8639482Z ==============================================================================
2025-10-02T15:28:06.1295916Z Trying to establish an SSH connection to ***@target.kkbruce.net:22
2025-10-02T15:28:06.1415016Z (node:272363) [DEP0106] DeprecationWarning: crypto.createCipher is deprecated.
2025-10-02T15:28:06.1416381Z (Use `node --trace-deprecation ...` to show where the warning was created)
2025-10-02T15:28:06.1417162Z (node:272363) Warning: Use Cipheriv for counter mode of aes-256-ctr
2025-10-02T15:28:06.3623361Z ##[error]Failed to connect to remote machine. Verify the SSH service connection details. Error: Error: All configured authentication methods failed
at doNextAuth (/home/***/ReleaseAgent01/_work/_tasks/SSH_91443475-df55-4874-944b-39253b558790/0.259.0/node_modules/ssh2/lib/client.js:865:21)
at tryNextAuth (/home/***/ReleaseAgent01/_work/_tasks/SSH_91443475-df55-4874-944b-39253b558790/0.259.0/node_modules/ssh2/lib/client.js:1082:7)
at USERAUTH_FAILURE (/home/***/ReleaseAgent01/_work/_tasks/SSH_91443475-df55-4874-944b-39253b558790/0.259.0/node_modules/ssh2/lib/client.js:430:11)
at 51 (/home/***/ReleaseAgent01/_work/_tasks/SSH_91443475-df55-4874-944b-39253b558790/0.259.0/node_modules/ssh2/lib/protocol/handlers.misc.js:408:16)
at Protocol.onPayload (/home/***/ReleaseAgent01/_work/_tasks/SSH_91443475-df55-4874-944b-39253b558790/0.259.0/node_modules/ssh2/lib/protocol/Protocol.js:2059:10)
at AESGCMDecipherNative.decrypt (/home/***/ReleaseAgent01/_work/_tasks/SSH_91443475-df55-4874-944b-39253b558790/0.259.0/node_modules/ssh2/lib/protocol/crypto.js:987:26)
at Protocol.parsePacket [as _parse] (/home/***/ReleaseAgent01/_work/_tasks/SSH_91443475-df55-4874-944b-39253b558790/0.259.0/node_modules/ssh2/lib/protocol/Protocol.js:2028:25)
at Protocol.parse (/home/***/ReleaseAgent01/_work/_tasks/SSH_91443475-df55-4874-944b-39253b558790/0.259.0/node_modules/ssh2/lib/protocol/Protocol.js:313:16)
at Socket.<anonymous> (/home/***/ReleaseAgent01/_work/_tasks/SSH_91443475-df55-4874-944b-39253b558790/0.259.0/node_modules/ssh2/lib/client.js:775:21)
at Socket.emit (node:events:524:28) {
level: 'client-authentication'
}.
All configured authentication methods failed 好嚇人的訊息!經過多次嘗試設定後,才發現這個 SSH service connection 的組態有點好玩。不過我先不處理它,我們需要再處理底層架構問題,還記得我們架構:
Azure Pipeline(Cloud) ←-> Azure Agent (source,serivce account) ←-> SSH - sudo commands(target,serivce account)
我們先將一開始設定在 target VM 使用的 SSH Account 設定為 Service Account 模式。也就是說,一模一樣的 SSH Account 除了target(目的端)之外,source(來源端,Agent 所在地)也需要存在。Service Account 是企業很常見的帳號管理模式。另外,source VM 也需要產生 SSH Key,產生好之後,手動在 Agent VM 上使用 (SSH)Service Account 透過 ssh / ssh-copy-id 完成登入 target VM 的動作。ssh-copy-id 執行成功,那麼 target VM 的 .ssh/authorized_keys 檔案裡會寫入 source VM 的 id_rsa.pub(公開金鑰)的內容。
這部分的操作,其實在 SSH Task 的 Prerequisites 有說明,只是文件沒說執行細節。
- The task supports use of an SSH key pair to connect to the remote machine(s).
- The public key must be pre-installed or copied to the remote machine(s).
注意:在 source VM 中的 Service Account 並不需要設定
sudoers權限。
最後我們來解決 SSH Task 的錯誤,我們來看當初 SSH service connection 設定畫面:
再看看我最後試出來有問題的地方:
快吐血,那組 Private Key 要填寫的是 source VM 的 SSH Private Key,整個設定全部都是填寫 target VM 的資訊,誰猜得到這個地方是要放 source VM 的!(我猜到了啦! 😅 😅 😅 )
重跑 SSH Task,SSH 連線、執行一般指令、執行 sudo 指令都沒問題。
2025-10-03T03:44:43.5781713Z ##[section]Starting: Run shell commands on remote machine
2025-10-03T03:44:43.5904018Z ==============================================================================
2025-10-03T03:44:43.5904923Z Task : SSH
2025-10-03T03:44:43.5905340Z Description : Run shell commands or a script on a remote machine using SSH
2025-10-03T03:44:43.5905656Z Version : 0.259.0
2025-10-03T03:44:43.5905998Z Author : Microsoft Corporation
2025-10-03T03:44:43.5906348Z Help : https://docs.microsoft.com/azure/devops/pipelines/tasks/deploy/ssh
2025-10-03T03:44:43.5906607Z ==============================================================================
2025-10-03T03:44:43.8470889Z Trying to establish an SSH connection to ***@target.kkbruce.net:22
2025-10-03T03:44:43.8607402Z (node:644520) [DEP0106] DeprecationWarning: crypto.createCipher is deprecated.
2025-10-03T03:44:43.8608189Z (Use `node --trace-deprecation ...` to show where the warning was created)
2025-10-03T03:44:43.8609326Z (node:644520) Warning: Use Cipheriv for counter mode of aes-256-ctr
2025-10-03T03:44:44.0866014Z Successfully connected.
2025-10-03T03:44:44.0867601Z echo "Hostname is $(hostname)"
2025-10-03T03:44:44.5433753Z Hostname is target
2025-10-03T03:44:44.5434529Z
2025-10-03T03:44:44.5451506Z sudo service nginx reload
2025-10-03T03:44:44.6778633Z ##[section]Finishing: Run shell commands on remote machine
這樣地端 Azure Agent 就能更靈活透過 SSH 去連線操控遠端的 Linux VM 了。


沒有留言:
張貼留言
感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。