来自低版本 IIS 受害者的记录,愿你不会用到过时的软件。
最近我维护的几个网站需要启用 HTTPS,这些网站有多个不同的主域名,部署在 Windows Server 2008 SP 1 的 IIS 7.5 上面。
之前我刚在一台 IIS 10 的服务器上为一些网站添加了 HTTPS,但是这次却遇到问题了,因为 IIS 7.5 版本太低了。
在 IIS 10 上面添加新的绑定时,选择 https 协议,界面是这样的:
然而在 IIS 7.5 上,界面是这样的:
红框里是两者的 2 个区别:
- IIS 7.5 不能输入主机名
- IIS 7.5 没有“服务器名称指定”功能
IIS 7.5 缺失的这两个功能导致了两个问题。
第一个问题就是不能输入主机名,这姑且可以解决,打开 C:\Windows\system32\inetsrv\config\applicationHost.config
,找到要添加 https 绑定的站点,之前已经有 http 绑定了,现在在下面添加 https 的绑定。例如:
<site name="站点名" id="1" serverAutoStart="true">
<application path="/" applicationPool="站点名">
<virtualDirectory path="/" physicalPath="E:\web1" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:80:www.a.com" />
<binding protocol="http" bindingInformation="*:80:www.b.com" />
<binding protocol="https" bindingInformation="*:443:www.a.com" />
<binding protocol="https" bindingInformation="*:443:www.b.com" />
</bindings>
</site>
但是此时这些 https 绑定并没有对应到它们自己的证书,这就会引出第二个问题。
在 IIS 里编辑绑定,选择这个 https 绑定所对应的证书,就会因为没有“服务器名称指定”功能而看到提示:
“服务器名称指定”就是 SNI 功能,如果网站服务器没有 SNI,那么一台服务器(即 1 个 IP)上面只能绑定 1 个 HTTPS 站点。如果你添加了第二个 HTTPS 绑定,第一个就会失效,总之这个服务器只支持一个 HTTPS 解析。
这个问题我搜索了一大圈,没找到可行的解决办法。
关于 SNI 什么是可以看这里:https://www.globalsign.com/en/blog/what-is-server-name-indication
对于 IIS 8 或更高版本,在添加 https 绑定时可以启用“服务器名称指定”,或者在 applicationHost.config
里面设置 sslFlags
为 1
启用 SNI。例如:
<binding protocol="https" bindingInformation="*:443:www.a.com" sslFlags="1" />
<binding protocol="https" bindingInformation="*:443:www.b.com" sslFlags="1" />
但是发布于 2009 年的 IIS 7.5 并不支持 sslFlags
标记,添加这个标记会导致 IIS 报错。(相关文档:https://learn.microsoft.com/en-us/iis/configuration/system.applicationHost/sites/site/bindings/binding)
为了解决 IIS 7.5 无法绑定多个 HTTPS 站点的问题,我尝试在这台服务器上使用 Nginx,让 IIS 负责 HTTP 网站,Nginx 负责 HTTPS 网站。
我先在 IIS 中删除了 HTTPS 绑定,重启 IIS 服务。
然后下载 Nginx 的 Windows 安装包并解压出来:http://nginx.org/en/download.html
接下来修改 Nginx 的配置文件 conf/nginx.conf
,把默认站点监听的 80 端口改为 443。
listen 443;
这是因为 IIS 和 Nginx 默认都会监听 80 端口,端口冲突时 Nginx 启动不了。在 logs/error.log
里会输出错误信息:
bind() to 0.0.0.0:80 failed (10013: An attempt was made to access a socket in a way forbidden by its access permissions)
修改端口后,执行命令 start nginx.exe
启动 Nginx。
如果在任务管理器里看到有 2 个 nginx.exe 的进程就表示启动成功了。此时在 IE 浏览器里访问 http://127.0.0.1:443/ 能够看到 Nginx 的欢迎页面,在外网通过 http://{IP}:443/ 也可以正常访问。
但是 Nginx 在 Windows 上不会作为服务运行,这意味着当前登录用户注销后,nginx.exe 也会结束运行。
搜索“如何把 Nginx 作为服务运行”,可以看到有很多种方法,有些比较早的方法不仅操作复杂,还有一些缺陷。仔细对比之后我根据 Stackoverflow 上的一个回答,使用一个叫 NSSM 的软件把 Nginx 包装成了服务。(来源:https://stackoverflow.com/a/41467168)
(如果 Nginx 已经在运行,则需要先退出它,然后再把它安装为服务,避免重复运行)
通过 NSSM 操作之后,可以在服务里看到 nginx 服务:
此时启动服务即可,注销后再次登录可以看到 Nginx 依然在运行,默认网站也可以照常打开。
修改了 Nginx 的配置文件之后,可以重启动此服务来达到重载 Nginx 的目的。
如果想要通过执行命令 nginx -s reload
来重载 Nginx,则需要修改服务的运行用户:
查看服务的属性,“登录”选项卡里默认是“本地系统账户”,将其改为当前登录的用户 Administrator。
因为我们用 CMD 执行命令 nginx -s reload
时不是系统账户,如果服务是以本地账户运行的话,因为用户身份不一致就会导致命令执行失败。
把登录用户修改为 Administrator 不会影响服务的运行效果,用户注销后 nginx.exe 进程依然存在。
之后修改 Nginx 的配置文件 conf/nginx.conf
,把默认网站的 server 节点删除,然后启用 HTTPS server
下被注释掉的 server 节点,填入网站信息,如:
server {
listen 443 ssl;
server_name www.a.com;
ssl_certificate E:/cert/nginx/xxx_www.a.com.pem;
ssl_certificate_key E:/cert/nginx/xxx_www.a.com.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
root E:/web1;
index index.html index.htm;
}
}
由于这台服务器是 Windows 系统,复制的文件路径里分隔符是反斜线 \
,需要修改成 Unix 风格的 /
。
之后重载 Nginx,在外部浏览器里用 https 协议访问网站,可以正常打开。
接下来把需要用 https 访问的其他网站也依次配置,复制粘贴再修改一下就搞定了。
最后需要做个访问 http 自动跳转到 https 的 URL 重定向。
这台服务器比较特殊,之前的 http 站点都在 IIS 上,需要在 IIS 里设置跳转,但是 IIS 里没有 https 站点。
我本以为这不能跳转到 Nginx 里的 https 站点,但是当我用 IIS 的 URL 重写模块设置了重定向之后,出乎意料的正常打开了 https 站点,并且证书也是正确的。
可能是 IIS 重定向到 https 网址时并不是在自己内部寻找对应的 HTTPS 绑定,而是把请求转发到了 443 端口,这样才能被 Nginx 正确接管吧。这太好了,如果 IIS 做不到这样的话,我就得把这些 http 网站转移到 Nginx 里,然而由于 80 端口冲突,事情会变成需要我把 IIS 里的所有网站都转移到 Nginx 里,然后停用 IIS。这下省事多了。