通过Nginx的正向代理审计监控内网用户的外网访问记录

正向代理

一个位于客户端和目标服务器之间的 Nginx 正向代理服务器, 客户端向 Nginx 正向代理发送一个请求并指定目标服务器,然后代理向目标服务器转交请求并将获得的内容返回给客户端及本地代理服务器缓存

适用场景:

用于解决内网服务器通过代理服务器访问外网监控内网终端的外网访问记录代理端缓存外网访问的响应结构,加速外网访问

正向代理又细分为 http、https 流量的透明代理和非透明代理

透明代理指利用内网 dns 将待访问的域名解析到 Nginx 的透明代理, 终端用户利用内网 dns 后不需要进行任何代理服务器设置非透明代理指终端用户需要设置代理服务器信息

如何代理加密的 HTTPS 流量是正向代理需要解决的主要问题, 当前主要的两种方式:

七层解决:HTTP CONNECT 透传(隧道)模式, 即不解密不感知上层流量四层解决: NGINX Stream

http 流量

1、透明代理, 利用本机 hosts 或 DNS 解析待访问的目标域名到代理服务器 Ip

# Nginx 透明正向代理 http 流量主要配置 server { resolver 114.114.114.114; listen 80; access_log /var/log/nginx/access.80.logmain; location / { proxy_pass http://$http_host$request_uri; proxy_set_header HOST $http_host; proxy_buffers 256 4k; proxy_max_temp_file_size 0k; proxy_connect_timeout 30; proxy_send_timeout 60; proxy_read_timeout 60; proxy_next_upstream error timeout invalid_header http_502; } }

2、非透明代理, 需在客户终端设置代理服务器信息

# Nginx 非透明正向代理 http 流量主要配置 server { resolver 114.114.114.114; listen 1080; location / { proxy_pass http://$http_host$request_uri; proxy_set_header HOST $http_host; proxy_buffers 256 4k; proxy_max_temp_file_size 0k; proxy_connect_timeout 30; proxy_send_timeout 60; proxy_read_timeout 60; proxy_next_upstream error timeout invalid_header http_502; } }

https 流量

1、HTTP CONNECT 隧道 方式(非透明代理)

# 需要 ngx_http_proxy_connect_module 模块支持; 客户端需要指定 https 代理服务器 yum -y install gcc-c++ zlib-devel openssl-devel.x86_64 pcre-devel gd-devel patch wget http://nginx.org/download/nginx-1.20.1.tar.gz tar xf nginx-1.20.1.tar.gz && cd nginx-1.20.1 # 给 nginx 打补丁, 需要根据不同的 nginx 版本 选择对应的补丁 cd /root/src/ && git clone https://github.com/chobits/ngx_http_proxy_connect_module cd nginx-1.20.1 patch -p1 < /root/src/ngx_http_proxy_connect_module/patch/proxy_connect_rewrite_1018.patch ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt=-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC --with-ld-opt=-Wl,-z,relro -Wl,-z,now -pie \ --add-module=/root/src/ngx_http_proxy_connect_module make && make install mkdir -p /var/cache/nginx/client_temp && useradd -M -r -s /sbin/nologin nginx # nginx server 配置 server { listen8443; resolver114.114.114.114; access_log/var/log/nginx/access.8443.logmain; proxy_connect; proxy_connect_allow443; proxy_connect_connect_timeout10s; proxy_connect_read_timeout 10s; proxy_connect_send_timeout 10s; location / { proxy_pass http://$host; proxy_set_header Host $host; } } # linux 命令行设置 http 代理服务器 export http_proxy="192.168.31.68:1080" export http_proxy="192.168.31.68:8443" # 通过 curl 测试代理服务器是否正常 curl --proxy 192.168.31.68:8443 https://www.baidu.com -svo /dev/null curl http://192.168.31.17:1083 -svo /dev/null

2、NGINX Stream 方式(HTTPS 流量的透明正向代理)

需要 --with-stream,--with-stream_ssl_preread_module、--with-stream_ssl_module 模块的支持;利用 ngx_stream_ssl_preread_module 模块, 在不解密的情况下拿到 HTTPS 流量待访问目标域名(利用 TLS/SSL 握手的第一个 Client Hello 报文中的扩展地址 SNI (Server Name Indication)来获取);如果客户端没有携带 SNI 字段,会造成代理服务器无法获取待访问目标服务域名,导致访问不成功; 因此要求所有客户端都需要在 TLS/SSL 握手中带上 SNI 字段!!将待访问的所有域名利用内网 dns 或 hosts 解析到代理服务器; 客户端侧则不需要指定代理服务器信息stream { resolver 114.114.114.114; log_format main $remote_addr - [$time_local] $protocol $status $bytes_sent $bytes_received $session_time "$upstream_addr" "$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time" $remote_addr $remote_port $server_addr $server_port; access_log /var/log/nginx/access.443.log main; server { listen 443; ssl_preread on; proxy_connect_timeout 5s; proxy_pass $ssl_preread_server_name:$server_port; } } # 命令行测试 或 通过浏览器测试 # 将要访问的目标域名通过本机 hosts 或 内网 dns 解析到代理服务器 openssl s_client -connect www.baidu.com:443 -servername www.baidu.com