起因
我的服务器中部署了一个typecho博客和两个使用docker容器的服务,其中docker容器使用端口映射,将容器中的端口映射到宿主机上的端口实现访问。
一次偶然机会发现服务器上的服务可以通过IP+端口的方式直接访问,如果有未备案的域名解析到我们服务器的IP,可能会导致云服务器厂商关停我们的服务造成一些问题。因此,我们需要禁止通过IP+端口直接访问服务。
Nginx 配置
这里的三个服务通过nginx进行转发,对不同server_name
的请求会直接转发到对应的服务进程。因此,这里有限考虑使用nginx配置来禁止IP+端口的访问。服务器上主要开放了两个端口,80和443,分别用于HTTP和HTTPS请求,在实际进行相应配置时二者也有所不同。
80 端口
对于80端口,我们在nginx.conf
中添加如下配置。具体原理在于,当根据listen无法得到最佳匹配时,nginx会使用请求中的Host值匹配server_name,匹配顺序可以参考这篇博客。IP+端口进行请求时匹配到下面的server配置,直接返回403错误信息。
server {
listen 80 default_server;
server_name _;
return 403;
}
443端口
由于使用了HTTPS协议,因此还需要禁止通过IP+443端口的访问方式。具体参考了下面的博客:
具体来说,Nginx 上对于 SSL 服务器在不配置证书的时候会出现协议错误,哪怕端口上配置了其他网站也会报错。因此,我们需要随便生成一个证书进行配置,生成 SSL 证书可以使用这个网站https://myssl.com/create_test_cert.html。在nginx.conf
中添加如下配置:
server {
listen 80 default;
listen 443 default_server;
#SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
#error_page 404/404.html;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/private.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
#SSL-END
server_name _;
return 403;
}
配置完成后重载 Nginx 配置。
$ nginx -t
$ nginx -s reload
Docker容器问题
Docker容器通过端口映射实现服务的对外可用性,这里是通过-p host_port:container_port
实现容器上的端口到宿主机上端口的映射。具体来说,以容器的80端口映射到宿主机的8080端口为例,docker容器运行时使用了-p 8080:80
,Nginx中监听了80端口,并在对应域名访问时将请求转发到服务器的8080端口。
location / {
proxy_pass http://host_ip:8080;
}
但是,在根据上面的配置禁止了直接使用IP+80/443端口访问的方式后,发现host_ip:8080
仍然能够访问到docker容器中的服务。查询相关资料修改了iptables路由表和sfw防火墙规则后,仍然无法解决问题。防火墙上没有打开端口,但仍然可以访问。最后通过查找资料发现是docker自身的原因,下面是docker官方的介绍:
If you don't specify an IP address (i.e.,-p 80:80
instead of-p 127.0.0.1:80:80
) when publishing a container's ports, Docker publishes the port on all interfaces (address0.0.0.0
) by default. These ports are externally accessible. This also applies if you configured UFW to block this specific port, as Docker manages its own iptables rules. Read more
大致意思是说docker容器中设置端口映射时如果没有指定宿主机IP,那么默认映射到0.0.0.0
,即所有IP都可以访问。并且由于Docker镜像自行管理其路由表规则,设置宿主机防火墙也不起作用。知道原因后,我们只需要修稿docker容器的端口映射即可,对于正在运行的docker容器,修改方式参考如下博客,修改对应容器的/var/lib/docker/containers/{container_id}/hostconfig.json
中的PostBindings->HostIp
,HostIp设置为127.0.0.1
即可。
评论 (0)