最近遇到一个很隐蔽的问题:一台内网 CentOS 7 服务器不能直接访问公网 Yum 源,需要通过一台内网中转机,再转发到云端 Nginx,由云端 Nginx 代理公网镜像站。

看起来只是一个“给内网机器配 Yum 源”的小需求,实际踩了几个坑:

  1. CentOS 7 已经 EOL,不能继续用普通 CentOS 源;
  2. EPEL 7 也需要走归档源;
  3. Nginx stream 只能做 TCP 转发,不能改 HTTP 头;
  4. 云端竟然会因为老版本 curl / Yum 的请求头返回 403 Forbidden
  5. 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.confstream {} 里加:

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 makecacheyum 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

看到 baseupdatesextrasepel 都正常出现,就说明源已经配置成功。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐