正向代理系统部署笔记

概述

正向代理系统在目前的业务场景中可谓愈来愈少了。今时难得有个场景需要它,于是就有了这篇文章。

服务器为安全需求,默认禁止让内网中的服务器主动去访问公网,然而有些业务系统又必须依赖外网资源,如:

  • 商业软件 License 定期访问指定服务器验证
  • 业务系统调用公共API接口
  • 等等。。。

目前,以上的种种需求都是通过防火墙ACL策略来实现,防火墙配置的策略繁琐、低效率、易出错、依赖性强,站在技术立场不建议在防火墙ACL去管控上述的各类需求。

正向代理可以有效的替代防火墙当前的管制手段,即所有的内部服务器出公网的数据请求都通过正向代理系统,我们只需审计正向代理出公网的日志即可。通俗一点说就是我们需要一套 正向系统 + 日志系统。

Squid 是一老牌的代理软件,功能强大,性能不俗。

ELK 是流行的日志框架系统。

架构

通过filebeat将代理服务器的日志发送到logstash,以便管理员通过kibana进行分析/可视化。

环境

  • squid:代理服务器
  • squidGuard:squid的过滤器,可以过滤URL或域名
  • Filebeat:将日志发送至logstash
  • Logistash:将filebeat收到的日志标为过滤
  • Elasticsearch:数据的索引制作和保存
  • kibana:数据的可视化和分析

版本这里使用最版本的 7.7,ELK 仓库使用https://mirrors.tuna.tsinghua.edu.cn/

Filebeat安装

使用RPM包安装

# rpm -ivh https://mirrors.tuna.tsinghua.edu.cn/elasticstack/7.x/yum/7.7.0/filebeat-7.7.0-x86_64.rpm

Filebeat配置

编辑 /etc/filebeat/filebeat.yml配置文件,编辑内容如下:

filebeat.inputs:
- type: log
 paths:
 - /var/log/squid/access.log
output.logstash:
 hosts: ["Logstash_IP:5044"]

服务启动

# systemctl enable filebeat
# systemctl start filebeat

squid配置

日志输出格式

默认的日志格式不能满足我们的需求,需要进其进一步定义输出。

squid.conf

logformat access-lt %{%Y-%m-%d}tg %{%H:%M:%S}tl.%03tu %>a:%>p %ui %un %<A "%rm %ru HTTP/%rv" "%{Referer}>h" "%{User-Agent}>h" "%{Cookie}>h" %>Hs %>st %tr %<st %mt %Ss:%Sh
access_log /var/log/squid/access.log access-lt

squid.conf配置范例

cache_mgr qiush@vqiu.cn
http_port 80

cache_dir ufs /var/spool/squid 1000 16 256
coredump_dir /var/spool/squid

# Visible Hostname
visible_hostname proxy.vqiu.cn

# Cache size
cache_mem 384 MB

# Proxy DNS Server
dns_nameservers 223.5.5.5 223.6.6.6

# Error Display
error_directory /usr/share/squid/errors/zh-cn

# Auth
#auth_param basic program /usr/lib64/squid/basic_ncsa_auth /etc/squid/.htpasswd
#auth_param basic children 5
#auth_param basic realm Squid Basic Authentication
#auth_param basic credentialsttl 5 hours
#acl password proxy_auth REQUIRED
#http_access allow password
#
http_access allow all

# not display IP address
forwarded_for off

# header
request_header_access Referer deny all
request_header_access X-Forwarded-For deny all
request_header_access Via deny all
request_header_access Cache-Control deny all


# add follows to the end
url_rewrite_program /usr/bin/squidGuard -c /etc/squid/squidGuard.conf

logformat access-lt %{%Y-%m-%d}tg %{%H:%M:%S}tl.%03tu %>a:%>p %ui %un %<A "%rm %ru HTTP/%rv" "%{Referer}>h" "%{User-Agent}>h" "%{Cookie}>h" %>Hs %>st %tr %<st %mt %Ss:%Sh
access_log /var/log/squid/access.log access-lt

以上包含squidGuard的配置

关于squidGuard移步至:https://www.server-world.info/en/note?os=CentOS_7&p=squid&f=6

logstash安装/配置

安装Java环境

logstash 依赖java,所以在安装logstash之前必须把java给管上,这里我们可以使用OenJDK

# yum -y install java-11-openjdk

安装logstash

# rpm -ivh https://mirrors.tuna.tsinghua.edu.cn/elasticstack/7.x/yum/7.7.0/logstash-7.7.0.rpm

为squid输出日志创建过滤器

squid日志输出样例如下:

2020-05-16 01:21:58.606 127.0.0.1:53438 - - www.n-novice.com "GET http://www.n-novice.com/entry/2018/07/05/000000 HTTP/1.1" "-" "curl/7.29.0" "-" 301 156 392 924 text/html TCP_MISS:HIER_DIRECT
2020-05-16 01:45:56.445 10.10.100.20:57917 - - api2.firefoxchina.cn "CONNECT api2.firefoxchina.cn:443 HTTP/1.1" "-" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0" "-" 200 1237 60090 3928 - TCP_TUNNEL:HIER_DIRECT

创建/设置过滤器

创建/etc/logstash/conf.d/01-squid.conf,并追加内容如下:

input {
  beats {
    port => "5044"
    type => "beats"
  }
}
filter {
 if [type] == "beats" {
 grok {
 match => [ "message", "%{TIMESTAMP_ISO8601:s_timestamp} %{IPORHOST:src_ip}:%{POSINT:src_port} %{USER:ident} %{USER:auth} %{USERNAME:req_domain} \"%{DATA:method} %{NOTSPACE:request} (?:HTTP/%{NUMBER:httpversion})\" \"%{NOTSPACE:referer}\" \"%{DATA:send_headers}\" \"%{DATA:cookes}\" %{INT:request_code} %{INT:request_size} %{INT:response_time} %{INT:response_size} %{DATA:mime} %{GREEDYDATA:squid_status}" ]
 }
 date{
 match => ["s_timestamp" , "yyyy-MM-dd_HH-mm-ss.SSS"]
 timezone => "Asia/Shanghai"
 }
 useragent {
 source => "send_headers"
 target => "useragent"
 }
 }
}
output {
  if [type] == "beats" {
    elasticsearch {
      hosts => ["ES_IP:9200"]
      index => "squid-%{+YYYY.MM}"
    }
  }
}

赋予权限

# chown logstash. /etc/logstash/conf.d/01-squid.conf

重启logstash服务

//重启服务
# systemctl restart logstash

//查看启用日志
# tail -f /var/log/logstash/logstash-plain.log

日志预览

扩展

关于多台服务器

目前的架构是基于单机的,如果内网存在一定的规模,需要部署多台,常规的做法是接入LB(负载均衡),但是这里会存在真实源IP的问题--获取的源IP都是LB地址,所以不是我们想要的。这时候可以使用以下技术去横向扩展:

  • 使用动态路由(OSPF或BGP)建立ECMP(线下我比较偏爱的一种架构)
  • 使用LVS FULLNAT(有杀鸡用牛刀之嫌)
  • 使用透明网关代理
  • 使用HAproxy的proxy_protocol协议(Haproxy/nginx)

负载均衡使用proxy_protocol获取真实源IP

后端 squid 配置

squid.conf

acl frontend src 10.10.100.9
http_port 3128 require-proxy-header
#http_port 3128 accel require-proxy-header
proxy_protocol_access allow frontend
  • frontend:负载均衡的IP地址,有多台就写多条

更多配置参考: http://squid.mirror.colo-serv.net/archive/3.5/squid-3.5.18-RELEASENOTES.html#ss2.7

Haproxy

global
    log /dev/log    local0
    log /dev/log    local1 notice
    chroot /var/lib/haproxy
    #stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    log     global
    mode    tcp
    option  tcplog
    option  dontlognull
    timeout connect 5000
    timeout client  50000
    timeout server  50000

frontend squid
    bind 80
    default_backend squid_pool

backend squid_pool
    balance roundrobin
    mode tcp
    server squid1 xx.xx.xx.xx.:3128 check send-proxy
    server squid2 xx.xx.xx.xx.:3128 check send-proxy

1.5版本以上

nginx

stream {
log_format basic  '{"@access_time":"$time_iso8601",'
                  '"clientip":"$remote_addr",'
                  '"pid":$pid,'
                  '"pro":"$protocol",'
                  '"stus":$status,'
                  '"sent":$bytes_sent,'
                  '"recv":$bytes_received,'
                  '"sess_time":$session_time}';
    server {
        listen 80;
        proxy_pass 127.0.0.1:3128;
        proxy_protocol on;
        access_log  /var/log/nginx/stream.log basic;
        error_log  /var/log/nginx/error.log;
    }
}

对外发布LB的业务IP为业务入口即可(如: proxy.vqiu.cn),Squid 后端可以通过proxy_protocol正常获取真实客户端的IP。


另: 如果觉得squid过于重量,可以使用 nginx + ngx_http_proxy_connect,具体部署细节不表,有兴趣者自行研究。