YOLO813

Web服务器:Nginx功能以及虚拟主机的作用

    之前写过http下方的server被称作为虚拟主机,当配置文件中只存在一个server时,Nginx会将该server当成默认网站,所有发给Nginx服务器80端口的数据都会默认发送给这个server。

    详解server中的配置

charset utf-8;
access_log  logs/112.access.log  main;

    一般日志中access_log就使用全局的日志记录即可,但如果你想做多个子域名(或者二级域名)网站最好还是单独配置一下access_log,这样日志不会混杂在一起,而且我发现之前使用dnf安装的nginx日志信息都做了定时切割,即每日的日志都打成一个单独压缩包,这样可以节省空间,也方便排查问题,非常棒。如果自己是用源码配置的nginx,那么就得手动去写一个sh脚本放在crontab中运行,不过也不麻烦。

    例如,我想写一个每日凌晨0点统计网站每个IP访问次数的脚本(管道符的含义是将前一个命令的结果当做后一个命令的对象)

#命名的尾缀并非一定要sh
touch record_uniq_ip.sh 
vim record_uniq_ip.sh
# 以下为record_uniq_ip.sh的内容
>#!/bin/bash
>#author:zhangxiaofei
>#record the number of uniq IP visits my web
>awk '{print $1}' /usr/local/nginx/logs/112.access.log | uniq -c > ip.log

    脚本写好后使用crontab -e来写定时任务(请注意,这里不需要填写user name)或者直接修改/etc/crontab中的内容,在我的云服务器上,crontab -e的定时任务存放在/var/spool/cron/root文件下(因为我是root用户)。

 

Nginx目录访问控制

    应用环境是网站存在a目录,但是我不想让它被别人访问。

 location / {
  #定义根目录/usr/local/nginx/html
      root   html;
      index  index.html index.htm;
  }
  #location定义的是相对于根目录的路径
  #/a其实表示的是/usr/local/nginx/html/a目录
  location /a {
      #可以在本机上通过127.0.0.1来访问该目录
      allow 127.0.0.1;
      #可以通过IP来源为IP112.110.99.241来访问该目录
      allow 112.110.99.241;
      deny all;
  }

    上面的allow 112.110.99.241指的不是通过浏览器输入该公网IP可以访问该目录,而是指在服务器上可以使用elinks http://112.126.88.219/a/这种方式访问到a目录,如果不加入这个,那么你在服务器上想通过112.110.99.241的IP来源(即服务器本机的公网IP)访问a目录是无法访问的。

    但是这种做法有个问题,通过公网访问该路径,返回的是403禁止页面,对状态码稍微了解一点的都知道,说明该页面存在,但是只是被禁止访问,稍作修改,让其返回404,500错误,亦或者让其重定向至某个页面(非状态码则必须是一个url);

  location /a {
      ...
      # return 404;
      return https://www.baidu.com
  }

    首先访问了a页面,返回的Status Code:302 Moved Temporarily,然后跳转至百度首页。

Nginx登录认证

    应用于某个页面必须要登录验证才能访问。为了便捷,需要借用到Apache的一个包,因为centos8上面已经预装了htpasswd,假如服务器上面没有安装,使用yum进行安装httpd-tools即可

rpm -qf `which htpasswd`
>httpd-tools-2.4.37-30.module_el8.3.0+561+97fdbbcc.x86_64

    在配置文件中加入以下代码

location /b{
    #开启密码验证,前端WWW-Authenticate中的Basic realm
    #可以是string也可以是off
    auth_basic "zhang";
    #存放秘钥文件所在
    auth_basic_user_file /usr/local/nginx/pwd/htpasswd;
}

    再次访问网站b目录下的路径,提示如下,即使我在htpasswd文件中使用vim加入了zhangxiaofei:123账户和密码也还是无法访问,是因为必须要加密的密码才会得到承认,有两种方法,一种是openssl,一种就是htpasswd

htpasswd -b pwd/htpasswd zhangxiaofei 123456
cat pwd/htpasswd
>zhangxiaofei:$apr1$7c07vj9T$qKR8HbLrGkC2XTR2Mwa7g1

    重启nginx之后,再次输入账号密码成功访问到该页面。

Nginx日志格式

    在http服务器配置中,有一个字段名为log_format,是用来定义记录日志的格式,可以定义多种日志格式,但是需要取不同的名字,如下所示,log_name为main,后接string(字符串,又称nginx变量)字段。

log_format  main '$remote_addr - $remote_user [$time_local] "$request" '
                 '$status $body_bytes_sent "$http_referer" '
                 '"$http_user_agent" "$http_x_forwarded_for"';

    知道了原理,我尝试重新定义nginx.conf的日志输出格式,如下:

#每一行必须用单引号包裹
log_format zxf_log '[$time_local] [$remote_addr] ["$request"] [$status]';
access_log  logs/112.access.log  zxf_log

再次查看访问日志,如下:

    排查日志是一件非常费时间和费眼力的事情,在以上的基础上,我们可以优化日志的输出格式,让输出变得更好看一些,那么完全可以尝试将日志输出为简洁和清晰的json格式。

log_format json_log '{"@timestamp":"$time_local",'
    '"client_ip":"$remote_addr",'
    '"request":"$request",'
    '"status":"$status",'
    '"bytes":"$body_bytes_sent",'
    '"x_forwarded":"$http_x_forwarded_for",'
    '"http_referer":"$http_referer",'
    '"user_agent":"$http_user_agent"'
'}';

    清晰很多!

Nginx防盗链

    防盗链的原理是通过Referrer Policy来判断的,只允许自己设置的路径来访问该图片链接。

    例如,现在我服务器上有一张图片,我不设置防盗链的话,我通过其它服务器配置img标签的src属性可以直接将图盗走,如

<img src='http://112.***.**.**9/0.png' height='100px' width='50px'>

    通过nginx日志可以看到http_referer正是另外一台服务器的IP

配置文件如下:

#~表示匹配,*不区分大小写,以下图片格式结尾的
location ~* \.(png|gif|bmp)$ {
      #blocked表示有防火墙标志
      valid_referers none blocked *.myweb.com;
      if ($invalid_referer){
              return 403;
      }
}


再次远程调用该图片,提示403禁止访问

 

虚拟主机的作用

    虚拟主机可以使用一个web服务器发布多个网站。一个web分享出去需要IP,Port,domain,因此相同的web服务器发布不同的网站可以基于这三点。

    一、基于IP的虚拟主机。描述:每个网站需要一个独立的IP,一个公网IP每年的费用大概在600元,由于没有公网IP,用elinks对两个内网IP来进行测试。

    先用ifconfig查看相关网卡信息,获取内网inet的IP,在其基础上建立一个子网卡,这样就有两个IP段可以用于测试

ifconfig eth0:1 172.23.246.187/24 up

    在nginx.conf配置文件中,配置如下

server {
    listen       80;
    server_name  172.23.246.177;
    location / {
        root   html/web1;
        index  index.html index.htm;
    }
}
server {
    listen       80;
    server_name  172.23.246.187;
    location / {
        root   html/web2;
        index  index.html index.htm;
    }
}

    使用elinks测试如下,就我目前的了解而言,内网IP是无法通过公网IP来进行访问的:

    测试完成之后,关闭子网卡

ifconfig eth0:1 down


    二、基于端口。适合私网用户。原理是一样的,还是配置两个server,这里就可以直接用浏览器对公网IP进行访问测试了,但是前提是该端口(示例为8000端口)在阿里云和防火墙之中放行了

server {
      listen       80;
      server_name  112.126.88.219;
      location / {
          root   html/web1;
          index  index.html index.htm;
      }
}
server {
      listen       8000;
      server_name  112.126.88.219;
      location / {
          root   html/web2;
          index  index.html index.htm;
      }
}


    三、基于域名。只需要一个IP就可以发布N个网站,测试如下

    首先修改云服务器dns解析策略

vim /etc/hosts
# 加入以下内容
112.126.88.219 www.cba.com
112.126.88.219 www.nba.com

    nginx.conf配置文件基本与第一种基于IP的相同,将server_name进行替换即可,使用elinks测试如下: