APT无阻碍代理架构

在我们维护Ubuntu/Debian系统时,我们都是使用apt 仓库进行软件更新。但是,由于所谓"主权"原因,我们无法访问很多互联网资源,特别是部署 Kubernetes Atlas 集群,无法访问Google软件仓库带来极大的阻碍。我经过一些尝试:

  • OpenConnect VPN

    • 优点: 完整的通用型SSL VPN解决方案,不仅适合打通墙内墙外网络实现APT无阻碍升级,也适合其他企业网络数据通许

    • 缺点:

      • 部署步骤繁杂,对于本文解决Ubuntu/Debian软件安装升级的需求过于沉重

      • 客户端vpn连接后可能破坏本地网络路由

  • Polipo代理和YUM代理配置

    • 优点: 部署简便,只需要在墙外启动服务提供代理,加上一些简单的客户端IP限制就能够提供软件包安装代理

    • 缺点:

      • 软件已经停止开发维护

      • 缺少安全机制(可以通过 SSH tunnel加密来弥补)

      • 每个升级客户端都要连接代理服务器,重复的互联网下载(可以本地局域网代理改进)

  • Squid父级socks代理

    • 优点:

      • squid作为著名的开源代理软件,经过了长期和广泛的验证

      • 性能优异稳定可靠,可以支持海量客户端

      • 通过多级代理部署,可以实现本地局域网和墙外代理的多层多源加速

    • 缺点:

      • 大型软件,需要持续学习和实践

适合apt软件仓库的代理方案

综合我的几次实践,目前我选择 Squid父级socks代理 作为基础,结合 SSH tunnel 来实现无阻碍网络:

  • Raspberry Pi Cluster 上部署了 ARM架构Kubernetes ,其中管控服务器节点 pi-master1 运行 squid 本地代理服务器

  • 租用海外云计算厂商的VPS,采用 Squid父级socks代理 架构,在服务器端运行一个只监听本地回环地址 127.0.0.1 的squid代理服务器:这样代理服务器不能被外部用户直接访问,只能通过ssh方式建立Tunnel之后才能通过端口映射方式被客户端访问到,这就提供了极强的安全加密

../../../_images/peering_basics.png
  • 本地树莓派主机 pi-master1 通过 SSH Tunnel 和海外VPS构建安全加密通道,然后本地 squid 代理服务器将VPS上监听在回环地址的squid作为父级代理,通过两跳方式,所有在本地局域网已树莓派主机 pi-master1 为代理的客户端,都能再通过海外squid代理访问整个互联网:

    • 本地树莓派主机 pi-master1 的squid配置ACL规则,可以只将部分被GFW屏蔽的网站请求转发给父级squid,这样可以节约大量的海外流量

    • squid具备的本地代理缓存可以大大加速本地局域网相同的软件安装下载

../../../_images/squid-parent-proxy-server.png

实施步骤

海外VPS运行squid配置

  • VPS运行Debian/Ubuntu系统,通过apt安装squid:

    sudo apt install squid
    
  • 修改 /etc/squid/squid.conf 配置,添加以下配置:

    # 仅提供本地回环地址服务,避免安全隐患
    http_port 127.0.0.1:3128
    

完整配置:

apt_proxy_arch/parent-squid.conf
 1acl SSL_ports port 443
 2acl Safe_ports port 80		# http
 3acl Safe_ports port 21		# ftp
 4acl Safe_ports port 443		# https
 5acl Safe_ports port 70		# gopher
 6acl Safe_ports port 210		# wais
 7acl Safe_ports port 1025-65535	# unregistered ports
 8acl Safe_ports port 280		# http-mgmt
 9acl Safe_ports port 488		# gss-http
10acl Safe_ports port 591		# filemaker
11acl Safe_ports port 777		# multiling http
12acl CONNECT method CONNECT
13http_access deny !Safe_ports
14http_access deny CONNECT !SSL_ports
15http_access allow localhost manager
16http_access deny manager
17http_access allow localhost
18http_access deny all
19http_port 127.0.0.1:3128
20coredump_dir /var/spool/squid
21refresh_pattern ^ftp:		1440	20%	10080
22refresh_pattern ^gopher:	1440	0%	1440
23refresh_pattern -i (/cgi-bin/|\?) 0	0%	0
24refresh_pattern (Release|Packages(.gz)*)$      0       20%     2880
25refresh_pattern .		0	20%	4320
  • 启动squid服务:

    sudo systemctl start squid
    
  • 检查日志:

    sudo systemctl status squid
    

本地树莓派squid

  • 本地 树莓派4b运行64位Ubuntu ,所以同样使用apt安装squid:

    sudo apt install squid
    
  • 由于我们后面会使用 SSH Tunnel 将本地 4128 端口转发到远程VPS上回环地址 3128 ,所以我们这里需要配置我们本地squid的父级squid监听是 127.0.0.1:4128 ,完整配置如下

apt_proxy_arch/client-squid.conf
  1#
  2# Recommended minimum configuration:
  3#
  4
  5# Example rule allowing access from your local networks.
  6# Adapt to list your (internal) IP networks from where browsing
  7# should be allowed
  8acl localnet src 0.0.0.1-0.255.255.255# RFC 1122 "this" network (LAN)
  9acl localne t src 10.0.0.0/8# RFC 1918 local private network (LAN)
 10acl localnet s      rc 100.64.0.0/10# RFC 6598 shared address space (CGN)
 11acl localnet sr     c 169.254.0.0/16    # RFC 3927 link-local (directly plugged) machines
 12acl localnet src 172.16.0.0/12# RFC 1918 local private network (LAN)
 13acl localnet src 192.       168.0.0/16# RFC 1918 local private network (LAN)
 14acl localnet src 11.        159.0.0/16
 15acl localnet src 11.124.0.0/16
 16acl localnet src fc00::/7           # RFC 4193 local private network range
 17acl localnet src fe80::/10          # RFC 4291 link-local (directly plugged) machines
 18
 19acl localnet src 30.0.0.0/8
 20
 21acl SSL_ports port 443
 22acl Safe_ports port 80# http
 23acl Safe_ports port 21              # ftp
 24acl Safe_ports port 443# https
 25acl Safe_ports port 70# gopher              
 26acl Safe_ports port 210# wais
 27acl Safe_ports port 1025-65535# unreg           istered ports
 28acl Safe_ports port 280# http-mgmt
 29acl Safe_ports port         488# gss-http
 30acl Safe_ports port 591# filemaker
 31acl Safe_ports por              t 777# multiling http
 32acl CONNECT method CONNECT
 33
 34#
 35# Recommended min       imum Access Permission configuration:
 36#
 37# Deny requests to certain unsafe ports
 38http_access deny !Safe_ports
 39
 40# Deny CONNECT to other than secure SSL ports
 41http_access deny CONNECT !SSL_ports
 42
 43# Only allow cachemgr access from localhost
 44http_access allow localhost manager
 45http_access deny manager
 46
 47# We strongly recommend the following be uncommented to protect innocent
 48# web applications running on the proxy server who think the only
 49# one who can access services on "localhost" is a local user
 50#http_access deny to_localhost
 51
 52#
 53# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
 54#
 55
 56# Example rule allowing access from your local networks.
 57# Adapt localnet in the ACL section to list your (internal) IP networks
 58# from where browsing should be allowed
 59http_access allow localnet
 60http_access allow localhost
 61
 62# And finally deny all other access to this proxy
 63http_access deny all
 64
 65# Squid normally listens to port 3128
 66http_port 3128
 67
 68# Uncomment and adjust the following to add a disk cache directory.
 69#cache_dir ufs /var/cache/squid 100 16 256
 70
 71# Leave coredumps in the first cache dir
 72coredump_dir /var/cache/squid
 73
 74#
 75# Add any of your own refresh_pattern entries above these.
 76#
 77refresh_pattern ^ftp:144020%10080
 78refresh_pattern ^gophe              r:14400%1440
 79refresh_pattern -i (/cgi-bin/|\?) 00%0
 80refresh_patter                  n .020%4320
 81
 82# nvidia
 83acl free-internet dstdomain .nvidia.com
 84# goo               gle
 85#acl free-internet dstdomain .google.com .gstatic.com .googlesyndication.com .google.cn
 86# twitter
 87acl free-internet dstdomain .twitter.com .twimg.com .branch.io t.co
 88# wikipedia
 89acl free-internet dstdomain .wikipedia.org
 90# facebook
 91acl free-internet dstdomain .facebook.com .fbcdn.net .facebook.net
 92# instagram.com
 93acl free-internet dstdomain .instagram.com .cdninstagram.com
 94# wall street journal
 95acl free-internet dstdomain .wsj.com .wsj.net .cxense.com .adnxs.com
 96# nytimes
 97acl free-internet dstdomain .nytimes.com nyt.com
 98# dw.com
 99acl free-internet dstdomain .dw.com
100# medium
101acl free-internet dstdomain .medium.com
102# evernote
103acl free-internet dstdomain .evernote.com
104# ubuntu
105acl free-internet dstdomain .ubuntu.com
106# kernel
107#acl free-internet dstdomain .kernel.org
108# fedoraproject
109#acl free-internet dstdomain .fedoraproject.org
110# docker
111#acl free-internet dstdomain .docker.com
112# wire
113acl free-internet dstdomain .wire.com
114# dropbox
115acl free-internet dstdomain .dropbox.com
116# misc
117acl free-internet dstdomain .myfonts.net
118# parent proxy:
119cache_peer 127.0.0.1 parent 4128 0 no-query default
120never_direct allow free-internet
121never_direct deny all

SSH Tunnel

在本地树莓派(client squid)和墙外VPS(partent squid)上运行了squid服务之后,我们使用SSH Stunnel来打通连接:

  • 在本地树莓派 pi-master1 上配置 ~/.ssh/config :

apt_proxy_arch/ssh_config
 1Host *
 2    ServerAliveInterval 60
 3    ControlMaster auto
 4    ControlPath ~/.ssh/%h-%p-%r
 5    ControlPersist yes
 6
 7Host parent-squid
 8    HostName parent-squid.huatai.me
 9    User admin
10    LocalForward 4128 127.0.0.1:3128

解析:

  • ServerAliveInterval 60 每60秒保持ssh服务连接1次,确保不被服务器断开

  • ControlMaster ControlPath ControlPersist 可以复用SSH通道,这样即使ssh客户端结束运行,ssh tunnel也保持

  • Compression yes 启用ssh的gzip压缩,这样可以节约网络流量(不过对于软件包下载都是已经压缩过文件,理论上该参数无实际效果反而会增大开销)

  • Host 段落,HostName 是连接墙外VPS服务器的域名,请按照你自己的服务器配置

  • LocalForward 4128 127.0.0.1:3128 将本地树莓派回环地址上的 4128 转发到远程VPS的回环地址 3128 上,这样client squid访问级联本级 4128 就相当于访问远程VPS的 3128 ,就能够将远程VPS上运行的squid作为自己的 parent squid

  • 执行SSH Tunnel命令:

    ssh parent-squid
    
  • 完成ssh连接后,退出,再次访问应该不再需要ssh密码,这表明ssh tunnel的连接始终保持

APT代理配置

  • APT包管理 程序支持代理,由于 pi-master1 上运行的本地squid允许局域网网段 192.168.x.x 访问,所以配置整个局域网使用代理作为APT访问方式,即 /etc/apt/apt.conf.d/proxy.conf 配置:

apt_proxy_arch/proxy.conf
1Acquire::http::Proxy "http://192.168.6.11:3128/";
2Acquire::https::Proxy "http://192.168.6.11:3128/";
  • 在本地任意一台Ubuntu主机上执行更新apt更新:

    sudo apt update
    sudo apt upgrade
    

如果一切正常,则会通过二级代理自由访问因特网。

现在,我们可以做我们想做的: