防火墙
技术的功能主要在于及时发现并处理计算机网络运行时可能存在的安全风险、数据传输等问题,其中处理措施包括隔离与保护,同时可对计算机网络安全当中的各项操作实施记录与检测,以确保计算机网络运行的安全性,保障用户资料与信息的完整性,为用户提供更好、更安全的计算机网络使用体验。
在RHEL8中使用的防火墙是firewalld
,在firewalld中涉及zone
的概念。
zone类似地铁的安检入口,不同的zone制定了不同的规则。某个网卡要和某个zone进行关联,这样从网卡进入的数据包都要使用其相关联的zone的过滤规则。网卡不能同时和多个zone关联,最多只能和一个zone关联。如果网卡没有和任何zone关联,则使用默认的zone中的挂则。
查看系统中有多少个zone,命令如下:
[root@server ~]# firewall-cmd --get-zones
block dmz drop external home internal nm-shared public trusted work
其中,block
拒绝所有的数据包通过,trusted
允许所有的数据包通过。
查看系统默认的zone,
[root@server ~]# firewall-cmd --get-default-zone
public
把默认的zone
修改为trusted
,再改回去
[root@server ~]# firewall-cmd --set-default-zone=trusted
success
[root@server ~]# firewall-cmd --get-default-zone
trusted
[root@server ~]# firewall-cmd --set-default-zone=public
success
[root@server ~]# firewall-cmd --get-default-zone
public
查看某个网卡和其关联zone,
[root@server ~]# firewall-cmd --get-zone-of-interface=ens32
public
[root@server ~]# firewall-cmd --get-zone-of-interface=ens35
public
把网卡加入某个zone,
firewall-cmd --add-interface=网卡名 --zone=zone名
前面提到一张网卡只能和一个zone关联,所以当网卡加入zone出现冲突的时候,需要把网卡从当前的zone删除,然后再添加
[root@server ~]# firewall-cmd --remove-interface=ens35 --zone=public
success
[root@server ~]# firewall-cmd --get-zone-of-interface=ens35
no zone
[root@server ~]# firewall-cmd --add-interface=ens35 --zone=public
success
[root@server ~]# firewall-cmd --get-zone-of-interface=ens35
public
网卡更换zone可以使用,
[root@server ~]# firewall-cmd --change-interface=ens35 --zone=home
success
[root@server ~]# firewall-cmd --get-zone-of-interface=ens35
home
[root@server ~]# firewall-cmd --change-interface=ens35 --zone=public
success
[root@server ~]# firewall-cmd --get-zone-of-interface=ens35
public
查看某个zone的规则,
[root@server ~]# firewall-cmd --list-all --zone=public
public (active)
target: default
icmp-block-inversion: no
interfaces: ens32 ens35
sources:
services: cockpit dhcpv6-client ssh
ports: 123/udp 323/udp
protocols:
forward: yes
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
关于icmp协议,参考 https://blog.csdn.net/u010230019/article/details/130880059
icmp有很多类型的数据包,ping的时候用的是以下两种:
echo-request
:我ping对方时发送出去的包echo-reply
:对方回应我的包一共有多少种类型的icmp包,可以通过firewall-cmd --get-icmptypes
来查看
[root@server ~]# firewall-cmd --get-icmptypes
address-unreachable bad-header beyond-scope communication-prohibited destination-unreachable echo-reply echo-request failed-policy fragmentation-needed host-precedence-violation host-prohibited host-redirect host-unknown host-unreachable ip-header-bad neighbour-advertisement neighbour-solicitation network-prohibited network-redirect network-unknown network-unreachable no-route packet-too-big parameter-problem port-unreachable precedence-cutoff protocol-unreachable redirect reject-route required-option-missing router-advertisement router-solicitation source-quench source-route-failed time-exceeded timestamp-reply timestamp-request tos-host-redirect tos-host-unreachable tos-network-redirect tos-network-unreachable ttl-zero-during-reassembly ttl-zero-during-transit unknown-header-type unknown-option
在sever上执行tcpdump
命令进行抓包,
[root@server ~]# tcpdump -i ens35 icmp
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ens35, link-type EN10MB (Ethernet), snapshot length 262144 bytes
15:15:35.830231 IP node-138 > server.rhce.cc: ICMP echo request, id 21152, seq 17, length 64
15:15:35.830277 IP server.rhce.cc > node-138: ICMP echo reply, id 21152, seq 17, length 64
15:15:36.830327 IP node-138 > server.rhce.cc: ICMP echo request, id 21152, seq 18, length 64
15:15:36.830398 IP server.rhce.cc > node-138: ICMP echo reply, id 21152, seq 18, length 64
...
^C
20 packets captured
47 packets received by filter
26 packets dropped by kernel
这里node-138向server.rhce.cc发送了多个echo-request
包,而server均回应了echo-reply
包。
在server上用防火墙设置拒绝别人发过来的echo-request
包,
[root@server ~]# firewall-cmd --add-icmp-block=echo-request
success
这时从node-138服务器发送的echo-request
包就将无法被接收
[root@node-138 yum.repos.d]# ping node-140
PING node-140 (192.168.17.140) 56(84) bytes of data.
From node-140 (192.168.17.140) icmp_seq=1 Packet filtered
...
From node-140 (192.168.17.140) icmp_seq=7 Packet filtered
^C
--- node-140 ping statistics ---
7 packets transmitted, 0 received, +7 errors, 100% packet loss, time 6002ms
恢复接收echo-request
包,
[root@server ~]# firewall-cmd --remove-icmp-block=echo-request
success
两台主机通信必须使用某个协议,例如,浏览器访问网址使用http,远程登录linux服务器使用ssh协议等。默认情况下,public这个zone只允许很少的服务通过,
[root@server ~]# firewall-cmd --list-all
public (active)
...
services: cockpit dhcpv6-client ssh
...
这里没有允许http通过,如果查看防火墙是否开放了某个协议,也可以通过如下,
[root@server ~]# firewall-cmd --query-service=http
no
[root@server ~]# firewall-cmd --query-service=ssh
yes
获取系统所支持的所有服务,
[root@server ~]# firewall-cmd --get-services
RH-Satellite-6 RH-Satellite-6-capsule amanda-client amanda-k5-client amqp amqps apcupsd audit bacula bacula-client bb bgp bitcoin bitcoin-rpc bitcoin-testnet bitcoin-testnet-rpc bittorrent-lsd ceph ceph-mon cfengine cockpit collectd condor-collector ctdb dhcp dhcpv6 dhcpv6-client distcc dns dns-over-tls docker-registry docker-swarm dropbox-lansync elasticsearch etcd-client etcd-server finger foreman foreman-proxy freeipa-4 freeipa-ldap freeipa-ldaps freeipa-replication freeipa-trust ftp galera ganglia-client ganglia-master git grafana gre high-availability http https imap imaps ipp ipp-client ipsec irc ircs iscsi-target isns jenkins kadmin kdeconnect kerberos kibana klogin kpasswd kprop kshell kube-api kube-apiserver kube-control-plane kube-controller-manager kube-scheduler kubelet-worker ldap ldaps libvirt libvirt-tls lightning-network llmnr managesieve matrix mdns memcache minidlna mongodb mosh mountd mqtt mqtt-tls ms-wbt mssql murmur mysql nbd netbios-ns nfs nfs3 nmea-0183 nrpe ntp nut open ovirt-imageio ovirt-storageconsole ovirt-vmconsole plex pmcd pmproxy pmwebapi pmwebapis pop3 pop3s postgresql privoxy prometheus proxy-dhcp ptp pulseaudio puppetmaster quassel radius rdp redis redis-sentinel rpc-bind rquotad rsh rsyncd rtsp salt-master samba samba-client samba-dc sane sip sips slp smtp smtp-submission smtps snmp snmptrap spideroak-lansync spotify-sync squid ssdp ssh steam-streaming svdrp svn syncthing syncthing-gui synergy syslog syslog-tls telnet tentacle tftp tile38 tinc tor-socks transmission-client upnp-client vdsm vnc-server wbem-http wbem-https wireguard wsman wsmans xdmcp xmpp-bosh xmpp-client xmpp-local xmpp-server zabbix-agent zabbix-server
[root@server ~]# firewall-cmd --get-services |wc -w
182
开放某个协议,
firewall-cmd --add-service=http
移除某个协议,
firewall-cmd --remove-service=http
前面介绍了对服务进行过滤与放行,这些服务使用的都是标准端口,例如,http使用80端口,ssh使用22端口。但有时服务会用用一个非标准端口,例如httpd端口更改为8080,在防火墙中只放行http服务,本质就是放行80端口,此时http无法提供web服务。所以我们要放行指定端口,常用语句:
firewall-cmd --add-port=端口/协议
[root@node-138 yum.repos.d]# firewall-cmd --query-port=8080/tcp
no
[root@node-138 yum.repos.d]# firewall-cmd --query-port=8080/udp
no
[root@node-138 yum.repos.d]# firewall-cmd --add-port=8080/tcp
success
[root@node-138 yum.repos.d]# firewall-cmd --add-port=8080/udp
success
[root@node-138 yum.repos.d]# firewall-cmd --remove-port=8080/tcp
success
[root@node-138 yum.repos.d]# firewall-cmd --remove-port=8080/udp
success
前面提到的,不管是对端口还是服务放行,都会遇到一个问题就是,如果允许则允许所有的客户端,拒绝则拒绝所有的客户端。
有时候需要设置只允许特定的客户端访问,其他客户端都不能访问,此时就需要使用到富规则。富规则可以对服务限制,也可以对端口限制。语法,
firewall-cmd --add-rich-rule='rule family=ipv4 source adddress=源网段 service name=服务名 accept'
这里单双引号均可,查看现有富规则,
[root@node-138 yum.repos.d]# firewall-cmd --list-rich-rules
移除前面在防火墙中添加的http
服务和端口,创建【服务】相关的富规则,
[root@server ~]# curl 192.168.17.138
curl: (7) Failed to connect to 192.168.17.138 port 80: No route to host
[root@node-138 yum.repos.d]# firewall-cmd --add-rich-rule="rule family=ipv4 source address=192.168.17.140 service name=http accept"
success
[root@node-138 yum.repos.d]# firewall-cmd --list-rich-rules
rule family="ipv4" source address="192.168.17.140" service name="http" accept
[root@server ~]# curl 192.168.17.138
hello noob
[root@node-138 yum.repos.d]# firewall-cmd --remove-rich-rule="rule family=ipv4 source address=192.168.17.140 service name=http accept"
success
[root@node-138 yum.repos.d]# firewall-cmd --list-rich-rules
[root@server ~]# curl 192.168.17.138
curl: (7) Failed to connect to 192.168.17.138 port 80: No route to host
移除前面富规则,添加【端口】相关富规则,
firewall-cmd --add-rich-rule="rule family=ipv4 source address=源网段 port port=M-N protocol=协议 accept"
M-N
为端口范围
[root@node-138 yum.repos.d]# firewall-cmd --add-rich-rule="rule family=ipv4 source address=192.168.17.140 port port=8080 protocol=tcp accept"
success
[root@server ~]# curl 192.168.17.138:8080
hello noob
[root@node-138 yum.repos.d]# firewall-cmd --remove-rich-rule="rule family=ipv4 source address=192.168.17.140 port port=8080 protocol=tcp accept"
success
[root@server ~]# curl 192.168.17.138:8080
curl: (7) Failed to connect to 192.168.17.138 port 8080: No route to host
注意:
前面提到的这些规则(包括服务,端口,富规则等)都只是临时生效,如果希望永久生效,需要添加
--permanent
选项,还需额外注意:
- 添加
--permanent
,当前不生效,重启系统或firewalld之后生效- 不加
--permanent
,当前生效,重启系统或firewalld之后失效
所以写规则的时候,可以添加两条,即包含--permanent
和不包含的
SELinux 全称是 Security-Enhanced Linux,目的是提高系统的安全性。当我们执行某个操作时,如果SELinux认为此操作有危险,则会拒绝进一步的访问。
详细参考 https://blog.csdn.net/u010230019/article/details/132318781
临时开启和关闭selinux
setenforce 1 #开启
setenforcd 0 #关闭
永久开启或关闭
[root@node-138 ~]# cat /etc/selinux/config
...
SELINUX=enforcing #开启
#SELINUX=disable #关闭
...
永久开启或关闭都需要重启机器,可能会很慢
在开启了selinux的情况下,selinux会为每个文件,每个进程都分配一个标签,这个标签我们称为上下文(context
),后面说的标签和上下文是同一个概念,查看上下文需要加上Z
选项,例如,
[root@node-138 yum.repos.d]# ps axZ|grep http|grep -v grep
...
system_u:system_r:httpd_t:s0 1713 ? Ss 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 1714 ? S 0:00 /usr/sbin/httpd -DFOREGROUND
...
其中,httpd
进程的上下文为httpd_t
,查看文件上下文,例如,
[root@node-138 yum.repos.d]# ll -Z /var/www/html/
-rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 index.html
可以看到index.html
的上下文是httpd_sys_content_t
。
特定上下文的进程只能访问特定上下文的文件,上下文为httpd_t
的进程可以访问上下文为httpd_sys_content_t
的文件或目录。
举个例子
[root@node-138 web]# mkdir /web
[root@node-138 web]# echo "new noob" > /web/index.html
[root@node-138 web]# ll -Z /web/index.html
-rw-r--r--. root root unconfined_u:object_r:default_t:s0 /web/index.html
[root@node-138 web]# ln -s /web/index.html /var/www/html/www
[root@node-138 web]# ll -Z /var/www/html/www
lrwxrwxrwx. root root unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/www -> /web/index.html
[root@server ~]# curl 192.168.17.138:8080/www/index.html
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /www/index.html
on this server.</p>
</body></html>
新建的文件上下文是default_t
,上下文不匹配,所以网页无法显示。
修改上下文,命令为chcon
,
chcon -R -t 上下文 目录
上面的示例,我们来修改下
[root@node-138 web]# chcon -R -t httpd_sys_content_t /web
[root@node-138 web]# ll -Z /web/
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 index.html
[root@server ~]# curl 192.168.17.138:8080/www
new noob
如果要恢复目录的默认上下文,使用restorecon -R 目录
命令。
[root@node-138 web]# restorecon -R /web/
恢复后网页无法访问了
修改默认上下文,命令,
semanage fcontext -a -t httpd_sys_content_t "/目录(/.*)?"
添加默认上下文
semanage fcontext -d -t httpd_sys_content_t "/目录(/.*)?"
移除默认上下文
[root@node-138 web]# semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"
[root@node-138 web]# ll -Z
-rw-r--r--. root root unconfined_u:object_r:default_t:s0 index.html
可以看到修改完,上下文仍是default_t
。要使其生效,需要使用restorecon
命令
[root@node-138 web]# restorecon -R /web/
[root@node-138 web]# ll -Z
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 123
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 index.html
[root@node-138 web]# touch 321
[root@node-138 web]# ll -Z
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 123
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 321
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 index.html
可以看到目录及其内文件上下文已经改变,并且新建文件上下文也发生变化。
端口也是有上下文的,如果一个端口没有上下文或者上下文和进程不匹配,则进程就无法访问此端口。
查看系统所有端口上下文使用semanage port -l
,
[root@node-138 web]# semanage port -l
SELinux Port Type Proto Port Number
...
afs_fs_port_t udp 7000, 7005
afs_ka_port_t udp 7004
...
http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000
...
我们把httpd
的启动端口改为808
,重启httpd,会有如下报错
Nov 22 15:15:14 node-138 setroubleshoot[3189]: SELinux is preventing /usr/sbin/httpd from name_bind access on the tcp_socket port 808. For complete SELinux messages run: sealert -l d26bbec0-a1ef-460e-b617-80f30cebfc2e
...
这里的意思是无法绑定端口808
,因为808
没有对应的端口上下文。可以通过修改端口上下文解决此问题,修改端口上下文语法,
semanage port -a -t 上下文 -p 协议 端口
[root@node-138 web]# semanage port -a -t http_port_t -p tcp 808
[root@node-138 web]# systemctl restart httpd
[root@node-138 web]# ss -nltp|grep 808
LISTEN 0 128 [::]:808 [::]:* users:(("httpd",pid=3241,fd=4),("httpd",pid=3240,fd=4),("httpd",pid=3239,fd=4),("httpd",pid=3238,fd=4),("httpd",pid=3237,fd=4),("httpd",pid=3236,fd=4))
布尔值可以理解为一个功能开关,在没有SELinux的情况下,电灯完全由电源开关控制。现在有了SELinux,电灯亮不亮由电源开关和SELinux开关同时控制。
查看系统中所有的SELinux的布尔值可以使用getsebool -a
[root@node-138 web]# getsebool -a|grep ftp
ftpd_anon_write --> off
ftpd_connect_all_unreserved --> off
ftpd_connect_db --> off
ftpd_full_access --> off
ftpd_use_cifs --> off
ftpd_use_fusefs --> off
ftpd_use_nfs --> off
ftpd_use_passive_mode --> off
...
下面以vsftpd为例讲解SELinux布尔值的用途
yum install -y vsftpd
cd /var/ftp/
mkdir incoming
chown ftp.ftp incoming/
[root@node-138 ftp]# cat /etc/vsftpd/vsftpd.conf |egrep -v '^$|^#'
anonymous_enable=YES
..
anon_upload_enable=YES
anon_mkdir_write_enable=YES
...
systemctl restart vsftpd
[root@node-137 ~]# lftp 192.168.17.138/incoming
cd ok, cwd=/incoming
lftp 192.168.17.138:/incoming> ls
lftp 192.168.17.138:/incoming> put /etc/hosts
put: Access failed: 553 Could not create file. (hosts)
[root@node-138 ftp]# getsebool -a|grep ftpd_full_access
ftpd_full_access --> off
[root@node-138 ftp]# setsebool -P ftpd_full_access on
[root@node-138 ftp]# getsebool -a|grep ftpd_full_access
ftpd_full_access --> on
lftp 192.168.17.138:/incoming> put /etc/hosts
398 bytes transferred
lftp 192.168.17.138:/incoming> ls
-rw------- 1 14 50 398 Nov 22 08:28 hosts
设置布尔值语法,关闭,
setsebool [-P] 布尔值 off
#或
setsebool [-P] 布尔值 0
开启,
setsebool [-P] 布尔值 on
#或
setsebool [-P] 布尔值 1
-P
表示永久生效,即重启后也生效
SELinux一共有三种模式:
想要切换模式,使用setenforce N
,
setenforce 1
:切换到Enforcing模式setenforce 0
:切换到Permissive模式[root@node-138 ftp]# getenforce
Enforcing
[root@node-138 ftp]# setenforce 0
[root@node-138 ftp]# getenforce
Permissive
[root@node-137 ~]# getenforce
Disabled
通过setenforce
命令只能临时切换模式,重启后会恢复默认模式,通过修改/etc/selinux/config
可以永久修改默认模式
[root@server yum.repos.d]# ll /etc/selinux/config
-rw-r--r-- 1 root root 1186 Oct 19 17:01 /etc/selinux/config
[root@server yum.repos.d]# ll /etc/sysconfig/selinux
lrwxrwxrwx. 1 root root 17 Sep 8 16:55 /etc/sysconfig/selinux -> ../selinux/config
[root@server yum.repos.d]# cat /etc/selinux/config
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
# See also:
# https://docs.fedoraproject.org/en-US/quick-docs/getting-started-with-selinux/#getting-started-with-selinux-selinux-states-and-modes
#
# NOTE: In earlier Fedora kernel builds, SELINUX=disabled would also
# fully disable SELinux during boot. If you need a system with SELinux
# fully disabled instead of SELinux running with no policy loaded, you
# need to pass selinux=0 to the kernel command line. You can use grubby
# to persistently set the bootloader to boot with selinux=0:
#
# grubby --update-kernel ALL --args selinux=0
#
# To revert back to SELinux enabled:
#
# grubby --update-kernel ALL --remove-args selinux
#
SELINUX=disabled
# SELINUXTYPE= can take one of these three values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targeted
前面提到过,修改后需要重启才能生效。