新买的服务器系统为Centos7,默认使用firewalld
和selinux
管理系统安全,sshd
默认为22
端口且为密码登陆,并且yum
自带的nginx
没有编译http2
。所以这里就从第一次连上服务器开始到完全上线https
服务器作讲解,包括配置yum
,ssh
,firewalld
,selinux
,nginx
,https
和http2
。
一、更换ssh端口并使用key登陆
第一次登陆服务器用的是账号密码登陆22
端口,而几乎所有服务器的22
端口都会被互联网上的扫描工具扫描并用密码暴力破解,如果你查看sshd
日志,会发现这点。所以买了服务器第一件事就是更换sshd
端口,并禁用密码,使用key
登陆。所以接下来就来配置sshd
。
1.复制本地ssh的public key到服务器
在本地执行:
1
liuxu:~$ ssh-copy-id root@服务器ip
这样就可以不用密码登陆服务器了:
1
liuxu:~$ ssh root@服务器ip
2.配置sshd端口并使用key登陆
现在更改端口,禁用密码。注意,当前登陆的bash
千万不要关闭。因为如果配置错误又重启了服务器的话,会无法用新的ssh
登陆,此时就可以用这个bash
检查配置。为了方便起见,我们称这个bash
为bash_safe
。现在来更改下配置:
1
2
3
4
5
6
7
8
9
10
cat /etc/ssh/sshd_config
# Port 22
Port 27154
#PasswordAuthentication yes
PasswordAuthentication no
#PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
ClientAliveInterval 60
注意是sshd_config
而不是ssh_config
,前者是服务端sshd
监听配置,后者是ssh
客户端配置。
配置中,Port 27812
的意思就是sshd
以后监听27812
端口。你也可以用别的,选值在1024-65535
之间,因为1024
以内是系统需要的端口,最好不要用,而端口最大值是65535
。PasswordAuthentication no
就是禁止密码登陆。多添加了一个ClientAliveInterval
,代表60
秒向客户的发送一个包,以保持长连接存活。
后面两项不用配置,一般默认就是这个样子,这里只是说明一下。#PubkeyAuthentication yes
默认是注释的,就是默认可以使用key登陆,AuthorizedKeysFile .ssh/authorized_keys
是公钥位置,也就是第一步的ssh-copy-id
复制到了这里。所以你也可以不用ssh-copy-id
,直接手动复制也行,其他很多教程也是用scp
手动复制,但authorized_keys
这个文件名经常打错导致无法用key登陆,所以建议用ssh-copy-id
。
3.配置selinux使sshd能监听非22端口
配置完sshd
后,此时是无法重启成功的,因为服务器的所有端口都在selinux
的控制之下,所以selinux
会阻止sshd
监听22
以外的端口,报错如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@liuquanhao ~]# getenforce
Enforcing
[root@liuquanhao ~]# systemctl restart sshd
Job for sshd.service failed because the control process exited with error code. See "systemctl status sshd.service" and "journalctl -xe" for details.
[root@liuquanhao ~]# journalctl -xe
...
10月 20 06:33:26 liuquanhao.com systemd[1]: Starting OpenSSH server daemon...
-- Subject: Unit sshd.service has begun start-up
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
--
-- Unit sshd.service has begun starting up.
10月 20 06:33:26 liuquanhao.com sshd[1035]: error: Bind to port 27812 on 0.0.0.0 failed: Permission denied.
10月 20 06:33:26 liuquanhao.com sshd[1035]: error: Bind to port 27812 on :: failed: Permission denied.
10月 20 06:33:26 liuquanhao.com sshd[1035]: fatal: Cannot bind any address.
10月 20 06:33:26 liuquanhao.com systemd[1]: sshd.service: main process exited, code=exited, status=255/n/a
10月 20 06:33:26 liuquanhao.com systemd[1]: Failed to start OpenSSH server daemon.
-- Subject: Unit sshd.service has failed
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
--
-- Unit sshd.service has failed.
--
-- The result is failed.
error: Bind to port 27812 on 0.0.0.0 failed: Permission denied
这个错误就是在说没有权限监听27812端口,即使你是root
也不行。
所以我们接下来得配置selinux
,首先安装配置工具:
1
[root@liuquanhao ~]# yum install policycoreutils-python
然后就可以使用semanage
查看22
端口属于selinux
的哪个type
:
1
2
3
[root@liuquanhao ~]# semanage port --list | grep 22
...
ssh_port_t tcp 22
这里就是说22
端口属于ssh_port_t
这个type
,我们来把27812
添加进来:
1
[root@liuquanhao ~]# semanage port --add -t ssh_port_t -p tcp 27812
再用semanage
查看ssh_port_t
:
1
2
[root@liuquanhao ~]# semanage port --list | grep ssh
ssh_port_t tcp 27812, 22
可以看见已经添加进去了。这里要说一下,22
端口是无法删除的,因为它是被编译进入selinux
的。selinux
在编译的时候,默认添加了一些常用的默认规则,如果要删除默认规则,需要自己重新编译selinux
。而我们的添加只是往配置文件里加了点配置,可以自己删除。
4.配置firewalld打开添加的端口
如果你现在重启sshd
,你依然会连不上,因为即使selinux
让你的sshd
可以监听27812
,也只是sshd
有了监听权限。如果你想远程连接服务器,还需要让防火墙打开这个端口,所以接下来配置firewall
:
先看看我们当前在firewalld
的哪个zone
:
1
2
3
[root@liuquanhao ~]# firewall-cmd --get-active-zones
public
interfaces: eth0
可以看见我们当前在public
这个zone
,firewalld
默认有多个zone
,默认在public
,不同zone
有不同配置,也就有了不同的安全级别。
再看看当前zone
有哪些service
和port
,也就可以知道当前防火墙开启了哪些端口:
1
2
3
4
5
6
7
8
9
10
[root@liuquanhao ~]# firewall-cmd --permanent --zone public --list-services
ssh dhcpv6-client
[root@liuquanhao ~]# firewall-cmd --info-service ssh
ssh
ports: 22/tcp
protocols:
source-ports:
modules:
destination:
[root@liuquanhao ~]# firewall-cmd --permanent --zone public --list-ports
只有ssh
和dhcpv6-client
两个service
,ssh
这个service
中配置的是tcp
的22
端口,而且当前zone
没有任何其他端口打开,只让22
端口通过。所以我们来把27812
添加到当前zone
中:
1
[root@liuquanhao ~]# firewall-cmd --permanent --add-port 27812/tcp
这里我没有用--zone public
明确选择public
这个zone
,因为它是当前默认,所以就不用特意添加上。--permanent
是持久保存的意思,如果没有它,重新加载firewalld
的配置时,配置会清空。
接下来我们可以把多余的ssh
这个service
从当前zone
去除,毕竟我们不再需要22
端口。
1
2
[root@liuquanhao ~]# firewall-cmd --permanent --remove-service ssh
success
现在重新加载firewalld
配置,否则无法生效:
1
2
3
4
5
[root@liuquanhao ~]# firewall-cmd --reload
[root@liuquanhao ~]# firewall-cmd --permanent --list-services
dhcpv6-client
[root@liuquanhao ~]# firewall-cmd --permanent --list-ports
27812/tcp
好了,sshd
端口问题就全部配置好了。
5.ssh配置完成
下面我们来重启sshd
:
1
[root@liuquanhao ~]# systemctl restart sshd
重启成功,现在这个bash_safe
窗口不要关闭,在新开一个bash
,我们称它为bash_complete
,然后登陆:
1
2
liuxu:~$ ssh -p 27812 'root@服务器ip'
Last login: Sat Oct 20 07:24:57 2018 from 服务器ip
OK,ssh
登陆成功,可以退出bash_safe
了。现在sshd
可以告一段落了,接下来就用我们的bash_complete
来配置nginx
。
二、安装nginx
因为我想要的是一台可以开启http2
的https
服务器,所以需要特别安装。
1.安装epel
默认yum
安装的nginx
是没有http2
的,所以首先需要安装epel
:
1
[root@liuquanhao ~]# yum install epel-release
2.安装nginx
1
2
3
4
5
6
7
8
9
10
11
[root@liuquanhao ~]# yum install nginx
...
Total 1.6 MB/s | 3.2 MB 00:00:01
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
Importing GPG key 0x352C64E5:
Userid : "Fedora EPEL (7) <epel@fedoraproject.org>"
Fingerprint: 91e9 7d7c 4a5e 96f1 7f3e 888f 6a2f aea2 352c 64e5
Package : epel-release-7-11.noarch (@extras)
From : /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
Is this ok [y/N]: y
...
3.配置systemd开机启动nginx
默认nginx
并不会开机启动,安装完成后也没有运行起来,所以我们来运行nginx
:
1
2
3
4
5
6
7
8
9
[root@liuquanhao ~]# systemctl enable nginx
Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.
[root@liuquanhao ~]# systemctl start nginx
[root@liuquanhao ~]# netstat -lnpt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
...
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1400/nginx: master
tcp6 0 0 :::80 :::* LISTEN 1400/nginx: master
可以看见nginx
启动成功。
4.配置firewalld打开80和443端口
安装启动nginx
后依然是无法访问80端口的,因为firewalld
默认并没有开启80
和443
端口,所以需要配置下firewalld
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@liuquanhao ~]# firewall-cmd --permanent --list-services
dhcpv6-client
[root@liuquanhao ~]# firewall-cmd --permanent --add-service http
success
[root@liuquanhao ~]# firewall-cmd --permanent --add-service https
success
[root@liuquanhao ~]# firewall-cmd --reload
success
[root@liuquanhao ~]# firewall-cmd --permanent --list-services
dhcpv6-client http https
[root@liuquanhao ~]# firewall-cmd --info-service http
http
ports: 80/tcp
protocols:
source-ports:
modules:
destination:
[root@liuquanhao ~]# firewall-cmd --info-service https
https
ports: 443/tcp
protocols:
source-ports:
modules:
destination:
firewalld
添加完成后nginx
的端口问题就配置完成了。这里不再需要配置selinux
中的port
,因为没有让http
和https
使用自定义端口。
5.配置nginx网页目录权限:
nginx
有个网页目录权限问题,因为selinux
会阻止nginx
随意访问目录。为了符合linux
目录规范,我将网页放在了/srv/www/
目录下:
1
2
3
4
5
6
7
8
9
[root@liuquanhao www]# pwd
/srv/www
[root@liuquanhao www]# ls -lZ
total 4
drwxr-xr-x. root root unconfined_u:object_r:var_t:s0 www.liuquanhao.com
[root@liuquanhao www]# ps auxZ | grep nginx
system_u:system_r:httpd_t:s0 root 11018 0.0 0.1 57728 1100 ? Ss 07:53 0:00 nginx: master process /usr/sbin/nginx
system_u:system_r:httpd_t:s0 nginx 11019 0.0 0.2 58248 2060 ? S 07:53 0:00 nginx: worker process
system_u:system_r:httpd_t:s0 nginx 11020 0.0 0.1 58248 1808 ? S 07:53 0:00 nginx: worker process
可以看见,默认复制到/srv/www/
下的读写权限是0755
,所有者是root:root
,selinux
的type
是var_t
。而nginx
的work
进程的所有者是root
,domain
是httpd_t
。由于selinux
的不同domain
只能读取相对应的type
,所以http_t
这个domain
无法读取var_t
这个type
的文件,我们得调整一下网页目录的权限。首先看看selinux
默认配置中的/srv/
目录权限有哪些:
1
2
3
4
5
6
7
8
[root@liuquanhao www]# semanage fcontext --list | grep srv
/srv/.* all files system_u:object_r:var_t:s0
/srv/([^/]*/)?www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0
/srv/([^/]*/)?ftp(/.*)? all files system_u:object_r:public_content_t:s0
/srv/([^/]*/)?rsync(/.*)? all files system_u:object_r:public_content_t:s0
/srv/([^/]*/)?www/logs(/.*)? all files system_u:object_r:httpd_log_t:s0
...
/srv all files system_u:object_r:var_t:s0
可以看见,selinux
默认配置中,/srv/
目录下的type
是var_t
,/srv/
下更精确匹配的www
目录type
是httpd_sys_content_t
,而我们目录的type
现在就是var_t
,而且所有者
是root
,所以修改一下权限:
1
2
3
4
5
6
7
8
[root@liuquanhao srv]# pwd
/srv
[root@liuquanhao srv]# restorecon -Rv www
[root@liuquanhao srv]# ls -lZ
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 www
[root@liuquanhao www]# chown -R nginx:nginx www/www.liuquanhao.com/
[root@liuquanhao www]# ls -lZ
drwxr-xr-x. nginx nginx unconfined_u:object_r:httpd_sys_content_t:s0 www.liuquanhao.com
6.配置ssl
这里我用的是网站sslforfree.com
的泛域名证书:
1
2
3
4
5
[root@liuquanhao www]# ls -l /etc/ssl/liuquanhao.com/
total 12
-rw-r--r--. 1 root root 1646 10月 16 17:28 ca_bundle.crt
-rw-r--r--. 1 root root 2178 10月 16 17:28 certificate.crt
-rw-r--r--. 1 root root 1703 10月 16 17:28 private.key
7.配置nginx.conf开启https和http2
[root@liuquanhao www]# cat /etc/nginx/nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
gzip on;
gzip_min_length 1k;
gzip_comp_level 3;
gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css image/x-icon;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80 default_server;
server_name _;
return 301 https://www.liuquanhao.com$request_uri;
}
server {
listen 443 ssl default_server;
ssl on;
ssl_certificate "/etc/ssl/liuquanhao.com/certificate.crt";
ssl_certificate_key "/etc/ssl/liuquanhao.com/private.key";
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
server_name _;
return 301 https://www.liuquanhao.com$request_uri;
}
server {
listen 80;
server_name liuquanhao.com;
return 301 https://www.$server_name$request_uri;
}
server {
listen 80;
server_name www.liuquanhao.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name liuquanhao.com;
ssl on;
ssl_certificate "/etc/ssl/liuquanhao.com/certificate.crt";
ssl_certificate_key "/etc/ssl/liuquanhao.com/private.key";
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
return 301 https://www.$server_name$request_uri;
}
server {
listen 443 ssl http2 backlog=4096;
server_name www.liuquanhao.com;
root /srv/www/www.liuquanhao.com;
ssl on;
ssl_certificate "/etc/ssl/liuquanhao.com/certificate.crt";
ssl_certificate_key "/etc/ssl/liuquanhao.com/private.key";
ssl_session_cache shared:SSL:5m;
ssl_session_tickets on;
ssl_session_timeout 10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
location = / {
index index.html;
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
}
这里我配置了nginx worker
进程的所有者是nginx
用户,然后我用默认服务器监听http
的80
端口跳转到https
协议去,然后用https
默认服务器监听liuquanhao.com
这个域名,让这个域名跳转到www.liuquanhao.com
的https server
中。在www.liuquanhao.com
这个server
中,我让文件目录指向/srv/www/www.liuquanao.com
,并在listen
的时候打开了http2
。
这样,当浏览器访问http://liuquanhao.com/
时就会301
跳转到https://liuquanhao.com/
,然后浏览器再次访问https://liuquanhao.com/
又会301
跳转到https://www.liuquanhao.com/
。
而当浏览器访问http://www.liuquanhao.com/
时,就会直接301
跳转到https://www.liuquanhao.com/
,这样我的网站就只运行在了www.liuquanhao.com
这个域名下。
8.nginx配置完成
现在已经配置完成,重新加载nginx
配置文件:
1
[root@liuquanhao www]# nginx -s reload
三、配置fail2ban防止被攻击
如果有人使用工具大量请求服务器,会导致服务器资源迅速被消耗,所以可以限制访问者一定时间能访问多少次,过量就封他ip
。
首先安装工具:
1
2
[root@liuquanhao ~]# yum install fail2ban-firewalld
[root@liuquanhao ~]# systemctl enable fail2ban
然后配置nginx.conf
:
1
2
3
4
5
http {
...
limit_req_zone $binary_remote_addr zone=lr_zone:10m rate=100r/m;
limit_req zone=lr_zone burst=100 nodelay;
}
第一行配置是说创建一个空间,使用二进制的ip
作为键,名字叫lr_zone
,大小是10MB
,限制每个ip
访问的频率是每分钟100
次。此时客户端1
分钟内发送100
没事,但发送101
个请求会导致100
个返回成功,1
个返回失败。第二行的意思是在http
模块里使用第一行配置的空间,然后给burst
设置为100
,并且标记为nodelay
。意思是说此时可以同时接收rate + burst
个请求,也就是200
个。这里要说明一下,假如只配置burst
,当服务器接收200
个请求时,其中rate
的100
个会及时处理,然后burst
中的100
个会每隔100/60
秒处理一个,每处理一个burst
的位置就让出来一个。但是我这里配置了nodelay
,这样的话当来了200
个请求的时候,会直接处理200
个,但是burst
的位置还是每隔100/60
让出来一个,主要是为了避免用户等待。
配置好了nginx
,就可以配置fail2ban
了:
1
2
3
4
5
6
7
[root@liuquanhao ~]# cat /etc/fail2ban/jail.local
[DEFAULT]
findtime = 60
maxretry = 1
bantime = 300
[nginx-limit-req]
enabled = true
findtime
是找60秒以内的日志,maxretry
为找到1
个就判定成功,bantime
是禁止300
秒,然后打开了nginx-limit-req
,这个就是检测上面nginx
日志,如果nginx
返回失败,fail2ban
就能检测到日志并执行任务。
现在启动fail2ban
:
1
2
3
[root@liuquanhao ~]# systemctl start fail2ban
[root@liuquanhao ~]# fail2ban-client status
[root@liuquanhao ~]# fail2ban-client status nginx-limit-req
现在如果你1
分钟访问服务器201
次,就会导致nginx
输出error
日志,从而导致fail2ban
禁止你的ip
。
但是现在有个问题,fail2ban
调用的firewallcmd-ipset
不会禁止已连接的连接,为了解决这个问题,可以使用iptables-mutilport
,也可以使用其他方法结局这个限制。首先安装工具:
1
[root@liuquanhao fail2ban]# yum install conntrack-tools
然后添加配置:
1
2
3
4
5
6
7
[root@liuquanhao fail2ban]# cat /etc/fail2ban/action.d/firewallcmd-ipset.local
[Definition]
actionban = ipset add fail2ban-<name> <ip> timeout <bantime> -exist
conntrack -D -s <ip> -p tcp --dport 443
conntrack -D -d <ip> -p tcp --sport 443
[root@liuquanhao ~]# systemctl restart fail2ban
这个配置的意思就是说,当禁止某个ip
时,先把它添加到ipset
里使这个ip
无法创建新连接,然后执行conntrack
命令删除这个ip
已建立的连接,这样就把这个ip
完全禁止了。这个我删除的是443
端口的连接,也可以直接删除整个ip
所有连接。因为我在测试,如果删除整个ip
的所有连接,会导致我的ssh
也断掉,为了方便我调试,所以就保持这样了。
OK,大功告成,现在就可以访问我的博客了。
参考连接: