前提
在过去的几天里,我设置了一个运行 Mastodon 和 Docker 的家庭服务器。我已经有一个在 Raspberry 上运行的 Nextcloud 实例,所以现在我正尝试通过专用子域提供这两个实例(我使用的是免费 DDNS)。
我设置了两个子域名(显然都指向我的家庭路由器),如下所示:
- mastodon.example.com
- nextcloud.example.com
然后我在路由器中设置端口转发,将端口 80、443 上的 TCP/UDP 流量发送到 Mastodon 服务器的相同端口,同时将自定义端口 3000、3001 上的 TCP/UDP 流量映射到 Nexcloud 服务器的端口 80、443。
到这里都没有问题,如果我https://mastodon.example.com
在浏览器中输入,我就会进入 Mastodon 服务器,然后https://nextcloud.example.com:3001
我就会进入 Nextcloud 服务器。
问题
现在我正在尝试编辑 mastodon 服务器中 nginx-proxy 容器的配置,以便将每个请求重定向到,以nextcloud.example.com
避免nextcloud.example.com:3001
必须指定端口。
我myadditions.conf
在映射到容器卷的文件夹中创建了一个新文件/etc/nginx/conf.d
,重新启动服务器后,我可以确认 nginx 已正确加载它docker exec nginx-proxy nginx -T
。
我的问题是,我尝试放入文件中以进行重定向的所有内容似乎都被忽略了,我输入时得到的结果nextcloud.example.com
始终相同:错误MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
(我使用的是 Firefox)。我猜想该错误是因为服务器没有尝试重定向调用,因此它提供的证书与 nextcloud 子域不匹配。我是不是遗漏了什么?
目前我的myadditions.conf
文件如下所示:
server {
server_name nextcloud.example.com;
return 301 $scheme://nextcloud.example.com:3001$request_uri;
}
但我尝试了很多设置,结果没有任何变化,所以我开始认为问题不在于我在文件中写入的内容。顺便说一句,如果我删除我的文件,错误也是一样的。
答案1
您正在连接到 HTTPS URL,因此应答的服务器必须为该域设置 HTTPS – 它不能在没有完全建立 TLS 连接的情况下仅提供纯文本重定向。(如果浏览器允许这样做,那么攻击者做同样的事情就很容易了。)
你的服务器块没有指定它将监听 TLS 端口,也没有定义任何证书,所以它很可能不会被选中用于请求——Nginx 最终会选择第一个服务器块是标记为支持 TLS 并使用在那里定义的任何证书。
请记住,30x
重定向是客户端的——服务器只返回新的 URL,客户端访问该 URL。你不需要进入非标准端口,但它会在地址栏中可见;nginx-proxy 容器实际上不会执行任何代理。
如果您需要代理,那么您需要使用proxy_pass
而不是return
。在这种情况下,Nginx 仍然需要有效的 HTTPS 设置。
Nginx 在没有访问域的匹配证书的情况下处理此问题的唯一其他方法是根据通过 TLS SNI 报告的主机名“盲目”代理所有数据(流代理模式),以便浏览器与后端服务器进行 HTTPS 握手。我不确定 Nginx 是否具有此功能,尽管其他代理有此功能。
答案2
以下是我解决问题所采取的步骤。
1. 获取附加证书
我创建了一个letsencrypt_user_data
文件并将其映射到容器nginxproxy/acme-companion
,以指示它请求并保持更新nextcloud.example.com
子域的附加证书(请参阅独立证书在容器文档中)。这是我现在的文件:
LETSENCRYPT_STANDALONE_CERTS=('NextCloudCert')
LETSENCRYPT_NextCloudCert_HOST=('nextcloud.example.com')
LETSENCRYPT_NextCloudCert_EMAIL='[email protected]'
LETSENCRYPT_NextCloudCert_TEST=false
2. 修正 nginx 重定向
我编辑了myadditions.conf
在 Nginx 配置文件夹中创建的文件:我将重定向类型从 更改为return 301
,proxy_pass
并添加了一个server
上下文,以重定向端口 443 上的流量并指定新证书。文件现在如下所示,请记住,我只是复制并编辑了它,它能用,但我真的不知道它所包含的大部分内容的含义,所以这可能是一个不好的例子:
log_format logFormatNextcloud '$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$upstream_addr"';
server {
server_name nextcloud.example.com;
listen 80 ;
access_log /var/log/nginx/access.log logFormatNextcloud;
# Do not HTTPS redirect Let's Encrypt ACME challenge
location ^~ /.well-known/acme-challenge/ {
auth_basic off;
auth_request off;
allow all;
root /usr/share/nginx/html;
try_files $uri =404;
break;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
server_name nextcloud.example.com;
access_log /var/log/nginx/access.log logFormatNextcloud;
listen 443 ssl http2 ;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_certificate /etc/nginx/certs/nextcloud.example.com.crt;
ssl_certificate_key /etc/nginx/certs/nextcloud.example.com.key;
ssl_dhparam /etc/nginx/certs/nextcloud.example.com.dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/certs/nextcloud.example.com.chain.pem;
set $sts_header "";
if ($https) {
set $sts_header "max-age=31536000";
}
add_header Strict-Transport-Security $sts_header always;
include /etc/nginx/vhost.d/default;
location / {
proxy_pass https://nextcloud.example.com:3001;
set $upstream_keepalive false;
}
}
现在两个子域名都可以使用了。谢谢@u1686_grawity,你的回答包含了我需要的所有提示和解释。