我已经禁用PasswordAuthentication
SSH,并使用公钥-私钥登录。但是,许多指南(特别是这个)我看到建议只允许从我的特定 IP 地址进行 SSH 访问。
/etc/ssh/sshd_config:
AllowUsers deploy@(your-ip) deploy@(another-ip-if-any)
ufw allow from {your-ip} to any port 22
sshd_config
通过 IP 地址进行限制并添加防火墙规则有什么意义吗?假设我有一个动态 IP 地址,那么这些规则似乎毫无用处,因为我必须使用托管提供商的控制台终端,以 root 身份登录才能允许从我的家庭 IP 进行访问。在这种情况下:建议的路线是什么?
1:除非我首先使用托管服务提供商终端,否则禁止所有通过 SSH 登录,以根身份登录以允许从我的家庭 IP 访问 SSH?
2:保持SSH端口开放,并信任公私密钥进行登录。
3:还有其他建议吗?
答案1
好吧,我做了很多挖掘,但一无所获。因此,我为我的家用电脑设置了动态 DNS 服务,并创建了以下 bash 脚本,希望它对其他人有用。
指示:
这个脚本不是特定于 SSH,是一种允许动态主机使用 ufw 永久访问目标端口的方法 - 不管该端口上侦听的服务是什么。因此,使用端口 22(默认情况下)将允许从动态主机永久访问 SSH。同样,您可以使用此脚本确保从动态主机永久访问 FTP(端口 21)之类的东西。
只需修改文件顶部的
RemoteHostName
和变量即可。显然,是动态 DNS 提供商提供的主机名,是我们允许访问的端口。PortNumber
RemoteHostName
PortNumber
如果解析的 IP 已经
RemoteHostName
存在于可以访问目标端口的 ufw 中,则脚本退出,因为没有什么可更改的。如果不存在,则删除 ufw 中所有具有目标端口号的条目,然后使用更新后的 IP 地址和目标端口添加新条目。简而言之,执行不是尝试对脚本中涉及端口的其他规则使用 ufw,因为脚本将遍历它们。
如果该脚本由 cron 作业或类似程序调用,那么理论上它应该始终保持 ufw 中的 IP 地址更新。
该脚本输出以下内容:
1) Standard output: Could not resolve the IP address of the RemoveHostName XYZ 2) Standard output: No change necessary as the IP address XYZ already has access to port XYZ. 3) Standard output: Deleting old ufw rule: XYZ 4) Standard output: Adding new ufw rule: XYZ 5) Error output: Could not resolve the IP address of the RemoveHostName XYZ
但请注意,我是不是非常熟悉 bash 脚本,但对正则表达式却不太熟悉。所以如果你使用这个脚本,请帮自己一个忙(也帮我一个忙!),仔细检查一下。如果你发现任何错误,请发表评论或新的答案。
脚本:
#!/bin/bash
##############################################################
#Variables
##############################################################
RemoteHostName=someremotehost.dyndnsorsomething.com
PortNumber="22"
##############################################################
#Check if the dynamic IP address exists within ufw.
##############################################################
DynamicIPAddress=$(host $RemoteHostName | awk '/has address/ { print $4 }')
if [[ $DynamicIPAddress = "" ]]
then
echo $(date -u -Iseconds): Could not resolve the IP address of the RemoveHostName $RemoteHostName >&2
exit
fi
UFWRules=$(ufw status numbered)
REGEX=" $PortNumber (.*)ALLOW IN(.*)$DynamicIPAddress$"
while read -r line; do
if [[ $line =~ $REGEX ]]
then
echo $(date -u -Iseconds): No change necessary as the IP address $DynamicIPAddress already has access to port $PortNumber.
exit
fi
done <<< "$UFWRules"
##############################################################
#Remove all entries with the given port number.
##############################################################
while true
do
UFWRules=$(ufw status numbered)
REGEX="(.*) $PortNumber (.*)ALLOW IN(.*)"
Matched="No"
while read -r line; do
if [[ $line =~ $REGEX ]]
then
echo $(date -u -Iseconds): Deleting old ufw rule: $line
RuleNumber=$(echo $line | (cut -d "[" -f2 | cut -d "]" -f1))
ufw --force delete $RuleNumber
Matched="Yes"
break
fi
done <<< "$UFWRules"
if [ $Matched = "No" ]
then
break
fi
done
##############################################################
#Add in access from the dynamic IP address.
##############################################################
echo $(date -u -Iseconds): Adding new ufw rule: "ufw allow from $DynamicIPAddress to any port $PortNumber".
ufw allow from $DynamicIPAddress to any port $PortNumber
答案2
如果您在同一台主机上运行 Web 服务器,这意味着可能也安装了 PHP,那么您可以安装我编写、使用并积极维护的工具:
https://github.com/cubiclesoft/web-knocker-firewall-service/
需要 SSH 访问的客户端在其所选操作系统上将客户端部分作为系统服务运行。客户端使用不频繁的加密数据包保持服务器的 SSH 端口和任何其他受保护端口(例如 POP3/IMAP)打开。这是一个非常不错的即发即弃解决方案。我个人使用该软件时没有遇到任何问题,但您可能希望在将其部署到生产环境之前在 DigitalOcean droplet 或类似服务上对其进行测试。除非发送续订数据包(客户端默认每 10 分钟尝试续订一次),否则端口将保持打开状态长达 30 分钟(默认),这允许 IP 地址在最短的停机时间内更改。当为 IP 地址打开端口时,可以选择向配置为接收此类通知的任何人发送电子邮件通知。
目标是通过关闭敏感端口来大幅限制攻击面,因为对于动态 IP 上的客户端,没有加密密钥的用户无法设置复杂的 VPN。即使攻击者最终使用相同的未锁定 IP 地址(例如咖啡店 WiFi 上的笔记本电脑),他们仍然必须通过 SSH,但至少俄罗斯、中国和朝鲜无法看到该端口。
顺便说一句,我不使用 ufw。它用一堆不必要的链污染了 iptables,使 iptables 规则变得比需要的复杂得多,而且规则越多,通过 iptables 处理数据包的速度就越慢。不久前,我写了一篇关于编写优雅 iptables 规则的文章:
http://cubicspot.blogspot.com/2016/06/elegant-iptables-rules-for-your-linux.html
实际上,直接使用 iptables 要容易得多,而且当您可以复制粘贴好的干净规则集时,它比使用 ufw 更省事。另外,您永远不知道何时会卡在 CentOS 机器上。
答案3
我对发表于@R4D4 的回答(并由@muru 编辑),因为我在 UFW 规则中有一行允许从本地网络进行 SSH 访问。
我还想在规则中包含协议 TCP,因为 SSH 使用 TCP。我希望在评论中看到上次更新/恢复 UFW 规则的时间。也许,这可以通过不同的方式实现,如果可以的话请告诉我,但它似乎有效:
#!/bin/bash
##############################################################
#Variables
##############################################################
RemoteHostName=mydomain.com
PortNumber="22"
##############################################################
#Check if the dynamic IP address exists within ufw.
##############################################################
DynamicIPAddress=$(host $RemoteHostName | awk '/has address/ { print $4 }')
if [[ $DynamicIPAddress = "" ]]
then
echo $(date -u -Iseconds): Could not resolve the IP address of the RemoveHostName $RemoteHostName >&2
exit
fi
UFWRules=$(ufw status numbered)
REGEX=" $PortNumber (.*)ALLOW IN(.*)$DynamicIPAddress$"
while read -r line; do
if [[ $line =~ $REGEX ]]
then
echo $(date -u -Iseconds): No change necessary as the IP address $DynamicIPAddress already has access to port $PortNumber.
exit
fi
done <<< "$UFWRules"
##############################################################
#Remove all entries with the given port number.
##############################################################
while true
do
UFWRules=$(ufw status numbered)
REGEX="(.*) $PortNumber\/tcp (.*)ALLOW IN(.*)"
Matched="No"
while read -r line; do
if [[ $line =~ $REGEX ]]
then
echo $(date -u -Iseconds): Deleting old ufw rule: $line
RuleNumber=$(echo $line | (cut -d "[" -f2 | cut -d "]" -f1))
ufw --force delete $RuleNumber
Matched="Yes"
break
fi
done <<< "$UFWRules"
if [ $Matched = "No" ]
then
break
fi
done
##############################################################
#Add in access from the dynamic IP address.
##############################################################
echo $(date -u -Iseconds): Adding new ufw rule: "ufw allow from $DynamicIPAddress to any port $PortNumber proto tcp".
ufw allow from $DynamicIPAddress to any port $PortNumber proto tcp comment "the rule for this IP was added by the bash script automatically on: $(date)"
##############################################################
#Restore access from IPs of my local network
##############################################################
echo $(date -u -Iseconds): Restoring the ufw rule: "ufw allow from 192.168.192.0/24 to any port 22 proto tcp".
ufw allow from 192.168.192.0/24 to any port 22 proto tcp comment "restored by the bash script access to the local network on: $(date)"
ufw reload
答案4
R4D4 的出色脚本 - 但我也做了一些改进:
应该可以从多个动态 IP 地址进行访问。因此,我修改了以下内容:
- 创建防火墙规则时,实际主机名将作为注释输入。
- 当尝试删除防火墙规则时,每个脚本执行仅处理为实际主机名创建的规则。
因此,我现在可以根据需要对尽可能多的动态主机名执行脚本:
./restrictLogin.sh host1.domain.tld
./restrictLogin.sh host2.domain.tld
./restrictLogin.sh something.totally.different.org
./restrictLogin.sh yet.anotherone.com
这也允许您创建单独的规则(针对静态 IP),而无需被脚本清除。
因此,脚本输出也与原始示例略有不同。示例ufw status numbered
To Action From
-- ------ ----
[ 6] 22 ALLOW IN 188.xx.xx.xx #host1.domain.tld
[ 7] 22 ALLOW IN 178.xx.xx.xx #host2.domain.tld
[ 8] 22 ALLOW IN 80.xx.xx.xx #host3.domain.tld
版本:
#!/bin/bash
##############################################################
#Variables
##############################################################
RemoteHostName=$1
PortNumber="22"
##############################################################
#Check if the dynamic IP address exists within ufw.
##############################################################
DynamicIPAddress=$(host $RemoteHostName | awk '/has address/ { print $4 }')
if [[ $DynamicIPAddress = "" ]]
then
echo $(date -u -Iseconds): Could not resolve the IP address of the RemoveHostName $RemoteHostName >&2
exit
fi
UFWRules=$(ufw status numbered)
REGEX=" $PortNumber (.*)ALLOW IN(.*)$DynamicIPAddress(.*)#\s*$RemoteHostName$"
while read -r line; do
if [[ $line =~ $REGEX ]]
then
echo $(date -u -Iseconds): No change necessary as the IP address $DynamicIPAddress / $RemoteHostName already has access to port $PortNumber.
exit
fi
done <<< "$UFWRules"
##########################################################################
#Remove all entries with the given port number and RemoteHostName comment.
###########################################################################
while true
do
UFWRules=$(ufw status numbered)
REGEX="(.*) $PortNumber (.*)ALLOW IN(.*)#\s*$RemoteHostName"
Matched="No"
while read -r line; do
if [[ $line =~ $REGEX ]]
then
echo $(date -u -Iseconds): Deleting old ufw rule: $line
RuleNumber=$(echo $line | (cut -d "[" -f2 | cut -d "]" -f1))
ufw --force delete $RuleNumber
Matched="Yes"
break
fi
done <<< "$UFWRules"
if [ $Matched = "No" ]
then
break
fi
done
##############################################################
#Add in access from the dynamic IP address.
##############################################################
echo $(date -u -Iseconds): Adding new ufw rule: "ufw allow from $DynamicIPAddress to any port $PortNumber comment '$RemoteHostName'".
ufw allow from $DynamicIPAddress to any port $PortNumber comment $RemoteHostName