apache: 配置 - 可以把ServerName和ServerAlias配置为IP吗?



0. 前言

1) 首先铺垫一下apache的virtualhost的理论配置

apache有两种配置virtualhost的方式,一种是基于IP(ip based),另外一种是基于名称(name based)。

关于apache的virtualhost详细文档,可以参见httpd 官方文档

基于IP的方式中,多个daemon还是单个daemon的问题,这里不讨论,可以参见上面提供的官方文档查看详细信息

另一个需要注意的点是,使用基于名称的虚拟主机配置时,在httpd的配置文件中,最先加载的virtualhost配置为默认的虚拟主机

通常,大部分情况下,我们常用的是使用基于名称的虚拟主机配置方式。

2) 基于名称的配置中,增加了一个ip访问的需求

有一天,在某办公室的神秘对话:

开发:运维同学,我想要用ip访问apache,辛苦您帮我配置一下
运维:好的

这个场景中,也是使用的更常见的基于名称的虚拟主机的配置方式

运维思索了一下,找出了以下方案:

  1. 用第一个默认virtualhost来提供需要用ip访问的虚拟主机内容。
  2. 或者是在默认virtualhost之外,增加一个虚拟主机,配置ServerName(如果有多个ip,可以使用ServerAlias补充)。

这两个方案中,第一个方案有明显的缺点,因为生产环境中通常都会给默认虚拟主机设定访问限制,来禁止使用ip或者其他未经授权的域名解析来访问服务器,只有那些明确监听的域名才会被响应。而第二个方案,运维同学心里有点打鼓,不确定同时拥有默认虚拟主机和将ServerName配置为IP的情况下,apache会使用哪个来发起响应。所以运维同学决定来验证一下。

1. 验证过程

1) 准备环境

配置三个虚拟主机,第一个是默认虚拟主机,访问会返回“from default”;第二个是配置ServerName为ip(127.0.0.1 & 192.168.1.124)的虚拟主机,访问会返回“from ip virtualhost”;第三个是配置了本地测试域名“test.local.net”的虚拟主机,访问会返回“from test.local.net”。三个虚拟主机是按照介绍的顺序依次从上到下排列。

虽然我们给ServerName配置了IP,但是使用ServerName本身实际上还是使用的基于名字的虚拟主机配置类型。

mkdir -p {conf/vhosts.d,www/{default,ip,test.local.net}}
sudo echo "127.0.0.1 test.local.net" >> /etc/hosts

# 准备 httpd 配置文件
cat << EOF > conf/vhosts.d/00-default.conf
<VirtualHost *:80>
  DocumentRoot "/www/default"  

  <Directory "/www/default">
    Require all granted
  </Directory>
</VirtualHost>
EOF

cat << EOF > conf/vhosts.d/01-ip.conf
<VirtualHost *:80>
  DocumentRoot "/www/ip"
  ServerName   127.0.0.1:80
  ServerAlias  192.168.1.124:80

  <Directory "/www/ip">
    Require all granted
  </Directory>
</VirtualHost>
EOF

cat << EOF > conf/vhosts.d/02-test.local.net
<VirtualHost *:80>
  DocumentRoot "/www/test.local.net" 
  ServerName   test.local.net:80

  <Directory "/www/test.local.net">
    Require all granted
  </Directory>
</VirtualHost>
EOF

# 准备不同虚拟主机的网站文件
cat << EOF > www/default/index.html
from default
EOF

cat << EOF > www/ip/index.html
from ip virtualhost
EOF

cat << EOF > www/test.local.net/index.html
from test.local.net
EOF

# httpd:2.4.51使用的网站用户的uid是33,需要正确的授权
sudo chown -R 33.33 www

# 准备基于docker的启动脚本
cat << EOF > start.sh
docker run -it -d --rm --name httpd \
    -p 80:80 \
    -v $(pwd)/www:/www \
    -v $(pwd)/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf \
    -v $(pwd)/conf/vhosts.d:/usr/local/apache2/conf/vhosts.d \
    httpd:2.4.51
EOF

2) 启动httpd

sh start.sh

3) 验证

首先,我们简单的使用curl访问一下,看看结果

curl 127.0.0.1
from ip virtualhost

curl 192.168.1.124
from ip virtualhost

curl test.local.net
from test.local.net

结果符合预期,每个访问都得到了设想的结果。

4) 然而,apache是如何来给请求匹配虚拟主机的呢?

《httpd基于名字的虚拟主机文档》中发现了这样的说明。

首先,请求进来后,apache先通过ip:port来筛选。然后,再使用ServerName和ServerAlias来进行进一步匹配。若匹配到了,则响应请求,若未匹配到,就会进一步使用默认虚拟主机来响应。

在这个过程中,ServerName和ServerAlias部分的匹配,使用的是请求中的”Host”首部。

那么,来手动修改“Host”首部来验证一下

curl -H "Host: 127.0.0.1" test.local.net
from ip virtualhost

curl -H "Host: test.local.net" 192.168.1.124
from test.local.net

curl -H "Host: nonexist.com" 192.168.1.124
from default

果然,返回的内容竟然和请求的url无关,而是和“Host”首部强关联。比如访问test.local.net返回的竟然是“from ip virtualhost”,这和”Host: 127.0.0.1”首部相对应。

3. 总结