OpenSSH/食譜/自動備份
使用帶有金鑰的 OpenSSH 可以方便地進行安全的自動備份。 rsync(1)[1]、 tar(1) 和 dump(8) 是大多數備份方法的基礎。遠端 root 訪問必須允許的說法是錯誤的。如果需要 root 訪問許可權, sudo(8) 可以正常工作,或者在 zfs(8) 的情況下,使用 OpenZFS 委託系統。請記住,在備份資料經過測試並證明可以可靠地恢復之前,它不算是備份副本。
rsync(1) 通常用於本地和遠端備份。它速度快、靈活,並且以增量方式複製,因此只傳輸更改,從而避免浪費時間重新複製已存在於目標位置的內容。它透過使用其著名的演算法來做到這一點。在遠端工作時,它需要 SSH 的一點幫助,通常的做法是透過 SSH 建立隧道。
rsync(1) 實用程式現在預設使用 SSH,並且從 2004 年開始使用[2]。因此,以下命令透過 SSH 連線,無需新增任何額外內容
$ rsync -a fred@server.example.org:./archive/ \
/home/fred/archive/.
但是,如果必須將其他選項傳遞給 SSH 客戶端,則仍然可以顯式指定 SSH 的使用。
$ rsync -a -e 'ssh -v' \
fred@server.example.org:./archive/ \
/home/fred/archive/.
對於某些型別的資料,如果兩端的主機 CPU 能夠處理額外的工作,則可以使用 rsync(1) 壓縮(-z)來極大地加快傳輸速度。但是,它也可能降低速度。因此,壓縮是需要在實際環境中進行測試才能確定它是否能提高或降低速度。
由於 rsync(1) 預設使用 SSH,因此它甚至可以使用 SSH 金鑰進行身份驗證,方法是使用 -e 選項指定其他選項。這樣,就可以指定一個特定的 SSH 金鑰檔案,供 SSH 客戶端在建立連線時使用。
$ rsync --exclude '*~' -avv \
-e 'ssh -i ~/.ssh/key_bkup_rsa' \
fred@server.example.org:./archive/ \
/home/fred/archive/.
如果需要,也可以以相同的方式將其他配置選項傳送到 SSH 客戶端,或者透過 SSH 客戶端的配置檔案傳送。此外,如果先將金鑰新增到代理,則只需要輸入一次金鑰的密碼短語。在現代桌面環境中,這很容易在互動式會話中完成。在自動指令碼中,需要顯式地設定代理,並將顯式套接字名稱傳遞給指令碼,並透過 SSH_AUTH_SOCK 變數訪問。
有時,備份過程需要訪問除可以登入的帳戶以外的其他帳戶。該其他帳戶通常是 root,出於最小特權原則,它通常被拒絕透過 SSH 直接訪問。如果需要,rsync(1) 可以遠端呼叫 sudo(8)。
假設您正在從伺服器備份到客戶端。客戶端上的 rsync(1) 使用 ssh(1) 建立到伺服器上 rsync(1) 的連線。客戶端上使用 -v 選項呼叫 rsync(1),以便檢視傳遞給伺服器的具體引數。這些細節將需要用於將其整合到伺服器上 sudo(8) 的配置中。這裡,SSH 客戶端以單級更高的詳細程度執行,以便顯示必須使用的選項。
$ rsync \
-e 'ssh -v \
-i ~/.ssh/key_bkup_rsa \
-t \
-l bkupacct' \
--rsync-path='sudo rsync' \
--delete \
--archive \
--compress \
--verbose \
bkupacct@server:/var/www/ \
/media/backups/server/backup/
引數 --rsync-path 告訴伺服器使用什麼來代替 rsync(1)。在這種情況下,它執行 sudo rsync。引數 -e 用於指定要使用的遠端 shell 工具。在本例中,它指的是 ssh(1)。對於由 rsync(1) 客戶端呼叫的 SSH 客戶端,-i 用於明確指定要使用的金鑰。這與是否使用身份驗證代理無關。使用多個金鑰是可能的,因為可以為不同的任務使用不同的金鑰。
您可以透過在客戶端上以詳細模式(-v)執行 SSH 來查詢在 /etc/sudoers 中使用的確切設定。在處理模式時要小心,不要匹配超出安全範圍的內容。
調整這些設定很可能是一個迭代過程。在觀察詳細輸出時,繼續對伺服器上的 /etc/sudoers 進行更改,直到它按預期工作為止。最終,/etc/sudoers 將最終包含一行,允許 rsync(1) 以最少的選項執行。
以下示例基於從遠端系統獲取資料。也就是說,資料從遠端系統上的 /source/directory/ 複製到本地上的 /destination/directory/。但是,反方向的步驟也是一樣的,但是一些選項將被放置在不同的位置,並且將省略 --sender。無論哪種情況,以下示例中的複製貼上都無法正常工作。
準備:建立一個專門用於備份的帳戶,建立一個僅用於該帳戶的金鑰對,然後確保可以使用 ssh(1) 使用和不使用這些金鑰登入到該帳戶。
$ ssh -i ~/.ssh/key_bkup_ed25519 bkupacct@www.example.org
伺服器上的帳戶名為“bkupacct”,私鑰 Ed25519 為客戶端上的 ~/.ssh/key_bkup_ed25519。在伺服器上,帳戶“bkupacct”是“backups”組的成員。如有必要,請參閱 公鑰身份驗證 部分。
公鑰,~/.ssh/key_bkup_ed25519.pub,必須複製到遠端系統上的帳戶“bkupacct”中,並放置在~/.ssh/authorized_keys中的正確位置。然後,需要確保伺服器上的以下目錄由 root 擁有,屬於“backups”組,並且可讀,但不可寫,並且絕對不可被世界讀取:~ 和 ~/.ssh/。該檔案~/.ssh/authorized_keys 也一樣(這還假設您沒有使用 ACL)。但是,這只是在遠端系統上設定許可權的眾多方法之一。
$ sudo chown root:bkupacct ~
$ sudo chown root:bkupacct ~/.ssh/
$ sudo chown root:bkupacct ~/.ssh/authorized_keys
$ sudo chmod u=rwx,g=rx,o= ~
$ sudo chmod u=rwx,g=rx,o= ~/.ssh/
$ sudo chmod u=rwx,g=r,o= ~/.ssh/authorized_keys
現在可以開始配置了。
步驟 1:配置 sudoers(5),以便 rsync(1) 可以與遠端主機上的 sudo(8) 一起工作。在這種情況下,資料將保留在遠端機器上。為了找到並設定稍後鎖定這些資料的特定選項,'backups' 組將臨時需要完全訪問許可權。
%backups ALL=(root:root) NOPASSWD: /usr/bin/rsync
這是一個過渡步驟,重要的是,這行不應長時間保持原樣。
但是,當它就位時,請確保 rsync(1) 可以與 sudo(8) 一起工作,方法是使用--rsync-path 選項進行測試。
$ rsync --rsync-path='sudo rsync' \
-aHv bkupacct@www.example.org:/source/directotry/ /destination/directory/
傳輸應該在沒有錯誤、警告或額外密碼輸入的情況下執行。
步驟 2:接下來,再次進行相同的傳輸,但使用金鑰進行身份驗證,以確保兩者可以一起使用。
$ rsync -e 'ssh -i ~/.ssh/key_bkup_ed25519' --rsync-path='sudo rsync' \
-aHv bkupacct@www.example.org:/source/directory/ /destination/directory/
同樣,如有必要,請參閱有關 公鑰身份驗證 的部分。
步驟 3:現在收集連線詳細資訊。它們是調整 sudoers(5) 的必要條件。
$ rsync -e 'ssh -E ssh.log -v -i ~/.ssh/key_bkup_ed25519' \
--rsync-path='sudo rsync' \
-aHv bkupacct@www.example.org:/source/directory/ /destination/directory/
$ grep -i 'sending command' ssh.log
第二個命令,即帶有 grep(1) 的命令,應該生成類似以下內容的結果
debug1: Sending command: rsync --server --sender -vlHogDtpre.iLsfxCIvu . /source/directory/
請注意字母的長字串和目錄,因為它們將用於稍微調整 sudoers(5)。請記住,在這些示例中,資料將從遠端機器上的/source/directory/ 複製到本地的/destination/directory/。
以下是與上述公式匹配的設定,假設帳戶在 backups 組中
%backups ALL=(root:root) NOPASSWD: /usr/bin/rsync --server --sender -vlHogDtpre.iLsfxCIvu . /source/directory/
該行調整了 sudoers(5),以便備份帳戶擁有足夠的許可權以 root 身份執行 rsync(1),但僅限於它應該執行的目錄中,並且不能在系統上隨意使用。
以後可能會進行更多改進,但這些是鎖定 sudoers(5) 的基礎。此時您幾乎完成了,儘管該過程可以進一步自動化。請確保備份資料在本地儲存後無法被其他人訪問。
步驟 4:使用 sudo(8) 透過 ssh(1) 測試 rsync(1),以驗證在 sudoers(5) 中進行的設定是否正確。
$ rsync -e 'ssh -i ~/.ssh/key_bkup_ed25519' --rsync-path='sudo rsync' \
-aHv bkupacct@www.example.org:/source/directory/ /destination/directory/
備份應該在此階段正常執行。
步驟 5:最後,可以透過在authorized_keys 檔案中使用command="..." 選項,在限制前面新增限制,從而將該金鑰鎖定到一項任務中。對此的解釋可以在 sshd(8) 中找到。
command="/usr/bin/rsync --server --sender -vlHogDtpre.iLsfxCIvu . ./source/directory" ssh-ed25519 AAAAC3Nz...aWi
此後,該金鑰僅用於備份。它是在 sudoers(5) 檔案中已進行的設定的基礎上增加的一層。
因此,您可以使用 rsync(1) 進行自動化遠端備份,並具有 root 級別訪問許可權,同時避免遠端 root 登入。儘管如此,請密切注意私鑰,因為它仍然可以用來獲取遠端備份,而遠端備份可能包含敏感資訊。
從頭到尾,該過程需要高度重視細節,但如果一步一步地進行,則很容易完成。設定從本地到遠端方向的備份非常相似。當從本地到遠端傳輸時,---sender 選項將被省略,並且目錄將不同。
Rsync 協議的其他實現
[edit | edit source]openrsync(1) 是 samba.org 實現的 rsync(1) 所支援的 Rsync 協議版本 27 的純淨室重新實現[3]。它已包含在 OpenBSD 版本 6.5 之後的 OpenBSD 基本系統中。它使用不同的名稱呼叫,因此,如果它在遠端系統上,而 samba.org 的 rsync(1) 在本地系統上,則--rsync-path 選項必須按名稱指向它。
$ rsync -a -v -e 'ssh -i key_rsa' \
--rsync-path=/usr/bin/openrsync \
fred@server.example.org:/var/www/ \
/home/fred/www/
反方向操作,從 openrsync(1) 開始連線到遠端系統上的 rsync(1),不需要進行任何調整。
使用 tar(1) 進行備份
[edit | edit source]建立存檔的常見選擇是 tar(1)。但由於它複製整個檔案和目錄,因此 rsync(1) 通常對於更新或增量備份來說效率更高。
以下將建立/var/www/ 目錄的 tar 包,並透過本地機器上的 stdout 傳送到遠端機器上的sdtin(透過管道傳輸到 ssh(1)),然後將其定向到名為backup.tar 的檔案。在這裡,tar(1) 在本地機器上執行,並將 tar 包儲存在遠端位置。
$ tar cf - /var/www/ | ssh -l fred server.example.org 'cat > backup.tar'
該配方几乎有無限的變體。
$ tar zcf - /var/www/ /home/*/www/ \
| ssh -l fred server.example.org 'cat > $(date +"%Y-%m-%d").tar.gz'
該示例執行相同操作,但也獲取使用者 WWW 目錄,使用 gzip(1) 壓縮 tar 包,並根據當前日期為結果檔案標記標籤。也可以使用金鑰執行此操作。
$ tar zcf - /var/www/ /home/*/www/ \
| ssh -i key_rsa -l fred server.example.org 'cat > $(date +"%Y-%m-%d").tgz'
對於 tar(1) 來說,從另一個方向獲取遠端機器上的內容並存儲在本地也很容易。
$ ssh fred@server.example.org 'tar zcf - /var/www/' > backup.tgz
或者,以下是一個在遠端機器上執行 tar(1) 但將 tar 包儲存在本地的更復雜的示例。
$ ssh -i key_rsa -l fred server.example.org 'tar jcf - /var/www/ /home/*/www/' \
> $(date +"%Y-%m-%d").tar.bz2
總之,使用 tar(1) 進行備份的秘訣是使用stdout 和stdin 透過管道和重定向進行傳輸。
使用 tar(1) 備份檔案,但不建立 tar 包
[edit | edit source]有時需要直接傳輸檔案和目錄,而不在目標位置建立 tar 包。除了寫入源機器上的stdin 之外,tar(1) 還可以從目標機器上的stdin 讀取,以一次傳輸整個目錄層次結構。
$ tar zcf - /var/www/ | ssh -l fred server.example.org "cd /some/path/; tar zxf -"
或者反方向操作,將是以下情況。
$ ssh 'tar zcf - /var/www/' | (cd /some/path/; tar zxf - )
但是,這些操作在每次執行時仍然會複製所有內容。因此,上述部分中描述的 rsync(1) 在許多情況下可能是更好的選擇,因為它在後續執行時僅複製更改的內容。此外,根據資料型別、網路條件和可用的 CPU,壓縮可能是使用 tar(1) 或 ssh(1) 本身進行壓縮的好方法。
使用 dump 進行備份
[edit | edit source]使用 dump(8) 遠端操作類似於使用 tar(1)。可以從遠端伺服器複製到本地伺服器。
$ ssh -t source.example.org 'sudo dump -0an -f - /var/www/ | gzip -c9' > backup.dump.gz
請注意,sudo(8) 的密碼提示可能不可見,並且必須盲目輸入。
或者可以反方向操作,從本地伺服器複製到遠端伺服器
$ sudo dump -0an -f - /var/www/ | gzip -c9 | ssh target.example.org 'cat > backup.dump.gz'
再次注意,密碼提示可能隱藏在 dump(8) 的初始輸出中。但是,即使不可見,它仍然存在。
使用 zfs(8) 快照進行備份
[edit | edit source]OpenZFS 可以輕鬆地建立完整或增量快照,這是寫時複製的便利副產品。這些快照可以透過 SSH 傳送到另一個系統或從另一個系統傳送。這種方法對於備份或恢復資料同樣有效。但是,頻寬是一個需要考慮的因素,快照的大小必須適合所討論的實際網路連線。OpenZFS 支援壓縮複製,以便磁碟上已壓縮的塊在傳輸過程中保持壓縮狀態,從而減少了使用其他程序重新壓縮的需要。傳輸可以是到常規檔案或另一個 OpenZFS 檔案系統,也可以是從這些檔案系統。這應該是顯而易見的,但重要的是要記住,較小的快照使用較少的頻寬,因此比較大的快照傳輸速度更快。
首先需要完整快照,因為增量快照只包含部分資料,並且需要它們形成的基礎存在。以下使用 zfs(8) 為名為 web 的池中名為 site01 的資料集建立名為 20210326 的快照。
$ zfs snapshot -r web/site01@20210326
程式本身很可能位於 /sbin/ 目錄中,要麼 PATH 環境變數需要包含它,要麼應該使用絕對路徑。隨後可以使用 -i 選項在初始完整快照的基礎上構建增量快照。但是,OpenZFS 管理的來龍去脈超出了本書的範圍。這裡將只檢查兩種系統間傳輸方法。一種方法是使用中間檔案,另一種方法是使用管道更直接。兩者都使用 zfs send 和 zfs receive,並且所涉及的帳戶必須在 OpenZFS 委託系統中具有正確的許可權。對於傳送,它將是相關 OpenZFS 池的 send 和 snapshot。對於接收,它將是相關池的 create、mount 和 receive。
可以將快照透過 SSH 傳輸到本地或遠端系統上的檔案。這種方法不需要在任一系統上擁有特權訪問許可權,但執行 zfs 的帳戶必須具有由 zfs allow 授予的正確的內部 OpenZFS 許可權。這裡從遠端系統下載一個非常小的快照到本地檔案
$ ssh fred@server.example.org '/sbin/zfs send -v web/site01@20210326' > site01.openzfs
full send of web/site01@20210326 estimated size is 1.72M
total estimated size is 1.72M
如果複製增量快照,則需要複製它們所基於的完整快照。因此,應注意確保這是一個完整快照,而不僅僅是增量快照。
稍後,恢復快照是進行相反方向的問題。在這種情況下,資料將從檔案檢索並透過 SSH 傳送到 zfs(8)。
$ cat site01.openzfs | ssh fred@server.example.org '/sbin/zfs receive -v -F web/site01@20210326'
receiving full stream of web/site01@20210326 into web/site01@20210326
received 1.85M stream in 6 seconds (316K/sec)
這是可能的,因為在執行時直接呼叫程式時,通道在沒有 PTY 的情況下啟動時是 8 位乾淨的。請注意,目標 OpenZFS 資料集必須首先使用 zfs(8) 解除安裝。然後在傳輸後必須重新掛載。
從本地系統傳輸到遠端系統是更改元件順序的問題。
$ /sbin/zfs send -v web/site01@20210326 | ssh fred@server.example.org 'cat > site01.openzfs'
full send of web/site01@20210326 estimated size is 1.72M
total estimated size is 1.72M
然後需要類似的更改才能從遠端系統恢復到本地系統。
$ ssh fred@server.example.org 'cat site01.openzfs' | /sbin/zfs receive -v -F web/site01@20210326'
receiving full stream of web/site01@20210326 into web/site01@20210326
received 1.85M stream in 6 seconds (316K/sec)
像往常一樣,為了避免在這些活動中使用 root 帳戶,執行 zfs(8) 的帳戶必須在 OpenZFS 委託系統中具有正確的訪問級別。
或者,可以將該快照透過 SSH 傳輸到遠端計算機上的檔案系統。這種方法需要特權訪問許可權,並將不可撤銷地替換自快照以來在遠端系統上進行的任何更改。
$ zfs send -v pool/www@20210322 | ssh fred@server.example.org 'zfs receive -F pool/www@20210322'
因此,如果在遠端系統上使用可行動硬碟驅動器,則這可以更新它們。
$ ssh fred@server.example.org 'zfs send -v pool/www@20210322' | zfs receive -F pool/www@20210322
同樣,遠端帳戶必須已經獲得必要的內部 ZFS 許可權。
同樣,要從遠端系統到本地系統進行另一個方向,需要更改元件的順序。
$ ssh fred@server.example.org 'zfs send -v pool/www@20210322' | zfs receive -F pool/www@20210322
並且,
$ zfs send -v pool/www@20210322 | ssh fred@server.example.org 'zfs receive -F pool/www@20210322'
同樣,使用 OpenZFS 委託系統可以避免在傳輸的任一端都需要 root 訪問許可權。
有時 CPU 和網路會在檔案傳輸期間交替成為瓶頸。mbuffer(1) 實用程式可以允許穩定的資料流 [4],即使 CPU 超過網路。關鍵是要留出足夠大的緩衝區,以便即使 CPU 趕上來,也總有一些資料在網路上傳輸。
$ cat site01.zfs | mbuffer -s 128k -m 1G \
| ssh fred@server.example.org 'mbuffer -s 128k -m 1G | /sbin/zfs receive -v -F web/site01'
summary: 1896 kiByte in 0.2sec - average of 7959 kiB/s
receiving full stream of web/site01@20210326 into web/site01@20210326
in @ 2556 kiB/s, out @ 1460 kiB/s, 1024 kiB total, buffer 0% full
summary: 1896 kiByte in 0.8sec - average of 2514 kiB/s
received 1.85M stream in 2 seconds (948K/sec)
有關使用 OpenZFS 和管理其快照的更多詳細資訊超出了本書的範圍。實際上,有關於 OpenZFS 的完整指南、教程,甚至書籍。
- ↑ "Rsync 工作原理". Samba.
- ↑ "Rsync 2.6.0 的新聞 (2004 年 1 月 1 日)". Samba. 2004-01-01. 檢索於 2020-05-02.
- ↑ "openrsync 匯入到樹中". Undeadly. 2019-02-11. 檢索於 2020-05-10.
- ↑ Dan Langille (2014-05-03). "在 FreeBSD 上透過 SSH 使用 mbuffer 傳送 zfs". 檢索於 2020-05-22.