【问题标题】:.Net core X-Forwarded-Proto header doesn't pass to Nginx properly.Net 核心 X-Forwarded-Proto 标头未正确传递给 Nginx
【发布时间】:2020-01-27 04:42:28
【问题描述】:

抱歉编辑历史,但这个问题我真的不清楚,很难找到确切的问题。

我有一个运行在 Nginx 后面的 .Net-Core Web 应用程序,并且 X-Forwarded-Proto 总是通过 http而不是https

Startup.cs

 public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<ForwardedHeadersOptions>(options =>
            {
                options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
            });

            services.AddMvc();

        }

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            //first middlewear
            app.UseForwardedHeaders();
             //and the rest
         }

Nginx 配置

server {
    listen        80;
    server_name   example.com;
    location / {
        proxy_pass         http://localhost:5001/;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

Nginx.conf

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    #cloudflare real ip
    #https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs-Logging-visitor-IP-addresses-with-mod-cloudflare-#12345681
    set_real_ip_from 173.245.48.0/20;
    real_ip_header    X-Forwarded-For;
    real_ip_recursive on;

    log_format  main  '"$scheme" $remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

access.log 记录

"http" 185.108.83.156 - - [03/Oct/2019:19:59:33 +0300] "GET /auth/signin/Facebook?returnUrl=%2F HTTP/1.1" 302 0 "https://example.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36" "156"

如您所见,我记录的 $scheme 始终是 HTTP

解决该问题的一个解决方案是像这样将 Scheme 强制为 HTTPS

app.Use((context, next) =>
        {
            context.Request.Scheme = "https";
            return next();
        });

来自https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-2.2#forwarded-headers-middleware-options

但使用此解决方案,我不会传递标头并丢失一些信息。

那么对于这种情况,有人有什么解决方案吗?

【问题讨论】:

  • 您的服务器日志中还有其他内容吗?
  • @poke 已编辑问题
  • 上面显示的代码对我来说很好用。根据错误信息,我猜是有问题导致您的应用无法获取真正的 https proto。所以请确保: 1. 您是否启用了 app.UseForwardedHeaders() before 其他中间件? 2. 请调用nginx -T 以确保您已启用proxy_set_header X-Forwarded-Proto $scheme; 等标头的代理设置 3. 顺便说一下,请尽快更改您的应用程序机密。
  • 它不允许我将其标记为重复。但是,它可能与 this 链接重复
  • @itminus 这不是真正的应用程序秘密。我启用了app.UseForwardedHeaders() 和nginx 有proxy_set_header X-Forwarded-Proto $scheme;

标签: nginx asp.net-core .net-core http-headers


【解决方案1】:

您的 Startup.cs 文件很好,但您没有为 Kesterl 配置 ssl。

您需要为您的 .net 核心应用程序配置 X.509 ssl 证书

来自https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-2.2#when-to-use-kestrel-with-a-reverse-proxy-1

只有反向代理服务器需要 X.509 证书,并且 服务器可以与内部网络上的应用程序服务器通信 使用纯 HTTP

因此,在您的情况下,您需要执行 7 个主要步骤:
1) 从 CloudFlare ORIGIN CA 获取 SSL 证书
2) 在您的服务器中安装 ssl 证书
3) 创建一个 PFX 文件,以便您的 Kesterl 服务器可以读取它。
4) 配置 Kesterl https 端点。
5) 修改你的 Nginx 服务器监听 443 端口,然后重定向到 Kestrel 监听端口
6) 根据您的配置修改服务文件
7) 将 Cloudflare 更改为运行严格模式

第 1 步 - 从 cloudflare 获取 ssl 证书:
转到 Cloudflare 的仪表板并选择 SSL/TLS --> 源服务器 --> 创建证书。 按照说明进行操作,您将在该过程结束时获得 2 个文件: example.com.key example.com.pem

第 2 步 - 将文件放在服务器中的 etc/ssl 文件夹中

第 3 步 - 创建 PFX 文件,以便 Kesterl 可以使用证书:
在你的服务器上运行它:

openssl pkcs12 -export -out example.pfx -inkey example.com.key -in example.com.pem

这将生成example.pfx,将其放在同一个文件夹中。

第 4 步 - 配置 Kesterl https 端点。
从第 3 步开始,您应该已经获得了在此步骤中需要使用的密码。
在您的appsetting.json 放置以下行:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "Kestrel": {
    "Endpoints": {
      "HTTPS": {
        "Url": "https://localhost:5001",
        "Certificate": {
          "Path": "/etc/ssl/example.pfx",
          "Password": "**********"
        }
      }
    }
  }

第 5 步 - 将 Nginx 配置为侦听端口 443,如下所示:

server {
    #listen        80;
    listen                 *:443 ssl;
    ssl_certificate        /etc/ssl/example.pem;
    ssl_certificate_key    /etc/ssl/example.key;
    server_name            example.com
    location / {
        proxy_pass         https://localhost:5001/;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

第 6 步 - 像这样配置您的服务文件:

  [Unit]
    Description=example

    [Service]
    WorkingDirectory=/var/www/example
    ExecStart=/usr/bin/dotnet /var/www/example/exampleSite.dll
    Restart=always
    # Restart service after 10 seconds if the dotnet service crashes:
    RestartSec=10
    KillSignal=SIGINT
    SyslogIdentifier=example
    User=www-data
    Environment=ASPNETCORE_ENVIRONMENT=Staging
    Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
    Environment=ASPNETCORE_HTTPS_PORT=5001
    Environment=ASPNETCORE_URLS=https://localhost:5001

    [Install]
    WantedBy=multi-user.target

第 7 步 - 在 SSL/TLS 上更改 Cloudflare 以使用严格模式

重新启动您的应用程序 + Nginx,您的服务器现在应该像这样工作:

请求 --> Cloudflare(HTTPS) --> Nginx(HTTPS)-->Example.com

【讨论】:

    【解决方案2】:

    您只在端口 80 上运行 nginx,并且没有 ssl,因此,$scheme 变量将始终为 http。见http://nginx.org/r/$scheme

    如果您关心 https,那么使用 HTTPS 保护 Cloudflare 和您的后端之间的连接可能也是一个好主意;这样,您的 $scheme 变量将被正确填充。否则,您也可以硬编码 https 来代替 $scheme,但这会破坏在您的真实和最终 .Net 后端进行条件测试和重定向的目的。

    【讨论】:

      【解决方案3】:

      通常,HTTP 的标头不接受非 ASCII 字符。 Urls 必须在生成时正确编码,Redirect 方法不会为您执行此操作。

      快速修复可能是使用WebUtility.UrlEncode

      using System.Net;
      // ...
      var encodedLocationName = WebUtility.UrlEncode(locationName);
      return Redirect("~/locations/" + encodedLocationName);
      

      【讨论】:

        【解决方案4】:

        请在您的 nginx 文件中设置适当的字符集。

        这是文档 http://nginx.org/en/docs/http/ngx_http_charset_module.html#charset_types

        【讨论】:

          【解决方案5】:

          这似乎是您的 NGinx 文件和 Cloudflare 设置方式的问题。从配置中,您的设置是

          HTTPS->Cloudflare->HTTP->你的服务器

          这就是为什么在你的 nginx 日志中 Scheme 总是“http”的原因。由于 Cloudflare 通过 HTTP 将请求传递到您的服务器。您应该在 Cloudflare 和您之间设置 TLS,这将在您的 nginx 配置上启用端口 443,这会将方案设置为 HTTPS。强制 dotnet 相信它是通过 HTTPS 处理的,这是无效的,因为 Cloudflare 正在通过公共互联网发送未加密的客户数据。

          https://support.cloudflare.com/hc/en-us/articles/115000479507-Managing-Cloudflare-Origin-CA-certificates

          Cloudflare 的文档列出了如何使用 NGinx 进行设置。

          【讨论】:

            猜你喜欢
            • 2021-12-06
            • 2015-12-31
            • 2018-06-15
            • 2020-11-26
            • 1970-01-01
            • 2013-10-03
            • 2014-06-14
            相关资源
            最近更新 更多