新买的服务器系统为Centos7,默认使用firewalldselinux管理系统安全,sshd默认为22端口且为密码登陆,并且yum自带的nginx没有编译http2。所以这里就从第一次连上服务器开始到完全上线https服务器作讲解,包括配置yumsshfirewalldselinuxnginxhttpshttp2

一、更换ssh端口并使用key登陆

第一次登陆服务器用的是账号密码登陆22端口,而几乎所有服务器的22端口都会被互联网上的扫描工具扫描并用密码暴力破解,如果你查看sshd日志,会发现这点。所以买了服务器第一件事就是更换sshd端口,并禁用密码,使用key登陆。所以接下来就来配置sshd

1.复制本地ssh的public key到服务器

在本地执行:

liuxu:~$ ssh-copy-id root@服务器ip

这样就可以不用密码登陆服务器了:

liuxu:~$ ssh root@服务器ip

2.配置sshd端口并使用key登陆

现在更改端口,禁用密码。注意,当前登陆的bash千万不要关闭。因为如果配置错误又重启了服务器的话,会无法用新的ssh登陆,此时就可以用这个bash检查配置。为了方便起见,我们称这个bashbash_safe。现在来更改下配置:

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以内是系统需要的端口,最好不要用,而端口最大值是65535PasswordAuthentication 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以外的端口,报错如下:

[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,首先安装配置工具:

[root@liuquanhao ~]# yum install policycoreutils-python

然后就可以使用semanage查看22端口属于selinux的哪个type

[root@liuquanhao ~]# semanage port --list | grep 22
...
ssh_port_t                     tcp      22

这里就是说22端口属于ssh_port_t这个type,我们来把27812添加进来:

[root@liuquanhao ~]# semanage port --add -t ssh_port_t -p tcp 27812

再用semanage查看ssh_port_t

[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

[root@liuquanhao ~]# firewall-cmd --get-active-zones
public
  interfaces: eth0

可以看见我们当前在public这个zonefirewalld默认有多个zone,默认在public,不同zone有不同配置,也就有了不同的安全级别。

再看看当前zone有哪些serviceport,也就可以知道当前防火墙开启了哪些端口:

[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

只有sshdhcpv6-client两个servicessh这个service中配置的是tcp22端口,而且当前zone没有任何其他端口打开,只让22端口通过。所以我们来把27812添加到当前zone中:

[root@liuquanhao ~]# firewall-cmd --permanent --add-port 27812/tcp

这里我没有用--zone public明确选择public这个zone,因为它是当前默认,所以就不用特意添加上。--permanent是持久保存的意思,如果没有它,重新加载firewalld的配置时,配置会清空。

接下来我们可以把多余的ssh这个service从当前zone去除,毕竟我们不再需要22端口。

[root@liuquanhao ~]# firewall-cmd --permanent --remove-service ssh
success

现在重新加载firewalld配置,否则无法生效:

[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

[root@liuquanhao ~]# systemctl restart sshd

重启成功,现在这个bash_safe窗口不要关闭,在新开一个bash,我们称它为bash_complete,然后登陆:

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

因为我想要的是一台可以开启http2https服务器,所以需要特别安装。

1.安装epel

默认yum安装的nginx是没有http2的,所以首先需要安装epel

[root@liuquanhao ~]# yum install epel-release

2.安装nginx

[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

[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默认并没有开启80443端口,所以需要配置下firewalld

[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,因为没有让httphttps使用自定义端口。

5.配置nginx网页目录权限:

nginx有个网页目录权限问题,因为selinux会阻止nginx随意访问目录。为了符合linux目录规范,我将网页放在了/srv/www/目录下:

[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:rootselinuxtypevar_t。而nginxwork进程的所有者是rootdomainhttpd_t。由于selinux的不同domain只能读取相对应的type,所以http_t这个domain无法读取var_t这个type的文件,我们得调整一下网页目录的权限。首先看看selinux默认配置中的/srv/目录权限有哪些:

[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/目录下的typevar_t/srv/下更精确匹配的www目录typehttpd_sys_content_t,而我们目录的type现在就是var_t,而且所有者root,所以修改一下权限:

[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的泛域名证书:

[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

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用户,然后我用默认服务器监听http80端口跳转到https协议去,然后用https默认服务器监听liuquanhao.com这个域名,让这个域名跳转到www.liuquanhao.comhttps 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配置文件:

[root@liuquanhao www]# nginx -s reload

三、配置fail2ban防止被攻击

如果有人使用工具大量请求服务器,会导致服务器资源迅速被消耗,所以可以限制访问者一定时间能访问多少次,过量就封他ip

首先安装工具:

[root@liuquanhao ~]# yum install fail2ban-firewalld
[root@liuquanhao ~]# systemctl enable fail2ban

然后配置nginx.conf

    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个请求时,其中rate100个会及时处理,然后burst中的100个会每隔100/60秒处理一个,每处理一个burst的位置就让出来一个。但是我这里配置了nodelay,这样的话当来了200个请求的时候,会直接处理200个,但是burst的位置还是每隔100/60让出来一个,主要是为了避免用户等待。

配置好了nginx,就可以配置fail2ban了:

[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

[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,也可以使用其他方法结局这个限制。首先安装工具:

[root@liuquanhao fail2ban]# yum install conntrack-tools

然后添加配置:

[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,大功告成,现在就可以访问我的博客了。


参考连接:

selinux配置nginx权限

Centos中使用firewalld

firewall-cmd官方文档

redhat selinux管理手册