Sử dụng ssh và sed trong tập lệnh python với os.system đúng cách
Tôi đang cố gắng chạy một lệnh ssh trong một tập lệnh python bằng cách sử dụng os.systemđể thêm một 0vào cuối của một chuỗi hoàn toàn khớp trong một máy chủ từ xa bằng cách sử dụng sshvà sed.
Tôi có một tệp được gọi nodelisttrong một máy chủ từ xa, một danh sách giống như thế này.
test-node-1
test-node-2
...
test-node-11
test-node-12
test-node-13
...
test-node-21
Tôi muốn sử dụng sed để thực hiện sửa đổi sau, tôi muốn tìm kiếm test-node-1và khi tìm thấy kết quả khớp đầy đủ, tôi muốn thêm số 0 vào cuối, tệp phải có dạng như thế này.
test-node-1 0
test-node-2
...
test-node-11
test-node-12
test-node-13
...
test-node-21
Tuy nhiên, khi tôi chạy lệnh đầu tiên,
hostname = 'test-node-1'
function = 'nodelist'
os.system(f"ssh -i ~/.ssh/my-ssh-key username@serverlocation \"sed -i '/{hostname}/s/$/ 0/' ~/{function}.txt\"")
Kết quả trở thành như thế này,
test-node-1 0
test-node-2
...
test-node-11 0
test-node-12 0
test-node-13 0
...
test-node-21
Tôi đã thử thêm a \ b vào lệnh như thế này,
os.system(f"ssh -i ~/.ssh/my-ssh-key username@serverlocation \"sed -i '/\b{hostname}\b/s/$/ 0/' ~/{function}.txt\"")
Lệnh hoàn toàn không hoạt động.
Tôi phải nhập tên nút theo cách thủ công thay vì sử dụng một biến như vậy,
os.system(f"ssh -i ~/.ssh/my-ssh-key username@serverlocation \"sed -i '/\btest-node-1\b/s/$/ 0/' ~/{function}.txt\"")
để làm cho lệnh của tôi hoạt động.
Lệnh của tôi bị sao vậy, tại sao tôi không thể làm theo ý mình?
Trả lời
Mã này có vấn đề bảo mật nghiêm trọng; sửa chữa chúng đòi hỏi phải cấu trúc lại nó từ đầu. Hãy làm điều đó ở đây:
#!/usr/bin/env python3
import os.path
import shlex # note, quote is only here in Python 3.x; in 2.x it was in the pipes module
import subprocess
import sys
# can set these from a loop if you choose, of course
username = "whoever"
serverlocation = "whereever"
hostname = 'test-node-1'
function = 'somename'
desired_cmd = ['sed', '-i',
f'/\\b{hostname}\\b/s/$/ 0/',
f'{function}.txt']
desired_cmd_str = ' '.join(shlex.quote(word) for word in desired_cmd)
print(f"Remote command: {desired_cmd_str}", file=sys.stderr)
# could just pass the below direct to subprocess.run, but let's log what we're doing:
ssh_cmd = ['ssh', '-i', os.path.expanduser('~/.ssh/my-ssh-key'),
f"{username}@{serverlocation}", desired_cmd_str]
ssh_cmd_str = ' '.join(shlex.quote(word) for word in ssh_cmd)
print(f"Local command: {ssh_cmd_str}", file=sys.stderr) # log equivalent shell command
subprocess.run(ssh_cmd) # but locally, run without a shell
Nếu bạn chạy điều này (ngoại trừ subprocess.runở cuối, yêu cầu khóa SSH thực, tên máy chủ, v.v.), đầu ra sẽ giống như sau:
Remote command: sed -i '/\btest-node-1\b/s/$/ 0/' somename.txt
Local command: ssh -i /home/yourname/.ssh/my-ssh-key whoever@whereever 'sed -i '"'"'/\btest-node-1\b/s/$/ 0/'"'"' somename.txt'
Đó là đầu ra chính xác / mong muốn; '"'"'thành ngữ vui nhộn là cách người ta đưa một trích dẫn đơn theo nghĩa đen vào một chuỗi được trích dẫn một cách an toàn trong một trình bao tuân thủ POSIX.
Khác biệt là gì? Rất nhiều:
- Chúng tôi đang tạo các lệnh chúng tôi muốn chạy dưới dạng mảng và cho phép Python thực hiện công việc chuyển đổi các mảng đó thành chuỗi khi cần thiết. Điều này giúp tránh các cuộc tấn công tiêm vỏ, một loại lỗ hổng bảo mật rất phổ biến.
- Bởi vì chúng tôi đang tự tạo danh sách, chúng tôi có thể thay đổi cách chúng tôi trích dẫn từng danh sách: Chúng tôi có thể sử dụng chuỗi f khi thích hợp để làm như vậy, chuỗi thô khi thích hợp, v.v.
- Chúng tôi không chuyển
~đến máy chủ từ xa: Điều này là thừa và không cần thiết vì~là nơi mặc định để bắt đầu phiên SSH; và các biện pháp phòng ngừa bảo mật mà chúng tôi đang sử dụng (để ngăn các giá trị được phân tích thành mã bởi một trình bao) ngăn nó có bất kỳ tác dụng nào (vì việc thay thế~bằng giá trị hoạt động củaHOMEkhông được thực hiện bởisedchính nó, mà bởi trình bao gọi nó , bởi vì chúng ta không gọi bất kỳ vỏ địa phương ở tất cả, chúng ta cũng cần phải sử dụngos.path.expanduserđể gây ra~trong~/.ssh/my-ssh-keyđể được vinh danh). - Bởi vì chúng tôi không sử dụng một chuỗi thô, chúng tôi cần phải nhân đôi các dấu gạch chéo ngược vào
\bđể đảm bảo rằng chúng được Python xử lý dưới dạng chữ thay vì cú pháp. - Đặc biệt, chúng tôi không bao giờ truyền dữ liệu trong một ngữ cảnh mà nó có thể được phân tích thành mã bởi bất kỳ trình bao nào, dù là cục bộ hay từ xa .