一次非常坑的内网 Yum 源代理排查:CentOS 7、Nginx Stream、EPEL Archive 与 User-Agent 403
最近遇到一个很隐蔽的问题:一台内网 CentOS 7 服务器不能直接访问公网 Yum 源,需要通过一台内网中转机,再转发到云端 Nginx,由云端 Nginx 代理公网镜像站。
看起来只是一个“给内网机器配 Yum 源”的小需求,实际踩了几个坑:
- CentOS 7 已经 EOL,不能继续用普通 CentOS 源;
- EPEL 7 也需要走归档源;
- Nginx
stream只能做 TCP 转发,不能改 HTTP 头; - 云端竟然会因为老版本
curl/ Yum 的请求头返回403 Forbidden; yum makecache报错时,要区分是404路径错误还是403请求被拦截。
本文记录完整排查过程和最终方案。
一、网络结构
假设内网有一台 CentOS 7 服务器:
10.16.46.5
它不能直接访问公网 Yum 源。
还有一台中转服务器:
10.31.21.72
它可以访问云端域名:
www.foobar.com
目标链路是:
10.16.46.5
-> 10.31.21.72:7112
-> www.foobar.com:7112
-> 公网镜像站
最终希望 10.16.46.5 使用如下 Yum 源:
http://10.31.21.72:7112/centos-vault/...
http://10.31.21.72:7112/epel-archive/...
二、CentOS 7 不应该再用普通 /centos/7/
CentOS 7 已经 EOL,因此普通镜像路径可能不可用、不完整或被清理。
不要再依赖:
/centos/7/os/x86_64/
/centos/7/updates/x86_64/
/centos/7/extras/x86_64/
更稳的是走 Vault:
/centos-vault/centos/7.9.2009/os/x86_64/
/centos-vault/centos/7.9.2009/updates/x86_64/
/centos-vault/centos/7.9.2009/extras/x86_64/
所以 CentOS 基础源应该指向:
centos-vault/centos/7.9.2009
三、EPEL 7 也要注意归档路径
一开始我以为 EPEL 7 可以直接走:
/epel/7/x86_64/
或者:
/epel/7/Everything/x86_64/
结果都可能遇到:
HTTP Error 404 - Not Found
最后确认 EPEL 7 应使用归档路径:
/epel-archive/7/x86_64/
也就是 Yum repo 里写:
baseurl=http://10.31.21.72:7112/epel-archive/7/$basearch/
而不是:
baseurl=http://10.31.21.72:7112/epel/7/$basearch/
四、云端 Nginx 配置
云端 www.foobar.com 的 Nginx 监听 7112,负责代理公网镜像站。
示例配置:
server {
listen 7112 default_server;
server_name _ www.foobar.com;
location /ubuntu/ {
proxy_pass https://ftp.sjtu.edu.cn/ubuntu/;
proxy_ssl_server_name on;
proxy_set_header Host ftp.sjtu.edu.cn;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
location /centos-vault/ {
proxy_pass https://mirrors.ustc.edu.cn/centos-vault/;
proxy_ssl_server_name on;
proxy_set_header Host mirrors.ustc.edu.cn;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
location /epel-archive/ {
proxy_pass https://mirrors.aliyun.com/epel-archive/;
proxy_ssl_server_name on;
proxy_set_header Host mirrors.aliyun.com;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
修改后检查并重载:
sudo nginx -t
sudo systemctl reload nginx
五、中转机 Nginx Stream 配置
中转机 10.31.21.72 起初使用 Nginx stream 做 TCP 转发:
server {
listen 10.31.21.72:7112;
proxy_pass www.foobar.com:7112;
proxy_connect_timeout 10s;
proxy_timeout 1h;
}
注意:这段配置必须写在 stream {} 里,而不是 http {} 里。
例如 /etc/nginx/nginx.conf:
stream {
include /etc/nginx/stream.d/*.conf;
}
然后把 TCP 转发规则放到:
/etc/nginx/stream.d/foobar.conf
六、一个容易误判的点:stream 里不能写 location
排查时,我想在中转机上加一个 /whoami:
location = /whoami {
default_type text/plain;
return 200 "remote_addr=$remote_addr\n";
}
结果 Nginx 报错:
nginx: [emerg] "location" directive is not allowed here
原因是当前配置在 stream 模块中。
stream 是四层 TCP 转发,不解析 HTTP,自然没有 location 概念。
如果要用 location,必须写在 http {} 的 server {} 里。
七、给 Nginx Stream 加日志
为了确认内网机器请求是否真的经过中转机,可以给 stream 加日志。
在 nginx.conf 的 stream {} 里加:
stream {
log_format stream_basic '$remote_addr:$remote_port -> $server_addr:$server_port '
'upstream=$upstream_addr status=$status '
'bytes_sent=$bytes_sent bytes_received=$bytes_received '
'session_time=$session_time';
access_log /var/log/nginx/stream-access.log stream_basic;
include /etc/nginx/stream.d/*.conf;
}
然后:
sudo nginx -t
sudo systemctl reload nginx
sudo tail -f /var/log/nginx/stream-access.log
如果从 10.16.46.5 发起请求,日志类似:
10.16.46.5:57030 -> 10.31.21.72:7112 upstream=203.0.113.10:7112 status=200 bytes_sent=329 bytes_received=140 session_time=1.657
这里的 status=200 只表示 TCP 转发成功,不代表 HTTP 返回 200。
这一点很重要。
即使客户端看到:
HTTP/1.1 403 Forbidden
stream 日志里仍然可能是:
status=200
因为 HTTP 403 是七层状态码,而 stream 日志记录的是四层连接状态。
八、最坑的问题:老版本 curl / Yum 请求被云端返回 403
中转链路打通后,仍然遇到这个现象。
在中转机上访问:
curl -I http://10.31.21.72:7112/centos-vault/centos/7.9.2009/os/x86_64/repodata/repomd.xml
返回:
HTTP/1.1 200 OK
但在内网 CentOS 7 机器上访问同一个 URL:
curl -I http://10.31.21.72:7112/centos-vault/centos/7.9.2009/os/x86_64/repodata/repomd.xml
返回:
HTTP/1.1 403 Forbidden
乍看非常离谱,因为 URL 一样、Host 一样、路径一样。
后来对比发现,两台机器的 curl 版本不同:
中转机:curl/7.81.0
CentOS 7:curl/7.29.0
在 CentOS 7 上手动模拟新版 curl:
curl -I -A 'curl/7.81.0' \
http://10.31.21.72:7112/centos-vault/centos/7.9.2009/os/x86_64/repodata/repomd.xml
居然直接返回:
HTTP/1.1 200 OK
这说明云端或上游某处根据 User-Agent 或请求头特征拦截了老版本客户端。
九、让 Yum 使用固定 User-Agent
既然问题是请求头,可以在 CentOS 7 的 /etc/yum.conf 中设置:
user_agent=curl/7.81.0
可以用命令自动添加或替换:
grep -q '^user_agent=' /etc/yum.conf \
&& sudo sed -i 's#^user_agent=.*#user_agent=curl/7.81.0#' /etc/yum.conf \
|| echo 'user_agent=curl/7.81.0' | sudo tee -a /etc/yum.conf
然后执行:
sudo yum clean all
sudo yum makecache
十、最终 Yum repo 配置
/etc/yum.repos.d/CentOS-Base.repo
[base]
name=CentOS-7.9.2009 - Base - via 10.31.21.72
baseurl=http://10.31.21.72:7112/centos-vault/centos/7.9.2009/os/$basearch/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
[updates]
name=CentOS-7.9.2009 - Updates - via 10.31.21.72
baseurl=http://10.31.21.72:7112/centos-vault/centos/7.9.2009/updates/$basearch/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
[extras]
name=CentOS-7.9.2009 - Extras - via 10.31.21.72
baseurl=http://10.31.21.72:7112/centos-vault/centos/7.9.2009/extras/$basearch/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
[centosplus]
name=CentOS-7.9.2009 - CentOSPlus - via 10.31.21.72
baseurl=http://10.31.21.72:7112/centos-vault/centos/7.9.2009/centosplus/$basearch/
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
[contrib]
name=CentOS-7.9.2009 - Contrib - via 10.31.21.72
baseurl=http://10.31.21.72:7112/centos-vault/centos/7.9.2009/contrib/$basearch/
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
/etc/yum.repos.d/CentOS-Vault.repo
这个文件可以保留,但建议全部禁用,避免重复源干扰。
[C7.9.2009-base]
name=CentOS-7.9.2009 - Base - Vault via 10.31.21.72
baseurl=http://10.31.21.72:7112/centos-vault/centos/7.9.2009/os/$basearch/
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
[C7.9.2009-updates]
name=CentOS-7.9.2009 - Updates - Vault via 10.31.21.72
baseurl=http://10.31.21.72:7112/centos-vault/centos/7.9.2009/updates/$basearch/
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
[C7.9.2009-extras]
name=CentOS-7.9.2009 - Extras - Vault via 10.31.21.72
baseurl=http://10.31.21.72:7112/centos-vault/centos/7.9.2009/extras/$basearch/
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
[C7.9.2009-centosplus]
name=CentOS-7.9.2009 - CentOSPlus - Vault via 10.31.21.72
baseurl=http://10.31.21.72:7112/centos-vault/centos/7.9.2009/centosplus/$basearch/
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
/etc/yum.repos.d/epel.repo
[epel]
name=Extra Packages for Enterprise Linux 7 - x86_64 - via 10.31.21.72 archive
baseurl=http://10.31.21.72:7112/epel-archive/7/$basearch/
enabled=1
gpgcheck=1
gpgkey=http://10.31.21.72:7112/epel-archive/RPM-GPG-KEY-EPEL-7
[epel-debuginfo]
name=Extra Packages for Enterprise Linux 7 - x86_64 - Debug - via 10.31.21.72 archive
baseurl=http://10.31.21.72:7112/epel-archive/7/$basearch/debug/
enabled=0
gpgcheck=1
gpgkey=http://10.31.21.72:7112/epel-archive/RPM-GPG-KEY-EPEL-7
[epel-source]
name=Extra Packages for Enterprise Linux 7 - Source - via 10.31.21.72 archive
baseurl=http://10.31.21.72:7112/epel-archive/7/SRPMS/
enabled=0
gpgcheck=1
gpgkey=http://10.31.21.72:7112/epel-archive/RPM-GPG-KEY-EPEL-7
十一、验证命令
先验证 CentOS Vault:
curl -I -A 'curl/7.81.0' \
http://10.31.21.72:7112/centos-vault/centos/7.9.2009/os/x86_64/repodata/repomd.xml
curl -I -A 'curl/7.81.0' \
http://10.31.21.72:7112/centos-vault/centos/7.9.2009/updates/x86_64/repodata/repomd.xml
curl -I -A 'curl/7.81.0' \
http://10.31.21.72:7112/centos-vault/centos/7.9.2009/extras/x86_64/repodata/repomd.xml
再验证 EPEL Archive:
curl -I -A 'curl/7.81.0' \
http://10.31.21.72:7112/epel-archive/7/x86_64/repodata/repomd.xml
然后刷新 yum:
sudo yum clean all
sudo yum makecache
yum repolist
正常情况下应该看到:
base/x86_64 CentOS-7.9.2009 - Base
updates/x86_64 CentOS-7.9.2009 - Updates
extras/x86_64 CentOS-7.9.2009 - Extras
epel/x86_64 Extra Packages for Enterprise Linux 7
示例:
repolist: 30275
十二、yum makecache 和 yum update 的区别
从 Ubuntu 过来容易混淆。
大致可以这么类比:
Ubuntu/Debian:
apt update = 刷新软件包索引
apt upgrade = 升级已安装软件包
CentOS/RHEL 7:
yum makecache = 刷新软件包缓存
yum update = 升级已安装软件包
yum install xxx = 安装软件包
所以验证 Yum 源是否可用时,应该先执行:
sudo yum clean all
sudo yum makecache
yum repolist
不要一上来就:
sudo yum update
因为 yum update 会升级系统已有软件包,现场环境里最好放到维护窗口执行。
十三、几个排查经验
1. 404 和 403 是两类问题
404 Not Found 通常是路径错了,比如:
/epel/7/x86_64/
实际上应该是:
/epel-archive/7/x86_64/
403 Forbidden 则说明路径可能已经到了,但被服务端策略拒绝了,比如 User-Agent 被拦。
2. Nginx Stream 日志里的 status=200 不是 HTTP 200
stream 是 TCP 层转发。
所以这个:
status=200
只说明 TCP 连接成功。
客户端实际收到的 HTTP 状态可能是:
HTTP/1.1 403 Forbidden
3. 本机 curl 和其他机器 curl 同一个地址,不一定等价
例如:
curl http://10.31.21.72:7112/...
在 10.31.21.72 本机执行和在 10.16.46.5 执行,看起来 URL 一样,但 TCP 来源 IP 不同、curl 版本不同、请求头长度也可能不同。
本次问题里,最终关键差异就是:
curl/7.29.0 -> 403
curl/7.81.0 -> 200
4. 软件源代理推荐用 HTTP 反代,而不是 Stream
stream 可以工作,但它只是原样转发 TCP 字节流,不能改 Host、User-Agent 等 HTTP 头。
如果代理的是 Yum/Apt 这类 HTTP 仓库,建议中转机也使用 HTTP 反代:
server {
listen 10.31.21.72:7112 default_server;
server_name _;
location /centos-vault/ {
proxy_pass http://www.foobar.com:7112/centos-vault/;
proxy_set_header Host www.foobar.com;
proxy_set_header User-Agent "curl/7.81.0";
proxy_http_version 1.1;
proxy_set_header Connection "";
}
location /epel-archive/ {
proxy_pass http://www.foobar.com:7112/epel-archive/;
proxy_set_header Host www.foobar.com;
proxy_set_header User-Agent "curl/7.81.0";
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
这样可以把内网各种老系统的请求统一整理后再发往云端,减少奇怪问题。
十四、最终结论
这次问题不是单一配置错误,而是多个因素叠加:
CentOS 7 EOL
+ EPEL 7 归档路径变化
+ 内网不能直接访问公网
+ Nginx stream 只能四层转发
+ 老版本 curl/Yum 请求头被云端拦截
最终可用方案是:
CentOS 7 base/updates/extras -> centos-vault/centos/7.9.2009
EPEL 7 -> epel-archive/7/x86_64
内网访问地址 -> http://10.31.21.72:7112
必要时在 yum.conf 设置 -> user_agent=curl/7.81.0
验证成功后:
sudo yum clean all
sudo yum makecache
yum repolist
看到 base、updates、extras、epel 都正常出现,就说明源已经配置成功。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)