# Stunnel 加密 Squid 代理服务

加密代理功能的实现，越过封锁限制，访问被墙的国外站点，比如 Google、FaceBook。

用户访问配置了代理服务的浏览器（可使用扩展实现自动切换代理，代理服务即国内 stunnel 监听地址）， 国内 stunnel 开启加密连接国外 stunnel 再连接国外 squid 真实代理服务器请求实际网站并返回数据， 如下架构图：

![](https://2134947750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvILwbD2PrkBCkSitM3m4%2Fuploads%2F30eXmY1LTJklLeam3wmb%2Fstunnel-squid-arch.png?alt=media\&token=55d6f32b-9f32-4019-99e0-a49bc51c4fb5)

按照上述图，也就是需要两台服务器，不过可以简化，国内 stunnel 可以直接在用户个人笔记本 / 台式机 启动，因为 stunnel 程序支持 windows、macOS、Linux。

以下步骤适用于 CentOS/RHEL 7.x 系统，stunnel 版本 4.56

## 一、国外的服务器安装 squid 和 stunnel

### 1. 安装 Squid

```
yum -y install squid openssl openssl-devel
```

### 2. 生成证书

```bash
openssl genrsa -out key.pem 2048
openssl req -new -x509 -days 3650 -key key.pem -out cert.pem -subj "/C=CN/ST=HeNan/L=ZhengZhou/O=devops/OU=know/CN=devops.com"

# ls
cert.pem  key.pem
```

squid 配置文件中添加：

```bash
https_port 4430 cert=/path/to/cert.pem key=/path/to/key.pem       # SSL PATH
```

/etc/squid/squid.conf 我的全部配置信息，按需修改

```bash
acl localnet src 127.0.0.1/8
acl localnet src <stunnel_ip>


http_access allow localhost 
http_access allow localnet 
http_access deny all

# PORT AND SSL PATH, 提供个 stunnel 高匿隧道
https_port 4430 cert=/path/to/cert.pem key=/path/to/key.pem

# 定义 squid 的cache 存放路径、cache目录容量、一级缓存目录数量、二级缓存目录数量
cache_dir ufs /var/spool/squid 100 16 256
coredump_dir /var/spool/squid

# 设置squid磁盘缓存最大文件，超过4M的不保存在硬盘
maximum_object_size_in_memory 4 MB

fqdncache_size 1024
cache_mem 300 MB
max_open_disk_fds 0  
minimum_object_size 1 KB
maximum_object_size 20 MB 

cache_swap_low 90 0%时停止

# 目录使用量大于95%时，开始清理旧的
cache_swap_high 95
ipcache_size 2048 
ipcache_low 90
ipcache_high 95 
refresh_pattern . 0 20% 4320 override-expire override-lastmod reload-into-ims ignore-reload

# Add any of your own refresh_pattern entries above these.

refresh_pattern ^ftp:           1440    20%     10080
refresh_pattern ^gopher:        1440    0%      1440
refresh_pattern -i (/cgi-bin/|\?) 0     0%      0
refresh_pattern .               0       20%     4320

# 高匿
request_header_access via deny all
request_header_access X-Forwarded-For deny all
```

### 3. 启动 squid

```bash
# 初始化检测，并启动
squid -z 
systemctl enabled squid && systemctl start squid
```

## 二、国内服务器安装 stunnel

### 1. 安装 stunnel

```
yum -y install stunnel
```

### 2. 修改配置文件

```ini
# vi /etc/stunnel/stunnel.conf  默认没有这个文件,手动创建，默认读取这个文件
client=yes

[sproxy]
accept  = 0.0.0.0:7071
connect = 国外服务器IP:4430
```

解释说明：

accept = 0.0.0.0:7071 即是用户要开放的端口，

connect = 国外服务器 IP:4430 squid 服务器地址

### 3. 启动 stunnel 服务

```bash
stunnel 
# 或者
stunnel /etc/stunnel/stunnel.conf
```

此时用户连接到国内服务器 7071 作为代理就可以访问一些被墙的网站。

在国内没有服务器，可以在本地安装的一个 Stunnel，用来加解密，所以没有国内服务也不要紧。

去 stunnel 官网下载一个 windows 版的客户端（也许被 qiang，[这里](https://static.saintic.com/download/soft/stunnel-5.55-win64-installer.exe)是 5.55，可直接下载）

### 注意: 同步时间

> 最好同步下国外服务器的时间，并与国内保持一致时区，否则有可能 SSL 验证错误。

`stunnel.pem` 详细参数

```ini
;;; CAfile = /etc/stunnel/stunnel.pem
;;; socket = l:TCP_NODELAY=1
;;; socket = r:TCP_NODELAY=1

;;;chroot = /var/run/stunnel
pid = /tmp/stunnel.pid
;;; verify = 3

;;; CApath = certs
;;; CRLpath = crls
;;; CRLfile = crls.pem

;;; setuid = stunnel
;;; setgid = stunnel

client=yes
compression = zlib
;;; taskbar = no
;;; delay = no
;;; failover = rr
;;; failover = prio
;;; sslVersion = TLSv1
;;; fips=no

;;; debug = 7
;;; syslog = no
output = /etc/stunnel/stunnel.log

[sproxy]
accept = 34567
connect = 127.0.0.1:3128
```

## 三、Ansible 批量部署

```bash
[root@localhost squid]# tree
.
|-- hosts
|-- install.yml
`-- roles
    |-- squid
    |   |-- files
    |   |   |-- cert.pem
    |   |   `-- key.pem
    |   |-- handlers
    |   |   `-- main.yml
    |   |-- tasks
    |   |   `-- main.yml
    |   `-- templates
    |       `-- squid.conf.j2
    `-- stunnel
        |-- handlers
        |   `-- main.yml
        |-- tasks
        |   `-- main.yml
        `-- templates
            `-- stunnel.conf.j2
```

### hosts 和 install.yml

```bash
[root@localhost squid]# more hosts install.yml 
::::::::::::::
hosts
::::::::::::::
[squid]
192.168.1.33


[stunnel]
192.168.1.33


[all:vars]
ansible_ssh_port=22
ansible_ssh_user=root
ansible_ssh_pass=1
squid_ssl_port=4430


::::::::::::::
install.yml
::::::::::::::
---
- name: 'Deployment to squid'
  gather_facts: false
  hosts: squid
  roles:
    - { role: 'squid', tags: "install_squid" }

- name: 'Deployment to stunnel'
  gather_facts: false
  hosts: stunnel
  roles:
    - { role: 'stunnel', tags: "install_stunnel" }
```

### squid 相关配置

```bash
[root@localhost squid]# more roles/squid/handlers/main.yml roles/squid/tasks/main.yml roles/squid/templates/squid.conf.j2
::::::::::::::
roles/squid/handlers/main.yml
::::::::::::::
- name: restart squid
  systemd:
    name: squid
    state: restarted
    enabled: yes
::::::::::::::
roles/squid/tasks/main.yml
::::::::::::::
- name: "Install squid stunnel openssl openssl-devel"
  yum:
    name: "{{ item.line }}"
    state: installed
  with_items:
    - {line: 'squid'}
    - {line: 'openssl'}
    - {line: 'openssl-devel'}

- name: 'Copy Squid pem to remote'
  copy: 
    src: "{{ item.src }}"
    dest: "{{ item.dest }}"
  with_items:
    - {src: 'key.pem', dest: '/etc/squid/key.pem'}
    - {src: 'cert.pem', dest: '/etc/squid/cert.pem'}

- name: 'template squid conf to remote'
  template:
    src: "{{ item.src }}"
    dest: "{{ item.dest }}"
  with_items:
    - {src: "squid.conf.j2", dest: "/etc/squid/squid.conf"}
  notify:
   - restart squid

- name: 'Crontab for restart squid'
  cron:
    name: restartSquid
    state: present
    hour: '23'
    job: "sync && echo 3 >/proc/sys/vm/drop_caches && rm -f /var/log/squid/access.log ; systemctl restart squid"

- name: 'Crontab for check squid'
  cron:
    name: CheckSquid
    state: present
    minute: '*'
    job: 'sleep 5 &&  systemctl status squid || systemctl start squid'

- name: 'See Port'
  shell: 'ss -tnlp |grep squid'
  register: return_status

- debug: 
    # msg: '{{ return_status.stdout_lines }}'
    var: return_status.stdout_lines
  #when: return_status.rc != 0
::::::::::::::
roles/squid/templates/squid.conf.j2
::::::::::::::
acl localnet src 127.0.0.1/8
#acl localnet src {{ ansible_ssh_host }}

{% for host in groups['stunnel'] %}

acl localnet src {{ host }}

{% endfor %}




http_access allow localhost 
http_access allow localnet 
http_access deny all


# PORT AND SSL PATH, 提供个 stunnel 高匿隧道
https_port {{ squid_ssl_port }} cert=/etc/squid/cert.pem key=/etc/squid/key.pem

# 定义 squid 的cache 存放路径、cache目录容量、一级缓存目录数量、二级缓存目录数量
cache_dir ufs /var/spool/squid 100 16 256
coredump_dir /var/spool/squid


# 设置squid磁盘缓存最大文件，超过4
maximum_object_size_in_memory 4 MB

fqdncache_size 1024
cache_mem 300 MB
max_open_disk_fds 0  
minimum_object_size 1 KB
maximum_object_size 20 MB 

cache_swap_low 90 0%时停
# 目录使用量大于95%时，开始清理旧的
cache_swap_high 95
ipcache_size 2048 
ipcache_low 90
ipcache_high 95 
refresh_pattern . 0 20% 4320 override-expire override-lastmod reload-into-ims ignore-reload

# Add any of your own refresh_pattern entries above these.

refresh_pattern ^ftp:           1440    20%     10080
refresh_pattern ^gopher:        1440    0%      1440
refresh_pattern -i (/cgi-bin/|\?) 0     0%      0
refresh_pattern .               0       20%     4320

# 高匿
request_header_access via deny all
request_header_access X-Forwarded-For deny all
```

### stunnel相关配置

```bash
[root@localhost squid]# more roles/stunnel/handlers/main.yml roles/stunnel/tasks/main.yml roles/stunnel/templates/stunnel.conf.j2     
::::::::::::::
roles/stunnel/handlers/main.yml
::::::::::::::
- name: restart stunnel
  shell: "ps -ef |grep stunnel |grep -v grep  |awk '{print $2}' |xargs kill -9  || stunnel"
::::::::::::::
roles/stunnel/tasks/main.yml
::::::::::::::
- name: "Install stunnel"
  yum:
    name: "{{ item.line }}"
    state: installed
  with_items:
    - {line: 'stunnel'}



- name: 'Template stunnel conf to remote'
  template:
    src: "{{ item.src }}"
    dest: "{{ item.dest }}"
  with_items:
    - {src: "stunnel.conf.j2", dest: "/etc/stunnel/stunnel.conf"}
  notify:
   - restart stunnel


- name: 'See Port'
  shell: 'ss -tnlp |grep stunnel'
  register: return_status


- debug: var=return_status.stdout_lines
  when: return_status.rc == 0
::::::::::::::
roles/stunnel/templates/stunnel.conf.j2
::::::::::::::
client=yes

{% for host in groups['squid'] %}

[squid_{{ loop.index }}]
accept  = 882{{ loop.index }}
connect = {{ host }}:{{ squid_ssl_port }}


{% endfor %}
```
