内容


在 Linux 上进行自动备份

轻松进行自主、安全、分布式网络备份

如果您使用 Linux,那么您就可以使用创建定制备份解决方案的极其强大的工具。本文中的解决方案可以让您使用开源工具执行从简单的到更高级而且安全的网络备份,几乎所有 Linux 发行版本都包含这些工具。

简单备份

本文采取循序渐进的方法。只要遵循这些基本步骤,该方法就会非常直观。

在学习更加先进的分布式备份解决方案之前,我们先看一个简单但功能强大的归档机制。我们查看一个名为 arc 的简单脚本,该脚本支持从 Linux 的 shell 提示符创建备份快照。

清单 1: arc 的 shell 脚本
   #!/bin/sh
   tar czvf $1.$(date +%Y%m%d-%H%M%S).tgz $1
   exit $?

arc 脚本接受单个文件或目录名作为参数,创建压缩的归档文件,并将当前日期嵌入到生成的归档文件名中。例如,如果存在一个名为 beoserver 的目录,那么可以调用 arc 脚本并将 beoserver 目录名传递给该脚本来创建压缩的归档文件,比如 beoserver.20040321-014844.tgz。

使用命令 date 嵌入日期和时间戳有助于组织归档文件。日期格式是年、月、日、小时、分钟和秒 —— 但是秒字段的 使用可能有点多余。查阅 date 命令的手册(man date)可以了解该命令的其他选项。而且在清单 1 中,选项 -v (verbose) 被传递给 tar。该选项指示 tar 显示所有正在归档的文件。如果希望备份过程静默进行,那么请去除选项 -v

清单 2:将 beoserver 目录归档
   $ ls
   arc  beoserver
   $ ./arc beoserver
   beoserver/
   beoserver/bookl.dat
   beoserver/beoserver_ab_off
   beoserver/beoserver_ab_on
   $ ls
   arc  beoserver  beoserver.20040321-014844.tgz

高级备份

上面的简单备份示例很有用;但是它仍然包含一个手动备份过程。行业最佳实践建议经常进行备份,并备份到位于多个地理位置的多种介质上。其中心思想是避免完全依赖单一存储介质和单一存储地点。

我们将在下一示例中解决这个挑战,分析一个虚构的分布式网络(如图 1 所示),它将展示一位系统管理员能够访问两台远程服务器和一台离线数据存储服务器。

图 1. 分布式网络
图 1. 分布式网络
图 1. 分布式网络

服务器 #1 和 #2 的备份文件可以安全地传输给离线存储服务器,且整个分布式备份过程将会在无人干预的情况下定期进行。我们将使用一组标准工具,包括部分 Open Secure Shell(OpenSSH)工具,还有磁带归档工具(tar)和 cron 任务调度服务。我们的整个计划是将 cron 用于调度,将 shell 编程和 tar 应用程序用于备份过程,将 OpenSSH 安全 shell 加密用于远程访问、身份验证和安全 shell 复制(scp)以自动化文件传输。有关这些工具的更多信息,请务必阅读它们的手册。

使用公钥或私钥的安全远程访问

在数字安全语境中,密钥是一段数据,用于对其他数据进行加密或解密。公钥与私钥方案非常有趣,因为使用公钥加密的数据只能由相关私钥进行解密。您可以随便发布公钥,这样其他人就可以对要发送给您的数据进行加密。公钥与私钥方案给数字安全带来变革的原因之一是数据的发送方和接收方不必使用公共的密码。此外,公钥与私钥加密推动了电子商务和其他安全事务的发展。在本文中,我们将创建和使用公钥与私钥,以创建一种高度安全的分布式备份解决方案。

备份过程使用的每台机器都必须运行 OpenSSH 安全 shell 服务(sshd),同时让 22 端口可以通过任何内部防火墙被访问。如果您访问远程的服务器,那么很有可能您正在使用安全 shell。

我们的目标将是,不需要人工提供密码就可以安全地访问机器。一些人认为最简单的办法是设置无密码的访问:不要这样做。这样做不安全。不用那样,本文中我们将使用的方法可能会占用您大约一个小时的时间,建立起一个与使用 “无密码” 帐号同样方便的系统 —— 不过它是公认非常安全的。

我们首先确保 OpenSSH 已经安装,接下来检查它的版本号。完成本文时,最新的发行的 OpenSSH 是 2004 年 2 月 24 日发布的版本 3.8。您应该考虑使用一个较新的而且稳定的发布版本,至少所用的版本应该要比版本 2.x 新。访问 OpenSSH Security 网页以获得关于特定旧版本的缺陷的细节(请参阅本文后面的 参考资料 中的链接)。到目前为止,OpenSSH 是非常稳定的,而且已经证明不存在其他 SSH 工具所报告的很多缺陷。

在 shell 命令提示符中,键入 ssh 并附带大写的 V 选项来检查版本号:

$ ssh -V
OpenSSH_3.5p1, SSH protocols 1.5/2.0, OpenSSL 0x0090701f

如果 ssh 返回的版本号高于 2.x,那么该机器的软件配置相对较好。但是,本文推荐全部软件使用最新稳定版,这对于与安全相关的软件尤其重要。

首先,我们使用能够访问服务器 #1 和 #2 的帐号登录到离线存储服务器(参见图 1)。

$ ssh accountname@somedomain.com

登录到离线存储服务器之后,使用 ssh-keygen 程序和 -t dsa 选项创建一个公/私钥对。-t 选项必不可少,它用于指定想要生成的加密密钥的类型。我们将使用数字签名算法(DSA),该算法支持使用较新的 SSH2 协议。有关详细内容,请参见 ssh-keygen 手册。

在 ssh-keygen 的执行过程中,将会提示您指定存储 ssh 密钥的位置,然后要求您输入密码短语(passphrase)。当 ssh-keygen 程序询问 ssh 密钥的存储位置时只需按回车键,该程序将创建一个名为 .ssh 的隐藏目录(如果该目录尚未创建)并附带两个文件,分别是公钥文件和私钥文件。

ssh-keygen 的一个有趣特性是,您可以在提示输入密码短语时直接按回车键。如果您不提供密码短语,那么 ssh-keygen 会生成一个未加密的密钥。可以想象这不是一个好想法。要求输入密码短语时,应当确保输入的密码短语具有合理的长度,包含字符和数字,不要使用简单字串。

清单 3. 始终选择好的密码短语
   [offsite]:$ ssh-keygen -t dsa
   Generating public/private dsa key pair.
   Enter file in which to save the key (/home/accountname/.ssh/id_dsa):
   Enter passphrase (empty for no passphrase): (enter passphrase)
   Enter same passphrase again: (enter passphrase)
   Your identification has been saved in /home/accountname/.ssh/id_dsa.
   Your public key has been saved in /home/accountname/.ssh/id_dsa.pub.
   The key fingerprint is:
   7e:5e:b2:f2:d4:54:58:6a:fa:6b:52:9c:da:a8:53:1b accountname@offsite

因为 ssh-keygen 创建的 .ssh 目录是一个隐藏的 “点” 目录,所以为了查看这个新创建的目录,需要将 -a 选项传递给 ls 命令:

[offsite]$ ls -a
. .. .bash_logout .bash_profile .bashrc .emacs .gtkrc .ssh

输入隐藏的 .ssh 目录并列出其内容:

[offsite]$ cd .ssh
[offsite]$ ls -lrt
id_dsa id_dsa.pub

现在,我们在隐藏的 .ssh 目录内有一个私钥(id_dsa)和一个公钥(id_dsa.pub)。要检查密钥文件的内容,可以使用诸如 vi、emacs 等文本编辑器,也可以使用 less 或 cat 命令。您将会发现密钥文件的内容由使用 base64 编码的字母和数字组成。

下面,需要复制公钥并将其安装到服务器 #1 和 #2 上。不要使用 ftp,而应使用安全的复制程序将公钥传输到每个远程机器上:

清单 4. 将公钥安装到远程服务器上
   [offsite]$ scp .ssh/id_dsa.pub accountname@server1.com:offsite.pub
   accountname@server1.com's password: (enter password, not new
   passphrase!)
   id_dsa.pub 100% |*****************************| 614 00:00

   [offsite]$ scp .ssh/id_dsa.pub accountname@server2.com:offsite.pub
   accountname@server2.com's password: (enter password, not new
   passphrase!)
   id_dsa.pub 100% |*****************************| 614 00:00

新公钥安装之后,我们可以使用在创建私钥和公钥时指定的密码短语登录到每个机器上。现在,登录到每个机器并将 offsite.pub 文件的内容附加到名为 authorized_keys 文件中,该文件存储在每个远程机器的 .ssh 目录中。我们可以使用文本编辑器或 cat 命令将非现场 offsite.pub 文件的内容附加到 authorized_keys 文件:

清单 5. 将 offsite.pub 添加到授权密钥列表
   [offsite]$ ssh accountname@server1.com
   accountname@server1.com's password: (enter password, not new
   passphrase!)
   [server1]$ cat offsite.pub >> ./ssh/authorized_keys

下面添加一些额外的安全性。首先我们更改 .ssh 目录的访问权,只允许所有者拥有读取、写入和执行的权限。其次,确保 authorized_keys 文件只能被其所有者访问。最后,删除以前上传的 offsite.pub 密钥文件,因为现在已不需要它了。确保正确设置访问权限非常重要,因为 OpenSSH 服务器可能拒绝使用具有不安全的访问权的密钥。

清单 6. 使用 chmod 更改访问权限
   [server1]$ chmod 700 .ssh
   [server1]$ chmod 600 ./ssh/authorized_keys
   [server1]$ rm offsite.pub
   [server1]$ exit

在服务器 #2 上完成相同的过程之后,我们就可以返回到离线存储服务器来测试新的密码短语类型的访问。在离线服务器上,您可以键入如下内容:

[offsite]$ ssh -v accountname@server1.com

在检验您的帐户现在能够使用新的密码短语而不是旧密码来访问远程服务器时,使用 -v 或 verbose 标志选项来显示调试信息。除了能够让您从高层次了解认证过程的工作原理之外,调试输出还能够显示通过其他方式可能无法看到的重要信息。在后续连接中不必指定 -v 标志,但该选项对测试连接颇为有用。

使用 ssh-agent 自动化机器访问

ssh-agent 程序如同一个看门人,它根据需要安全地提供对安全密钥的访问。ssh-agent 启动之后在后台运行,并且可以由 ssh、scp 程序等其他 OpenSSH 应用程序访问。它允许 ssh 程序请求已解密密钥,而不是每次需要时都要求您提供私钥的密码短语。

让我们进一步研究一下 ssh-agent。当 ssh-agent 运行时,它会输出 shell 命令:

清单 7:运行中的 ssh-agent
   [offsite]$ ssh-agent
   SSH_AUTH_SOCK=/tmp/ssh-XX1O24LS/agent.14179; export SSH_AUTH_SOCK;
   SSH_AGENT_PID=14180; export SSH_AGENT_PID;
   echo Agent pid 14180;

使用 eval 命令可以指示 shell 执行 ssh-agent 显示的输出命令:

[offsite]$ eval `ssh-agent`
Agent pid 14198

eval 命令告诉 shell 计算(执行)ssh-agent 程序生成的命令。请确保使用的是反引号(`)而不是单引号。执行 eval `ssh-agent` 语句之后,它会返回代理的过程标识符。shell 变量 SSH_AUTH_SOCKSSH_AGENT_PID 已经在后台导出并可以使用。可以将它们显示到 shell 控制台以查看它们的值:

[offsite]$ echo $SSH_AUTH_SOCK
/tmp/ssh-XX7bhIwq/agent.14197

$SSH_AUTH_SOCK(即 SSH Authentication Socket 的缩写)是一个本地套接字的位置,应用程序使用它与 ssh-agent 通信。为了确保变量 SSH_AUTH_SOCKSSH_AGENT_PID 始终被注册,在您的 ~/.bash_profile 中输入语句 eval `ssh-agent`

现在,ssh-agent 已经成为后台进程,可以使用命令 topps 查看。

现在,我们可以将密码短语与 ssh-agent 共享了。为此,必须使用程序 ssh-add 将密码短语添加(发送)至运行中的 ssh-agent 程序。

清单 8. 用于免密码登录的 ssh-add
   [offsite]$ ssh-add
   Enter passphrase for /home/accountname/.ssh/id_dsa: (enter passphrase)
   Identity added: /home/accountname/.ssh/id_dsa
   (/home/accountname/.ssh/id_dsa)

现在,当我们访问服务器 #1 时,不会被要求输入密码:

[offsite]$ ssh accountname@server1.com
[server1]$ exit

如果还不确信,那么可以尝试删除(kill -9)ssh-agent 进程并重新连接服务器 #1。这次您将会发现,服务器 #1 将请求存储在 .ssh 目录下 id_dsa 文件中的私钥的密码短语:

[offsite]$ kill -9 $SSH_AGENT_PID
[offsite]$ ssh accountname@server1.com
Enter passphrase for key '/home/accountname/.ssh/id_dsa':

使用 keychain 简化密钥访问

迄今为止,我们已经学习了几个 OpenSSH 程序(ssh、scp、ssh-agent 和 ssh-add),而且我们已经创建并安装了私钥与公钥来支持安全、自动化登录过程。您可能已经注意到,本文的大多数设置工作只需做一次。例如,创建密钥、安装密钥、通过 .bash_profile 执行 ssh-agent 的过程在每台机器只需要进行一次。这真是好消息。

不太理想的消息是,每次登录离线服务器的时候都必须调用 ssh-add,而且,ssh-agent 与我们将要用来自动化备份工作的 cron 调度进程并不直接兼容。cron 进程不能与 ssh-agent 通信的原因是,cron 作业是作为 cron 的子进程来执行,这样它们就不会继承 $SSH_AUTH_SOCK

幸运的是,有一个解决方案不仅消除了有关 ssh-agent 和 ssh-add 的限制,而且可以让我们使用 cron 来自动进行各种需要对其他机器进行安全地无密码访问的过程。Daniel Robbins 于 2001 年在 developerWorks 发表了一个包含三部分的系列 OpenSSH key management(参阅 参考资料 中的链接),其中提供了一个称为 keychain 的 shell 脚本。它是 ssh-add 和 ssh-agent 的前端,可以简化整个无密码过程。从那时起,这个 keychain 脚本经过了多次改进,现在由 Aron Griffis 维护,目前最新版本是 2004 年 6 月 17 日发布的 2.3.2-1 版本。

keychain shell 脚本太大了,本文不方便列出来,因为这个精心编写的脚本中包括了很多错误检测、丰富的文档以及非常多的跨平台代码。不过,keychain 可以从项目的 Web 站点上方便地下载得到(参见 参考资料 中的相关链接)。

在下载并安装之后,keychain 脚本使用起来非常简便。只需登录每台机器并向 .bash_profile 文件添加下面两行代码:

keychain id_dsa
. ~/.keychain/$HOSTNAME-sh

在您第一次重新登录到每台机器时,keychain 将要求您输入密码短语。不过,除非机器被重新启动,否则,以后再登录时,keychain 将不再要求您重新输入密码短语。最好的是,cron 任务现在可以使用 OpenSSH 命令来安全地访问远程计算机,而不需要交互地使用密码短语。现在我们能够同时获得更好的安全性和易用性。

清单 9. 在每台计算机上初始化 keychain
   KeyChain 2.3.2; http://www.gentoo.org/projects/keychain
   Copyright 2002-2004 Gentoo Technologies, Inc.; Distributed under the
   GPL

   * Initializing /home/accountname/.keychain/localhost.localdomain-sh
   file...
   * Initializing /home/accountname/.keychain/localhost.localdomain-csh
   file...
   * Starting ssh-agent
   * Adding 1 key(s)...
   Enter passphrase for /home/accountname/.ssh/id_dsa: (enter passphrase)

脚本化备份过程

本文的下一个任务是创建 shell 脚本来执行必要的备份过程。它的目标是对服务器 #1 和 #2 进行完全的数据库备份。在本文的示例中,各服务器都运行 MySQL 数据库服务器,我们将使用 mysqldump 命令行实用工具将一些数据库表导出到一个 SQL 导入文件中。

清单 10. 服务器 #1 的 dbbackup.sh shell 脚本
   #!/bin/sh

   # change into the backup_agent directory where data files are stored.
   cd /home/backup_agent

   # use mysqldump utility to export the sites database tables
   mysqldump -u sitedb -pG0oDP@sswrd --add-drop-table sitedb --tables
   tbl_ccode tbl_machine tbl_session tbl_stats > userdb.sql

   # compress and archive
   tar czf userdb.tgz userdb.sql

在服务器 #2 上,我们将设置一个类似的脚本来备份站点数据库中给出的独有表单。每个脚本都通过下面的步骤标记为可执行:

[server1]:$ chmod +x dbbackup.sh

在服务器 #1 和 #2 上都有了 dbbackup.sh 文件之后,我们返回到离线数据服务器。在那里,我们将创建一个 shell 脚本来调用各个远程 dbbackup.sh 脚本,然后传输压缩的(.tgz)数据文件。

清单 11. 离线数据服务器上使用的 shell 脚本 backup_remote_servers.sh
   #!/bin/sh

   # use ssh to remotely execute the dbbackup.sh script on server 1
   /usr/bin/ssh backup_agent@server1.com "/home/backup_agent/dbbackup.sh"

   # use scp to securely copy the newly archived userdb.tgz file
   # from server 1.  Note the use of the date command to timestamp
   # the file on the offsite data server.
   /usr/bin/scp backup_agent@server1.com:/home/backup_agent/userdb.tgz
   /home/backups/userdb-$(date +%Y%m%d-%H%M%S).tgz

   # execute dbbackup.sh on server 2
   /usr/bin/ssh backup_agent@server2.com "/home/backup_agent/dbbackup.sh"

   # use scp to transfer transdb.tgz to offsite server.
   /usr/bin/scp backup_agent@server2.com:/home/backup_agent/transdb.tgz
   /home/backups/transdb-$(date +%Y%m%d-%H%M%S).tgz

shell 脚本 backup_remote_servers.sh 使用 ssh 命令执行远程服务器上的脚本。因为我们已经设置了无密码访问,所以 ssh 命令能够从离线服务器执行服务器 #1 和 #2 上的命令。现在,整个身份验证过程都是自动的,这归功于 keychain。

调度

本文的下一个也是最后一个任务是,调度离线数据存储服务器上的 shell 脚本 backup_remote_servers.sh 的执行。我们将给 cron 调度服务器添加两个条目,以要求每天对脚本备份两次,一次在凌晨 3:34,另一次在晚上 8:34。在离线服务器上使用编辑选项(-e)调用 crontab 程序。

[offsite]:$ crontab -e

crontab 调用默认编辑器。默认编辑器使用 shell 环境变量 VISUALEDITOR 来指定。下面键入两个条目,保存并关闭该文件。

清单 12. 离线服务器上的 crontab 条目
   34 3 * * * /home/backups/remote_db_backup.sh
   34 20 * * * /home/backups/remote_db_backup.sh

一个 crontab 行包括两个主要部分,即时间调度部分及其后接的命令部分。时间调度被划分为字段,用于说明命令应该在何时执行:

清单 13. Crontab 格式
          +---- minute
          | +----- hour
          | | +------ day of the month
          | | | +------ month
          | | | | +---- day of the week
          | | | | | +-- command to execute
          | | | | | |
         34 3 * * * /home/backups/remote_db_backup.sh

检验您的备份

应该对备份进行例行检查,以保证备份过程工作正常。自动备份过程能够去除不必要的枯燥工作,但是不应因此而变得懒惰。如果数据值得备份,那么它也值得您时常进行抽样检查。

考虑添加 cron 作业来提醒自己每月至少检查一次备份。此外,每隔一段时间修改一次安全密钥也是个好主意,也可以调度一个 cron 作业来提醒自己及时更改安全密钥。

额外的安全措施

为了加强安全,可以考虑在所有机器上安装和配置入侵检测系统(IDS),例如 Snort。当入侵正在发生或者最近曾发生过,IDS 将会通知用户。使用 IDS,还能够添加其他级别的安全性,例如对备份进行数字签名和加密。

流行的开源工具(比如 GNU Privacy Guard (GnuPG)、OpenSSL 和 ncrypt)都支持通过 shell 脚本保护归档文件,但是本文不建议在没有 IDS 提供的额外防护级别的情况下这样做(有关 Snort 的更多信息,请参阅 参考资料)。

结束语

本文展示了如何在远程服务器上执行脚本,以及如何进行安全和自动化的文件传输。我希望您能由此得到灵感而开始考虑保护您自己的重要数据,并使用 OpenSSH 和 Snort 等开放源代码工具来构建新的解决方案。


相关主题

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Linux
ArticleID=22328
ArticleTitle=在 Linux 上进行自动备份
publish-date=07212008