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
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
acl SSL_ports port 443
acl Safe_ports port 80		# http
acl Safe_ports port 21		# ftp
acl Safe_ports port 443		# https
acl Safe_ports port 70		# gopher
acl Safe_ports port 210		# wais
acl Safe_ports port 1025-65535	# unregistered ports
acl Safe_ports port 280		# http-mgmt
acl Safe_ports port 488		# gss-http
acl Safe_ports port 591		# filemaker
acl Safe_ports port 777		# multiling http
acl CONNECT method CONNECT
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
http_access allow localhost
http_access deny all
http_port 127.0.0.1:3128
coredump_dir /var/spool/squid
refresh_pattern ^ftp:		1440	20%	10080
refresh_pattern ^gopher:	1440	0%	1440
refresh_pattern -i (/cgi-bin/|\?) 0	0%	0
refresh_pattern (Release|Packages(.gz)*)$      0       20%     2880
refresh_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
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#
# Recommended minimum configuration:
#

# Example rule allowing access from your local networks.
# Adapt to list your (internal) IP networks from where browsing
# should be allowed
acl localnet src 0.0.0.1-0.255.255.255# RFC 1122 "this" network (LAN)
acl localne t src 10.0.0.0/8# RFC 1918 local private network (LAN)
acl localnet s      rc 100.64.0.0/10# RFC 6598 shared address space (CGN)
acl localnet sr     c 169.254.0.0/16    # RFC 3927 link-local (directly plugged) machines
acl localnet src 172.16.0.0/12# RFC 1918 local private network (LAN)
acl localnet src 192.       168.0.0/16# RFC 1918 local private network (LAN)
acl localnet src 11.        159.0.0/16
acl localnet src 11.124.0.0/16
acl localnet src fc00::/7           # RFC 4193 local private network range
acl localnet src fe80::/10          # RFC 4291 link-local (directly plugged) machines

acl localnet src 30.0.0.0/8

acl SSL_ports port 443
acl Safe_ports port 80# http
acl Safe_ports port 21              # ftp
acl Safe_ports port 443# https
acl Safe_ports port 70# gopher              
acl Safe_ports port 210# wais
acl Safe_ports port 1025-65535# unreg           istered ports
acl Safe_ports port 280# http-mgmt
acl Safe_ports port         488# gss-http
acl Safe_ports port 591# filemaker
acl Safe_ports por              t 777# multiling http
acl CONNECT method CONNECT

#
# Recommended min       imum Access Permission configuration:
#
# Deny requests to certain unsafe ports
http_access deny !Safe_ports

# Deny CONNECT to other than secure SSL ports
http_access deny CONNECT !SSL_ports

# Only allow cachemgr access from localhost
http_access allow localhost manager
http_access deny manager

# We strongly recommend the following be uncommented to protect innocent
# web applications running on the proxy server who think the only
# one who can access services on "localhost" is a local user
#http_access deny to_localhost

#
# INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS
#

# Example rule allowing access from your local networks.
# Adapt localnet in the ACL section to list your (internal) IP networks
# from where browsing should be allowed
http_access allow localnet
http_access allow localhost

# And finally deny all other access to this proxy
http_access deny all

# Squid normally listens to port 3128
http_port 3128

# Uncomment and adjust the following to add a disk cache directory.
#cache_dir ufs /var/cache/squid 100 16 256

# Leave coredumps in the first cache dir
coredump_dir /var/cache/squid

#
# Add any of your own refresh_pattern entries above these.
#
refresh_pattern ^ftp:144020%10080
refresh_pattern ^gophe              r:14400%1440
refresh_pattern -i (/cgi-bin/|\?) 00%0
refresh_patter                  n .020%4320

# nvidia
acl free-internet dstdomain .nvidia.com
# goo               gle
#acl free-internet dstdomain .google.com .gstatic.com .googlesyndication.com .google.cn
# twitter
acl free-internet dstdomain .twitter.com .twimg.com .branch.io t.co
# wikipedia
acl free-internet dstdomain .wikipedia.org
# facebook
acl free-internet dstdomain .facebook.com .fbcdn.net .facebook.net
# instagram.com
acl free-internet dstdomain .instagram.com .cdninstagram.com
# wall street journal
acl free-internet dstdomain .wsj.com .wsj.net .cxense.com .adnxs.com
# nytimes
acl free-internet dstdomain .nytimes.com nyt.com
# dw.com
acl free-internet dstdomain .dw.com
# medium
acl free-internet dstdomain .medium.com
# evernote
acl free-internet dstdomain .evernote.com
# ubuntu
acl free-internet dstdomain .ubuntu.com
# kernel
#acl free-internet dstdomain .kernel.org
# fedoraproject
#acl free-internet dstdomain .fedoraproject.org
# docker
#acl free-internet dstdomain .docker.com
# wire
acl free-internet dstdomain .wire.com
# dropbox
acl free-internet dstdomain .dropbox.com
# misc
acl free-internet dstdomain .myfonts.net
# parent proxy:
cache_peer 127.0.0.1 parent 4128 0 no-query default
never_direct allow free-internet
never_direct deny all

SSH Tunnel

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

  • 在本地树莓派 pi-master1 上配置 ~/.ssh/config :
apt_proxy_arch/ssh_config
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Host *
    ServerAliveInterval 60
    ControlMaster auto
    ControlPath ~/.ssh/%h-%p-%r
    ControlPersist yes

Host parent-squid
    HostName parent-squid.huatai.me
    User admin
    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
1
2
Acquire::http::Proxy "http://192.168.6.11:3128/";
Acquire::https::Proxy "http://192.168.6.11:3128/";
  • 在本地任意一台Ubuntu主机上执行更新apt更新:

    sudo apt update
    sudo apt upgrade
    

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

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