Nginx作为Ubuntu上的反向代理,用于使用UseRewriter

时间:2017-11-11 08:44:26

标签: c# ubuntu ssl nginx asp.net-core

正如this doc中所述,我按如下方式设置了我的Startup类:

public class Startup
{
    public Startup(IHostingEnvironment environment, IConfiguration configuration)
    {
        Environment = environment;
        Configuration = configuration;
    }

    public IHostingEnvironment Environment { get; }
    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        var secretsFolder = Path.Combine(Configuration["SecretsFolder"].Split('/'));
        services.AddDataProtection()
            .PersistKeysToFileSystem(new DirectoryInfo(Path.Combine(secretsFolder, "keys")));

        services.AddDbContext<ApplicationDbContext>(options =>
        {
            var connectionString = Configuration["MyAppDbConnectionString"] +
                "User ID=" + Configuration["DatabaseUser"] +
                ";Password=" + Configuration["DatabasePassword"] + ";";
            options.UseNpgsql(connectionString);
        });

        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

        services.ConfigureApplicationCookie(options =>
        {
            // Cookie settings
            options.Cookie.Name = ".MyApp.Identity";
            options.Cookie.HttpOnly = true;
            options.Cookie.Expiration = TimeSpan.FromDays(30);
            options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
            options.SlidingExpiration = true;
            options.LoginPath = "/Account/Login";
            options.LogoutPath = "/Account/Logout";
            options.AccessDeniedPath = "/Account/AccessDenied";
        });

        services.AddAntiforgery(options =>
        {
            options.Cookie.Name = ".MyApp.AntiCSRF";
            options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        });

        services.Configure<MvcOptions>(options =>
        {
            // See https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl
            options.Filters.Add(new RequireHttpsAttribute());

            options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());

            // Make authentication compulsory by default; 
            var policy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
            options.Filters.Add(new AuthorizeFilter(policy));
        });

        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app)
    {
        // See https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl
        var options = new RewriteOptions().AddRedirectToHttps();
        app.UseRewriter(options);

        if (Environment.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
            app.UseDatabaseErrorPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();

        app.UseForwardedHeaders(new ForwardedHeadersOptions
        {
            ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
        });

        app.UseAuthentication();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

在Ubuntu 16.04上,我从默认的ubuntu repo安装了Nginx,然后安装了LetsEncrypt。以下是/ etc / nginx / sites-available / default文件的内容:

add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
server {
    listen 80;
    server_name myapp.com;
    root /home/me/myapp/wwwroot;
    location / {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    } # managed by Certbot
}

请注意,我还没有更改/etc/nginx/nginx.conf(其中包含指向/ etc / nginx / sites-available / default文件的包含指令)。

但是当发出https://myapp.com请求时,我的应用会抛出以下错误:

  

防伪系统的配置值为AntiforgeryOptions.Cookie.SecurePolicy = Always,但当前请求不是SSL请求。

谷歌浏览器显示:

  

ERR_TOO_MANY_REDIRECTS

如果我注释掉以下代码:

        //var options = new RewriteOptions().AddRedirectToHttps();
        //app.UseRewriter(options);

错误消失,我的应用程序按预期工作。

请解释一下这个问题。我如何配置nginx,以便我的网络应用程序仍然可以工作,而无需注释掉上述代码?或者我应该摆脱程序化重定向,如果nginx已配置为将http重定向到https?

1 个答案:

答案 0 :(得分:0)

  

谷歌浏览器显示:

     

ERR_TOO_MANY_REDIRECTS

从nginx配置中可以看出,它通过http协议将用户请求转发到后端服务器,而不是https。您可能知道,这是一种常见的做法,您的后端服务器必须位于专用网络中(例如https://www.digitalocean.com/community/tutorials/how-to-set-up-nginx-load-balancing-with-ssl-termination)。

  

proxy_pass http://localhost:5000;

但是你的后端服务器总是返回302重定向,因为

options.Filters.Add(new RequireHttpsAttribute());

// this filter redirects to https

var options = new RewriteOptions().AddRedirectToHttps();
app.UseRewriter(options);

// this rewrite redirects to https

因此,您有两个位置,您的后端服务器将返回302重定向。

这样,当您打开https://myapp.com时,nginx会通过http协议将您的请求转发给您的后端服务器,它会以302重定向到https协议(https://myapp.com)进行响应,并且循环重复。

要解决此问题以及与此问题相关的其他问题,请确保

1)Nginx在通过https收到用户请求后,将X-Forwarded-ForX-Forwarded-Proto标头发送到后端服务器。

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

✓你的nginx配置有

2)Asp Core使用这些标题

app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});

✓您的应用使用标题

3)RewriteOptions().AddRedirectToHttps()部分追踪app.UseForwardedHeaders部分

❌这就是你的应用程序停留在重定向循环中的原因

由于mvc过滤器是在中间件之后应用的,如果您的重写重定向被注释掉,options.Filters.Add(new RequireHttpsAttribute())不会抱怨https。

但是如果同时删除app.UseForwardedHeaders和重写重定向,你会得到另一个重定向循环,但现在它是由mvc过滤器引起的。

并且,如果你删除了过滤器,反伪造系统会通过抛出The antiforgery system has the configuration value AntiforgeryOptions.Cookie.SecurePolicy = Always, but the current request is not an SSL request.异常开始抱怨cookie。