前言
在Linux基础 装饰你的Shell中,我们简单地复习了一下Vim
的用法,还接触了一些比较酷的Linux软件,比如ohmyzsh
和htop
。
虽然这些功能看似很强大,但你还不是真正的强大。因为在某种程度上,你的VPS还暴露在一个危险的互联网环境中,而你对此可能还没有太多的认识。所以,本回我将讨论个人VPS安全的相关问题。与个人对应的自然是企业级的安全了,不过那在我的能力范畴之外了。
大家可能偶有耳闻网络安全相关的新闻。比如,facebook的帐号信息被泄露啦,twitter上的名流被人冒名顶替啦。还有以前我读书那会的爆炸性新闻——美国“棱镜计划”和斯诺登。这些好像离我们十分遥远。说近的,平时互联网中很多自动化的攻击,对付它们往往也需要耗费大量的精力。
在日常生活中,会定期改改密码的人估计都是凤毛麟角。平时大家很少过分地考虑网络安全,好像一直也没啥事。这是为什么呢?对于这个问题,我也没有明确答案。不过,我有几个推测,大家可以参详一下:
- **私人信息基本上已经是被窃取了。**这点我就不想多说了。我还记得以前有骚扰电话可以准确地说出我的电话、名字、身份证号和住址。我也不知道这些信息是怎么被泄露出去的,可能在发生在很久以前,具体原因也不可考了。
- 你的密码还是相对安全的。
- 我们平时使用的服务,比如银行、淘宝、微信之类的,账户和密码的安全由服务商保证。它们基于
https
和ssl
协议,在数学原理和现代密码学原理的buff加持下使得直接破解是十分困难。我们平时说的密码泄露,多半是自己在不安全的地方(比如http开头的网站)输入了自己的帐号信息,又或者你使用了弱密码
被别人暴力破解了。所以“定期更换复杂的密码”是一种比较合理的建议(当然,我认为bitwarden才是最佳实践)。 - 随着移动智能设备的流行,二次验证也变得十分常见。比如你输入正确的帐户信息后,可能还要手机再确认一下,这样的帐户基本上很难被窃取。如果攻击者同时获得了你的手机和你的帐户信息,这基本上就意味着你可能遭遇了绑架之类的人身攻击了,这时候人身安全是优先考虑的问题了。
- 我们平时使用的服务,比如银行、淘宝、微信之类的,账户和密码的安全由服务商保证。它们基于
所以呀,在日常生活中,其实是服务提供商帮你考虑了安全问题。可是,如果你玩VPS的话,你就变成VPS的安全负责人了。这时,你就需要了解一些基本的网络安全的知识了。试想你通过http协议传输密码,那么你的信息将以明文的方式在互联网中传播。这根本没有任何安全性可言。
关于个人VPS的安全,如果以后有更多观点再补充。网络安全是一个相对的概念,**你要做到的不是绝对安全,而是比大多数人安全。**这样攻击者就不会去啃你这个硬骨头,而去找一些软柿子(那些没有安全防护的VPS)捏了(这么说好像有点腹黑,哈哈)。不过也不用太担心安全问题。毕竟你的VPS才开通没久,也没有布署多少网络服务,攻击者暂时还没发现你。况且,正规的云服务商本身也有一些安全措施,攻击者要突破他们的防线也绝非易事。
总之,在进一步学习Linux之前,我建议大家对自己的VPS做一些安全防护。这里会介绍一些通用的、简单的但却十分有效的安全措施。
测试环境
uname -a # Linux VM-12-8-ubuntu 5.4.0-42-generic #46-Ubuntu SMP Fri Jul 10 00:24:02 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
安全措施
你应该可以正常地顺着下面的代码一步步无障碍运行。
更新软件
旧版本的软件可能会含有某些漏洞,从而成为攻击者发起攻击的基础。这个怎么说呢,其实作为个人用户没有太多可以做的。**请保持软件的最新状态吧!**我使用OpenMediaVault
这个NAS系统时,安全相关的更新会强制自动安装的。
下面这个命令你也学过了:
sudo apt-get update && sudo apt-get upgrade
使用非root用户
创建非root用户
从今天开始,希望你不要再直接使用root用户了!我们先马上来创建一个新的非root用户吧!
首先,创建一个新用户组:
# 创建新用户组
sudo groupadd -g 344 test
344
叫做gid
,我是随便取的。你只要取一个不为0的整数应该都可以,因为0是root用户组专用的gid。另外,1000
、100
也是很常见的内置用户组。如果你打算用这些用户组,那你可以不需要运行此命令。
这时,有童鞋可能会问:**为什么用户(组)会有id
和name
这两个属性呢?**这个问题以后再讨论。
接着,我们为test
用户组创建一个用户,名字叫做test_user
。
# 创建一个新用户
sudo useradd \
-m -d /home/test_user `#自动创建用户目录`\
-s /bin/bash `#设置默认shell`\
-g test `#主组`\
-G sudo `#副组`\
test_user
这个命令有点长,而且结构有点奇怪,哈哈
这其实是一种shell命令的可视化写法,通过\
号将一行中的多个元素分至不同的行。在shell中,这会被认为是一条命令。
这样拆分写的好处是,可以比较清晰地看到代码的功能结构。你以后运行或记录很长的shell命令时,也可以使用这个技巧。在markdown的代码框中,可以用TAB
链来迅速生成前方空格,或者用Shift+TAB
消除前方空格。以后有机会,你可以自己试试看。它和下面的写法是等价的:
sudo useradd -m -d /home/test_user -s /bin/bash -g test -G sudo test_user
下面我们将这个用户(组)删除。
# 删除用户
sudo userdel test_user
# 删除用户组
sudo groupdel test
注意,要将所有的用户删除后才可以删除用户组。
然后,根据类似的方法创建自己专属的用户和用户组吧!我这里就用test_user:test
来展示了。
下面,我给这个用户设置一个密码:
# 设置密码
sudo passwd test_user # 假设我输入了testtest
要输入两次密码并完全一样才会生效:
New password:
Retype new password:
passwd: password updated successfully
这个密码,在我们调用sudo
的权限会用到。
那么,要怎么切换到test_user
用户呢?可以这样:
su test_user # su <用户名>
如果你目前的用户级别不比test_user
高,它会要求你输入密码。这个密码就是testtest
。
如果你是root
,直接就切换过去了。应该可以理解这种层次的关系以及它的合理性吧?
在切换的一瞬间,你会发现漂亮的ohmyzsh
不见了。这是因为我们用了默认的/bin/bash
的缘故。现在先不要管外观这些花里胡哨的东西先
这里,我们可以观察一下主机都有哪些用户:
sudo less /etc/passwd
自己看一下就好了,最下面应该是新的用户。按q
可以终止less命令的使用。
观察home目录
我们进入一下新用户的home目录:
cd ~
看看目录的内容:
ls -hl
输出total 0
,表示什么都没有。
这时,观察一下home目录的绝对路径:
pwd
输出/home/test_user
。
好吧,在Linux里创建新用户就是这么简单?对呀,我也不知道为什么这么简单呀
这里就是新家了,以后基本上就是在这里部署应用啦!
探索sudo
这个时候,我们试一下与root
的交互。比如:
cd /root
输出bash: cd: /root: Permission denied
。因为我们是普通用户嘛,没有办法进入root
的目录。在大多数情况下,你甚至没有办法进入另外一个普通用户的home
目录,除非你的权限比它高级。所以如果你想保护一些东西,可以用root
权限保护它。
我们再试试能不能使用sudo
:
sudo apt-get update
输入密码后就可以正常地运行了。因为创建用户的时候,sudo
是test_user
的副组。是不是很帅呐☺️
这时,我们再输入这个命令,观察一下我们的用户:
id
输出如下:
uid=1002(test_user) gid=344(test) groups=344(test),27(sudo)
uid
是用户的id,gid
是用户组的id。这里看到test_user
属于test
和sudo
两个用户组。如果test_user
不属于sudo
用户组,它将不可以使用sudo
!
至于uid
和gid
的具体用途,以后玩Docker的时候你会有所体会的。这里按下不表。
最后,我个人认为你可以看一下/etc/sudoers
文件:
ls -hl /etc/sudoers
输出是:
-r--r----- 1 root root 825 Apr 17 14:01 /etc/sudoers
看!在默认状态下,这个文件就算是root
用户也只可以查看而不可以改动。说明它真的很重要喔!
实际上,它控制了用户对于root
权限调用的程度。我们可以尝试来改动一下它。
首先,让它变成root可修改的文件
sudo chmod +600 /etc/sudoers # 600是啥我以后再介绍
然后用Vim编辑它:
sudo vim /etc/sudoers
它的内容应该会类似于:
#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
# timestamp_timeout=30即有效期改成30分钟。
Defaults env_reset, timestamp_timeout=30
Defaults mail_badpass
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
# User privilege specification
root ALL=(ALL:ALL) ALL
...
如刚刚运行sudo apt-get update
时,你要输入密码。默认这个授权时间是5分钟。
你可以改得稍微长一些,比如30min。我们将Defaults env_reset
来改为Defaults env_reset, timestamp_timeout=30
即可。Vim退出:wq
后生效。如果你不想改,就不要做任何事。直接:q!
退出。
之后,我们将它的权限改回来:
sudo chmod -w /etc/sudoers && ls -hl /etc/sudoers
输出为:
-r--r----- 1 root root 847 Apr 19 13:38 /etc/sudoers
这里值得注意的一行是:root ALL=(ALL:ALL) ALL
。这意味着root用户在做事情时都不用输入密码(没理解错吧?)。不要让普通用户轻易地拥有这种能力!
另外,你还可以即时强行结束sudo
对你的授权:
sudo -K
此后你再调用sudo
权限,就要再次输入用户密码获得授权。
其实sudo
还有很多更高级的用法,不过我们作为新手了解这些就足够了。
目前阶段咱们还是稳一波,乖乖地用普通的非root用户和密码来做事☺️
禁用root使用ssh登陆
我们用非root用户的重要原因,就是因为**我们要禁用root通过ssh登陆我们的VPS。**像腾讯云,在初始化系统的时候就是禁root的。这是为什么呢?
我用自己来举例。比如,我可以通过ssh user@ip
的方式尝试我的另一台VPS:
一般情况下,主机是允许root
用户远程登陆的。而root
用户的名字一般就是root
。所以,如果攻击者知道你VPS的ip地址(ping你的域名获得)、用户名(默认有一个root可用)和ssh端口号(默认22),那么保护你电脑的就只有你的用户密码了。如果你的root密码被破解了(比如你用了一些123456
的弱密码),那你的VPS就任人鱼肉了!如果你只是非root用户密码被破解,那么root用户可以暂时保护一下你。不过这种时候基本上已经非常危险了。
所以呀,我们用非root用户是有原因的,这样我们可以在用户名这个层面上增强VPS的安全防护。
当然,**如果你能进一步操作ssh端口号和ip,那么你的VPS的安全等级将呈指数级上升!**我下面还会介绍如何进行设置ssh端口和ip的!
我不知道root
用户能不能改名,但我觉得没必要。你用一个新的非root用户,然后禁root远程登陆就行了。
这里我们开始讲讲**怎么禁用root
用户远程ssh登陆。**首先,我们编辑ssh程序的设置文件:
sudo vim /etc/ssh/sshd_config
在界面中应该在类似的内容:
#Port 22
#AddressFamily any
AddressFamily inet
#ListenAddress 0.0.0.0
#ListenAddress ::
#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key
# Ciphers and keying
#RekeyLimit default none
# Logging
#SyslogFacility AUTH
#LogLevel INFO
# Authentication:
#LoginGraceTime 2m
PermitRootLogin no (就是这里!!)
# PermitRootLogin prohibit-password
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10
找到PermitRootLogin
参数,它的值yes
改成no
。要注意去掉前面的#
号。:wq
退出保存。
重启ssh服务后生效:
sudo service sshd restart
这里要切记,不要关闭旧的Shell
。新开一个Shell
,用你刚刚的非root用户登陆Shell
。成功后再关闭旧的Shell
窗口。切记切记!
隐藏公网ip
从前面的讨论,我们知道VPS的安全也可以在公网ip的层面进行操作。
我们可以通过这个命令获取自己的公网ip:
curl ifconfig.me
它通常长成这样:000.00.00.00
,3个点分成4段数字。如果你用VPS,这个公网ip是固定的。如果你用的是家用宽带,这个ip是动态变化的,一般是48小时左右变化一次(视装网地区而定)。某些公司的网络是专线网,公网ip也是固定的。
隐藏ip之前,你应该要准备一个域名并托管在cloudflare上(其它域名托管商没有用过,不知道有没有类似的功能。这里我只展示cloudflare怎么用)。
首先,我们要准备一个前缀,比如abcd
。登陆cloudflare的后台:https://dash.cloudflare.com/login。
先在右上角先选择简体中文
。
点泛域名进去,选择DNS
。为了安全起见,我隐藏了自己的真实ip和地址名称。
添加一条记录,比如:
记得先关掉小黄云,先仅限DNS
模式。最后保存。
回到shell中,你用以下命令ping下你的VPS:
ping abcd.<你的泛域名> # 我这个记录就是abcd.hwb0307.com
如果可以返回准确的ip,再回到clouldflare后台,将小黄云点开:
保存后生效。你也可以再试一下:
ping abcd.<你的泛域名> # 我这个记录就是abcd.hwb0307.com
这时ip已经发生变化了。你也可以用一些站长工具ping一下:https://ping.chinaz.com/。
我这里用博客地址来举例:
这时真实的IP已经被隐藏起来。别人用abcd.<你的泛域名>
无法正常地访问你的主机,因为你正尝试通过ssh访问cloudflare的某台服务器。是不是很神奇?当然,你也只能用真实ip去访问自己的VPS,而不是某个域名。
这是为什么呢?因为你选择Proxy
模式后,每当外部有一个请求要访问abcd.<你的泛域名>
时,cloudflare会首先接收这个请求,然后通过一些安全的协议(可能是https之类)与你真实的公网ip进行通信,这时它的作用有点像Nginx反向代理服务器,但应该使用了一些高级的技术。而ssh是没有办法通过这种方式访问的,因为端口号和协议不对,那个主机不一定会开放22端口或者你所改的1234端口。原理图如下:
DNS only: 外部-->你的域名-->VPS公网ip
Proxy: 外部-->你的域名-->Cloudflare反代服务器(这个服务器可能用了CDN之类的技术,ip也是不固定的)-->VPS公网ip
另外,如果你的VPS上有NPM之类的反向代理服务正在反代你的个人网站(比如博客),此时访问它们可能会出现ERR_TOO_MANY_REDIRECTS
的报错。官方是这样解释的:
Cloudflare SSL/TLS 应用中的灵活 SSL 选项通过 HTTPS 加密浏览器和 Cloudflare 网络之间的流量。但是,当启用灵活SSL选项时,Cloudflare 会通过 HTTP 将请求发送到未加密的源 Web 服务器。如果您的源 Web 服务器配置为在使用灵活 SSL 选项时将所有 HTTP 请求重定向到 HTTPS,则会发生重定向循环。
解决**重定向循环
的方法就是:SSL/TLS的灵活模式
改为完全模式
**:
这时候,我们会使用NPM(或者Nginx)自己的证书(通常是来自Let’s Encrypt的免费个人证书)来保证https请求的安全性。这个你可以在我另一篇博文Docker系列 两大神器Nginx proxy manager (NPM)和ddns-go的安装中更加详细地了解。
基于前面的原理,我们还可以推测,要安全地远程访问一台具有动态ip的主机(比如家用NAS)则不能使用Proxy
的方式。你应该只能设置DNS only
的模式,让域名直接解析你的域名;你也没有办法一直通过ip访问ip,因为你的ip在动态变化中。所以在这种情况下,你应该小心地保护这个域名,不要被别人知道。
不过,Proxy模式可能会造成某些地方无法访问你的网络。如果这个操作对你的需求造成造成了明显影响,你还是选择DNS-only模式
吧。或者选择国内的域名托管商(搭建互联网服务要记得备案喔!)
这部分内容是我的推测,未经求证。也许我的理解有误,欢迎专业人士留言指正了!
安装UFW防火墙
官方的帮助文档:https://help.ubuntu.com/community/UFW
前面,我们通过更改ssh端口的方式增强安全性。其实呢,你还可以通过管理VPS所有的端口来进一步增加安全性喔!这里我们介绍ufw
。
首先,给大家可以看一段wiki上的介绍:
大致来说,ufw是一个简单好用的防火墙工具,使用iptables
进行设置。这里我不准备展开介绍iptables
。自己想了解百度Google即可。
这里多说一句,iptables
其实是一个很强大的防火墙工具,不过它确实很难用。如果你学会熟练地使用iptables
的话,我觉得ufw
应该也可以不用。像我们这种新手用ufw
就足够了。
首先,观察一下自己的机器有没有ufw
which ufw # /usr/sbin/ufw
没有的话,直接安装一个:
sudo apt-get install ufw
如果有的话,就可以开始了!
用sudo ufw status
查看防火墙状态。如果你之前没有设置过ufw
,一般是显示Status: inactive
。
下面我们逐步来设置ufw
。
- 设置
ufw
的默认值
sudo ufw default deny incoming # 禁止所有进站的流量
sudo ufw default allow outgoing # 允许所有出站的流量
- 允许https/http连接
sudo ufw allow 80 # 默认http
sudo ufw allow 443 # 默认https
- 允许新ssh端口(下文会用到)
sudo ufw allow 1234/tcp comment 'SSH_2' # 1234记得换成你自定义的端口喔
如果你遵循我的docker教程,80
端口和443
端口一般是由Nginx proxy manager进行管理。一般情况下,都是Nginx软件管理80和443端口(比如宝塔面板)。
你也可以根据自己的需要开放端口:
sudo ufw allow <你的端口号>/tcp comment '你想要的备注'
如果你用docker,通常要添加某个应用的端口号,这样才可以正常地通过NPM反代。
值得一提的是,iptable
可以提供更加精细的调控,但ufw
我还不知道怎么精细地控制端口不同类型的流量。
- 启动ufw:
sudo ufw enable # 启动ufw。重启主机后正式生效。
其它常用的命令有:
# 查看帮助
ufw -h
# 如果你改动了ufw规则,记得reload一下生效
sudo ufw reload
# 显示规则的数字
sudo ufw status numbered
# 删除某个规则。基于sudo ufw status numbered命令。
sudo ufw delete <数字>
更改默认的ssh端口
默认的ssh端口号是22
。SSH 的端口一般建议把它改成一个大于1024小于65535的整数。比如,我想改成1234
。
在你的VPS后台防火墙里先打开1234
端口。有些VPS后台可能没有防火墙,就可以不用管。
下面,简单地用ufw
将新的ssh端口1234
打开:
sudo ufw allow 1234/tcp comment 'SSH' # 1234记得换上你自己的端口啦
然后像修改root访问ssh的权限一样打开ssh的设置文件:
sudo vim /etc/ssh/sshd_config
在开头不远处就一个#Port 22
的字样。#
号在多数情况下是注释号,表明后面的内容不生效。
将#
号去除,然后22
改为1234
:
Port 1234
重启ssh服务生效
sudo service sshd restart
此时不要关闭终端,新开一个窗口进行用新端口号、非root用户进行ssh登陆测试。如果成功,就可以关闭旧的Shell了。OK,安全指数+1,哈哈
新端口成功后,旧的22
端口已经没有用。可以关掉了。
禁ping
终于到最后一步了
就算你隐藏了公网ip,别人通过穷举法还是有可能发现你的ip。
所以还是设置自己的主机禁ping,伪装成是一台不能使用的机器吧。
很简单,只要
sudo vim /etc/ufw/before.rules
外面到本地的访问是由INPUT
控制的,其中echo-request
就是ping所采用的类型。因此,只要找到ok icmp codes for INPUT
下面的记录:
# ok icmp codes for INPUT
-A ufw-before-input -p icmp --icmp-type destination-unreachable -j ACCEPT
-A ufw-before-input -p icmp --icmp-type time-exceeded -j ACCEPT
-A ufw-before-input -p icmp --icmp-type parameter-problem -j ACCEPT
# -A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT
-A ufw-before-input -p icmp --icmp-type echo-request -j DROP
如上面的代码框所示,找到含有echo-request
,把ACCEPT
改成DROP
。这样外面的机器就无法ping通你的VPS了!
可重新加载一下ufw
以使改动生效(不知道是否是必需):
sudo ufw reload
你可以用windows个人电脑或者Linux的主机ping一下你的VPS(~ ̄▽ ̄)~
小结
这一节,我们基本对个人的VPS进行了基本的防护,包括对ip、端口、用户的操作。这样可以让VPS更安全,但也不是绝对的。我也并没有太多经验。
总之,你可以在此基础上进行深入的Linux学习了,比如布署Docker应用和Wordpress之类的!
如果你以后网站做大做强了,访问量很大的时候,还是需要请专业的团队来帮忙维护的!
参考资料
- 保护好你的小鸡!保姆级服务器安全教程!
转至个人博客:https://blognas.hwb0307.com。 欢迎关注!
本文使用 文章同步助手 同步