我最近在建構一個高效能網路自動化實驗室,過程中累積了一些 Python 自動化和 SSH 管理的實戰經驗,想在此分享給大家。

首先,我將示範如何使用 Python 和 Paramiko 函式庫實作網路裝置時間和時區的自動化設定,並分享我如何逐步最佳化程式碼結構和資料組織方式。

import paramiko
import time
import json

# 裝置資訊,使用字典列表儲存,每個字典代表一個裝置
devices = [
    {'ip': '192.168.127.3', 'hostname': 'R1'},
    {'ip': '192.168.127.4', 'hostname': 'R2'},
    {'ip': '192.168.127.101', 'hostname': 'SW1'},
    {'ip': '192.168.127.102', 'hostname': 'SW2'},
    {'ip': '192.168.127.133', 'hostname': 'R3'}
]

username = 'blackcat'
password = 'cisco123'

ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

for device in devices:
    try:
        ssh_client.connect(hostname=device['ip'], username=username, password=password, look_for_keys=False, timeout=5)
        print(f"成功連線到 {device['hostname']} ({device['ip']})")
        conn = ssh_client.invoke_shell()
        conn.send("conf t\n")
        conn.send("clock timezone AEST +10\n")
        conn.send("clock summer-time AEST recurring 1 0 2 10 2 0 2\n") # 設定夏令時間
        conn.send("end\n")
        time.sleep(2)  # 等待指令執行完成
        output = conn.recv(65535).decode('utf-8') # 取得設定結果
        print(f"{device['hostname']} 設定結果:\n{output}")
        ssh_client.close()
    except Exception as e:
        print(f"無法連線到 {device['hostname']} ({device['ip']}):{e}")


# 將裝置資訊轉換為 JSON 格式
json_devices = json.dumps(devices, indent=4)
print(f"\nJSON 格式的裝置資訊:\n{json_devices}")

這段程式碼使用迴圈迭代 devices 列表,利用 Paramiko 連線到每個裝置,並設定時間和時區。我加入了錯誤處理機制 try...except,以及 timeout 引數來避免程式卡住。此外,我使用 conn.recv() 接收並顯示設定結果,方便除錯。最後,程式碼將 devices 列表轉換為 JSON 格式字串並輸出。

  graph LR
    C[C]
    A[Python Script] --> B(Paramiko);
    B --> C{SSH Connection};
    C -- Success --> D[Configure Time/Timezone];
    C -- Fail --> E[Error Handling];

此流程圖描述了 Python 指令碼使用 Paramiko 連線到 SSH 伺服器的過程,包含成功設定時間和時區,以及連線失敗時的錯誤處理。

接下來,我將分享如何使用 NMAP 掃描 SSH 連線埠,並分析潛在的安全風險。

nmap -p 22 --script ssh-auth-methods,ssh2-enum-algos <目標主機>

這個 NMAP 指令結合了 ssh-auth-methodsssh2-enum-algos 兩個指令碼,一次性檢測 SSH 驗證方法和支援的演算法,提升效率。<目標主機> 應該替換成實際的 IP 位址或網域名稱。

最後,我將探討一個常見的 SSH 連線問題:「No matching cipher found」,並提供解決方案。這個錯誤訊息通常表示客戶端和伺服器端支援的加密演算法不比對。我的解決方案是調整伺服器端的 SSH 設定,使其支援更廣泛的加密演算法,或者調整客戶端使用的演算法。

  sequenceDiagram
    participant Client
    participant Server

    Client->>Server: Client Hello (Cipher Suites)
    Server->>Client: Server Hello (Chosen Cipher Suite)
    alt Cipher Suite Match
        Client->>Server: Key Exchange
        Server->>Client: Key Exchange
        Client->>Server: Encrypted Data
        Server->>Client: Encrypted Data
    else Cipher Suite Mismatch
        Server->>Client: Alert (No Matching Cipher)
        Client->>Server: Connection Closed
    end

這個序列圖更詳細地展示了 SSH 連線建立的流程,包含客戶端和伺服器端交換加密演算法的過程,以及當演算法不比對時發生的錯誤情況。

透過以上分享,我希望我的實務經驗能幫助大家更好地運用 Python 網路自動化技術,並提升 SSH 連線的安全性與穩定性。

在現代網路管理中,時間同步扮演著至關重要的角色,舉凡日誌記錄、安全機制、排程任務等,都仰賴精確的時間戳記。當網路裝置的時間不同步時,可能導致系統錯誤、安全漏洞,甚至影響營運效率。因此,有效管理和設定網路時間協定 (NTP) 伺服器,是確保網路穩定執行的關鍵任務。

我經常需要管理大量的網路裝置,手動設定每台裝置的 NTP 伺服器既費時又容易出錯。因此,我開發了一套 Python 自動化指令碼,可以快速與有效地完成這項工作。

確認 SSH 與 NTP 連線埠狀態

在開始之前,我習慣先確認目標裝置的 SSH (TCP 22) 和 NTP (UDP 123) 連線埠是否暢通。我會使用 nmap 進行掃描:

nmap -p 22 192.168.1.10 192.168.1.20 # 確認 SSH 連線埠
nmap -p 123 -sU 192.168.1.10 192.168.1.20 192.168.1.100 # 確認 NTP 連線埠 (-sU 代表 UDP)

若連線埠狀態顯示 closed,可能是設定有誤或防火牆阻擋。filtered 通常表示防火牆過濾了連線。

除了 nmap,我也會直接登入裝置,使用以下指令檢查:

show ip interface brief  # 顯示介面狀態和 IP 位址
show ntp status        # 顯示 NTP 狀態

NTP 伺服器狀態檢查與設定

確保 NTP 伺服器本身運作正常非常重要。以下是我常用的檢查和設定指令:

timedatectl status   # 檢查系統時間狀態
timedatectl set-ntp true # 啟用 NTP 同步
firewall-cmd --zone=public --add-port=123/udp --permanent # 開放防火牆 UDP 123 連線埠
firewall-cmd --reload  # 重新載入防火牆設定

Python 自動化指令碼:設定 NTP 伺服器

以下 Python 指令碼使用 paramiko 模組,透過 SSH 遠端設定網路裝置的 NTP 伺服器:

import paramiko
import time
import getpass  # 安全地取得密碼

username = input("請輸入使用者名稱:")
password = getpass.getpass("請輸入密碼:")
devices = ['192.168.1.10', '192.168.1.20']  # 裝置 IP 位址列表
ntp_server = '192.168.1.100'  # NTP 伺服器 IP 位址

client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

for ip in devices:
    try:
        client.connect(ip, username=username, password=password, look_for_keys=False, timeout=5)  # 設定連線逾時
        print(f"已連線至 {ip}")
        shell = client.invoke_shell()
        shell.send("configure terminal\n")
        shell.send(f"ntp server {ntp_server}\n")  # 設定 NTP 伺服器
        shell.send("end\n")
        shell.send("wr mem\n") # 儲存設定
        time.sleep(2)
        output = shell.recv(65535).decode()
        print(output)
        print("-" * 80)
        client.close()
    except paramiko.AuthenticationException:
        print(f"驗證失敗:{ip}")
    except paramiko.SSHException as e:
        print(f"SSH 連線錯誤:{ip} - {e}")
    except Exception as e:
        print(f"其他錯誤:{ip} - {e}")

此指令碼迭代處理 devices 列表中的每個 IP 位址。它使用 paramiko 建立 SSH 連線,並透過 invoke_shell() 建立互動式 shell,以便依序傳送指令。f-string 格式化字串,將 NTP 伺服器 IP 位址嵌入指令中。wr mem 指令儲存設定。try...except 塊處理各種潛在錯誤,例如驗證失敗、SSH 連線錯誤等,提升程式碼的穩健性。getpass 模組安全地取得密碼,避免在程式碼中直接寫入密碼。

  graph LR
    B[B]
    C[C]
    A[開始] --> B{取得使用者名稱和密碼};
    B --> C{迴圈處理每個裝置 IP};
    C --> D[建立 SSH 連線];
    D -- 成功 --> E[設定 NTP 伺服器];
    E --> F[儲存設定];
    F --> G[關閉連線];
    D -- 失敗 --> H[顯示錯誤訊息];
    H --> G;
    G --> I[結束];

**圖表説明:**此流程圖展示了程式碼的執行流程,從取得使用者認證到設定 NTP 伺服器,最後結束迴圈。

  sequenceDiagram
    participant 使用者
    participant Python 指令碼
    participant 網路裝置

    使用者->>Python 指令碼: 提供使用者名稱和密碼
    loop 每個裝置 IP
        Python 指令碼->>網路裝置: 建立 SSH 連線
        alt 連線成功
            Python 指令碼->>網路裝置: 設定 NTP 伺服器
            Python 指令碼->>網路裝置: 儲存設定
            網路裝置-->>Python 指令碼: 回傳設定結果
            Python 指令碼->>網路裝置: 關閉連線
        else 連線失敗
            網路裝置-->>Python 指令碼: 回傳錯誤訊息
        end
    end

**圖表説明:**此循序圖詳細描述了使用者、Python 指令碼和網路裝置之間的互動流程,更清晰地呈現了程式碼的執行步驟。

透過這個 Python 自動化指令碼,我可以輕鬆管理多台網路裝置的 NTP 設定,大幅提升了工作效率,同時也降低了人為錯誤的風險。

#!/usr/bin/env python3
import time
import paramiko
from datetime import datetime
from getpass import getpass

t_ref = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")

def get_credentials():
    """取得使用者認證資訊"""
    username = input("* 輸入網路管理員帳號 : ")
    password = None
    while not password:
        password = getpass("* 輸入網路管理員密碼 : ")
        password_verify = getpass("** 確認網路管理員密碼 : ")
        if password != password_verify:
            print("! 網路管理員密碼不符,請重新輸入。")
            password = None
    return username, password

def backup_config(device_ip, username, password):
    """備份裝置設定"""
    try:
        ssh_client = paramiko.SSHClient()
        ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh_client.connect(device_ip, username=username, password=password)
        shell = ssh_client.invoke_shell()
        shell.send("enable\n")  # 進入特權模式
        shell.send("terminal length 0\n") # 設定終端輸出長度為 0,避免分頁顯示
        time.sleep(1)
        shell.send("show running-config\n")
        time.sleep(5) # 等待指令執行完成
        output = shell.recv(65535).decode() # 接收設定檔內容
        ssh_client.close()

        # 將設定檔儲存到檔案
        filename = f"backup_{device_ip}_{t_ref}.cfg"
        with open(filename, "w") as f:
            f.write(output)
        print(f"{device_ip} 設定已備份至 {filename}。")

    except Exception as e:
        print(f"無法備份 {device_ip} 設定:{e}")


if __name__ == "__main__":
    username, password = get_credentials()
    device_ips = []
    while True:
        device_ip = input("輸入裝置 IP 地址 (或輸入 'done' 完成輸入): ")
        if device_ip.lower() == 'done':
            break
        device_ips.append(device_ip)

    for device_ip in device_ips:
        backup_config(device_ip, username, password)

    print("備份程式完成。")

這個程式碼整合了先前的使用者認證和計時工具,並新增了備份設定的功能。get_credentials() 函式用於取得使用者認證資訊。backup_config() 函式使用 paramiko 連線到裝置,執行 show running-config 指令,並將輸出儲存到檔案。主程式會先取得使用者認證,然後提示使用者輸入多個裝置 IP,最後逐一備份每個裝置的設定。加入了 enable 指令進入特權模式,以及terminal length 0 指令避免分頁顯示,確保完整備份設定檔。

  graph LR
    D[D]
    A[使用者輸入認證] --> B{驗證認證};
    B -- 成功 --> C[輸入裝置 IP];
    C --> D{SSH 連線到裝置};
    D -- 成功 --> E[執行 show running-config];
    E --> F[儲存設定檔];
    F --> G[備份完成];

**圖表説明:**此流程圖描述了互動式備份指令碼的執行流程,從使用者輸入認證資訊開始,到成功備份設定檔結束。

  sequenceDiagram
    participant User
    participant Script
    participant Device

    User->>Script: 輸入帳號密碼
    activate Script
    Script->>Script: 驗證帳號密碼
    Script->>User: 輸入裝置 IP
    User->>Script: 裝置 IP
    Script->>Device: SSH 連線
    activate Device
    Device-->>Script: 連線成功
    Script->>Device: 執行 show running-config
    Device-->>Script: 設定檔內容
    deactivate Device
    Script->>Script: 儲存設定檔
    deactivate Script
    Script-->>User: 備份完成

**圖表説明:**此序列圖詳細展示了使用者、指令碼和裝置之間的互動過程,更清晰地呈現了備份流程的每一步驟。

在現今複雜的網路環境中,維護大量網路裝置的設定檔至關重要。手動備份不僅耗時費力,還容易出錯。因此,自動化網路裝置備份成為提升效率和可靠性的關鍵。本文將以 Python 的 Paramiko 函式庫為例,示範如何開發一個自動化備份方案,簡化網路管理流程。

import time
import paramiko
from datetime import datetime
import getpass

# 取得目前時間戳記,用於備份檔案命名
t_ref = datetime.now().strftime("%Y%m%d%H%M%S")

# 裝置 IP 列表
device_list = ["192.168.1.1", "192.168.1.2", "192.168.1.3"]

# 使用者名稱和密碼
username = input("請輸入使用者名稱:")
password = getpass.getpass("請輸入密碼:")


def get_credentials():
    global username, password


start_timer = time.mktime(time.localtime())

get_credentials()

for ip in device_list:
    print(t_ref)
    print(f"正在登入 {ip}")
    ssh_client = paramiko.SSHClient()
    ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        ssh_client.connect(hostname=ip, username=username, password=password, look_for_keys=False)
        print(f"成功連線至 {ip}\n")

        print(f"正在備份 {ip} 的執行組態\n")
        remote_connection = ssh_client.invoke_shell()
        time.sleep(3)
        remote_connection.send("copy running-config tftp\n")
        remote_connection.send("192.168.127.10\n")  # TFTP 伺服器 IP
        remote_connection.send(ip + ".bak@" + t_ref + "\n")
        time.sleep(3)
        output = remote_connection.recv(65535)
        print(output.decode('ascii'))
        print(f"成功備份執行組態至 TFTP,並斷開與 {ip} 的連線\n")
        print("-" * 80)

    except paramiko.AuthenticationException:
        print(f"登入 {ip} 失敗:使用者名稱或密碼錯誤。")
    except paramiko.SSHException as sshException:
        print(f"SSH 連線至 {ip} 發生錯誤:{sshException}")
    except Exception as e:
        print(f"發生錯誤:{e}")
    finally:
        if ssh_client:
            ssh_client.close()
        time.sleep(1)

total_time = time.mktime(time.localtime()) - start_timer
print(f"總執行時間: {total_time} 秒")

這段程式碼的核心功能是利用 Paramiko 模組實作網路裝置的自動化備份。首先,程式碼取得當前時間戳記,用於建立獨一無二的備份檔名,避免覆寫先前的備份。接著,程式碼迭代裝置 IP 列表,針對每個 IP 地址建立 SSH 連線。連線成功後,程式碼會執行備份指令,將裝置的執行組態(running-config)複製到指定的 TFTP 伺服器。

我特別加入了 try...except...finally 區塊來處理潛在的錯誤,例如驗證失敗、SSH 連線錯誤等,提升程式碼的穩健性。此外,程式碼在備份完成後會關閉 SSH 連線,釋放資源。最後,程式碼計算並顯示整個備份過程的總執行時間,方便追蹤效能。

這個程式碼片段不僅展示瞭如何使用 Paramiko 進行 SSH 連線和執行指令,更體現了我在網路自動化方面的實務經驗,例如錯誤處理、時間戳記的使用以及程式碼結構的設計。透過這些技巧,可以有效提升網路管理效率,並確保備份流程的可靠性。

我認為,自動化備份是現代網路管理的根本。透過這個 Python 指令碼,我們可以輕鬆地將備份工作自動化,減少人工介入,降低錯誤風險,並提高整體網路的穩定性。更重要的是,這個指令碼可以根據實際需求進行調整,例如修改裝置 IP 列表、TFTP 伺服器地址等,使其更具彈性。