reverse proxy-ს უკან ASP.NET Core ყველა მოთხოვნას ხედავს, როგორც უბრალო HTTP-ს ერთი მისამართიდან - proxy-ისგან - სანამ სხვა რამეს არ ეტყვი. გამოსავალი ერთი middleware-ია, UseForwardedHeaders, რომელიც კონფიგურირებულია X-Forwarded-For-ისა და X-Forwarded-Proto-ს წასაკითხად, რეგისტრირებულია ყველაფერზე ადრე და ზუსტად იცის, რომელ proxy-ს შეიძლება დაუჯეროს. სწორად გააკეთე და კლიენტის IP, scheme, HTTPS გადამისამართება, HSTS, უსაფრთხო cookie-ები და OAuth callback-ები თავისით გასწორდება. შეცდი ზედმეტი ნდობის მიმართულებით და ნებისმიერს შეეძლება თავისი IP მისამართი თავად აირჩიოს header-ის გაგზავნით.
ეს პოსტი middleware-ს პარამეტრ-პარამეტრ გადის, შემდეგ კი იმას, რაც მის გვერდით დგას: გადამისამართების ციკლებს, HSTS-ს, WebSocket-ებსა და SignalR-ს proxy-ს გავლით, და timeout-ებსა და ლიმიტებს ორივე მხარეს.
რა ტყდება proxy-ს უკან#
როცა proxy TLS-ს ასრულებს და მოთხოვნას Kestrel-ს უბრალო HTTP-ით უგზავნის, კავშირი, რომელსაც Kestrel ხედავს, proxy-დანაა, ხოლო თავდაპირველი დეტალები მხოლოდ proxy-ის მიერ დამატებულ header-ებად გადარჩება.
middleware-ის გარეშე ესენი ყველა ჩუმად არასწორია:
HttpContext.Connection.RemoteIpAddressproxy-ის მისამართია. ლოგები ერთ ვიზიტორს აჩვენებს; IP-ით დაყოფილი rate limiter ყველა მომხმარებელს ერთ ვედროში ათავსებს.HttpContext.Request.Schemeარისhttp, ხოლოRequest.IsHttps- false.UseHttpsRedirectionგადაამისამართებს მოთხოვნებს, რომლებიც უკვე HTTPS-ით მოვიდა, და ბრაუზერი ციკლში ტრიალებს, სანამ ზედმეტად ბევრ გადამისამართებას არ შეგატყობინებს.UseHstsთავის header-ს არასოდეს აგზავნის, რადგან მას მხოლოდ HTTPS პასუხებს ამატებს.- cookie-ები
CookieSecurePolicy.SameAsRequest-ითSecureflag-ის გარეშე გაიცემა. - გენერირებული აბსოლუტური URL-ები, მათ შორის
redirect_uri, რომელიც გარე login-ისას Google-ს, Microsoft-ს ან GitHub-ს ეგზავნება,http://-ით იწყება, და provider მათ შეუსაბამობად უარყოფს.
თითოეული მათგანი ერთი და იმავე გამოტოვებული კონფიგურაციის სიმპტომია. რას აკეთებს reverse proxy სინამდვილეში proxy-ის მხარეს ზოგადად განიხილავს; ეს პოსტი იმაზეა, რა უნდა გააკეთოს ამასთან დაკავშირებით ASP.NET Core-მა.
forwarded headers middleware#
middleware ASP.NET Core-ის ნაწილია; პაკეტი საჭირო არ არის.
using Microsoft.AspNetCore.HttpOverrides;using System.Net;var builder = WebApplication.CreateBuilder(args);builder.Services.Configure<ForwardedHeadersOptions>(options =>{ options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.KnownProxies.Add(IPAddress.Parse("10.0.0.1")); // your proxy, see below});var app = builder.Build();app.UseForwardedHeaders(); // first, before anything that reads IP or schemeapp.UseHsts();app.UseHttpsRedirection();// authentication, rate limiting, endpoints...რას აკეთებს ის ყოველ მოთხოვნაზე: თუ კავშირი სანდო proxy-დან მოდის, ის კითხულობს forwarded header-ებს, ცვლის RemoteIpAddress-სა და Scheme-ს მათში არსებული მნიშვნელობებით და ორიგინალებს გადააქვს X-Original-For-სა და X-Original-Proto-ში, რომ მათი ნახვა მაინც შეგეძლოს. თუ კავშირი სანდო proxy-დან არ მოდის, ის საერთოდ არაფერს აკეთებს.
რიგს მნიშვნელობა აქვს. ნებისმიერი middleware, რომელიც UseForwardedHeaders-მდეა რეგისტრირებული, მოთხოვნას proxy-ის თვალით ხედავს. Microsoft-ის რეკომენდაციაა, ის ყველაფერზე ადრე გაეშვას, გარდა დიაგნოსტიკისა და შეცდომების დამმუშავებელი middleware-ისა. განსაკუთრებით მოთხოვნების ლოგირება უნდა მოდიოდეს მის შემდეგ, თორემ შენი ლოგები proxy-ს ჩაწერას გააგრძელებს.
პარამეტრები, რომლებსაც მნიშვნელობა აქვს:
| პარამეტრი | ნაგულისხმევი | რას აკონტროლებს |
|---|---|---|
ForwardedHeaders | None | რომელი header-ები დამუშავდეს. სანამ არ დააყენებ, არაფერი ხდება |
ForwardLimit | 1 | რამდენი ჩანაწერი აიღოს X-Forwarded-For-ის მარჯვენა მხრიდან |
KnownProxies | loopback | ცალკეული proxy მისამართები, რომლებსაც ენდოს |
KnownNetworks | 127.0.0.0/8 | მისამართების დიაპაზონები, რომლებსაც ენდოს |
AllowedHosts | ცარიელი | ჰოსტები, რომლებიც X-Forwarded-Host-იდან მიიღება |
RequireHeaderSymmetry | false | მოითხოვს ყველა header-ში მნიშვნელობების ერთნაირ რაოდენობას |
ForwardedHeaders.XForwardedHost მაგალითიდან შეგნებულადაა გამოტოვებული. მისი ჩართვა proxy-ს - ან ნებისმიერს, ვინც აპლიკაციას პირდაპირ აღწევს - საშუალებას აძლევს შეცვალოს Request.Host, რომელიც გენერირებულ ბმულებსა და პაროლის აღდგენის ელფოსტებში ხვდება. ჩართე ის მხოლოდ AllowedHosts-ით, რომელშიც შენი რეალური ჰოსტის სახელებია, და მხოლოდ მაშინ, თუ proxy Host header-ს გადაწერს და არა უცვლელად გადასცემს. proxy-ების უმეტესობა ორიგინალ Host-ს უცვლელად გადასცემს, და ამ შემთხვევაში ის არ გჭირდება.
KnownProxies: ვის დაუჯერო#
X-Forwarded-For header-ია, ხოლო კლიენტს შეუძლია ნებისმიერი header გამოაგზავნოს. proxy იმ მისამართს, რომელიც რეალურად დაინახა, ამატებს იმას, რაც კლიენტმა გამოაგზავნა, ამიტომ 198.51.100.7-იდან მოთხოვნისთვის, რომელიც ყალბ მნიშვნელობას შეიცავდა, აპლიკაცია იღებს:
X-Forwarded-For: 6.6.6.6, 198.51.100.7ყველაზე მარჯვენა ჩანაწერი შენმა proxy-მ დაწერა და ის ჭეშმარიტია. ყველაფერი მის მარცხნივ კლიენტმა მოგაწოდა და ჭეშმარიტი არ არის. ForwardLimit = 1-ით middleware მარჯვნიდან ზუსტად ერთ ჩანაწერს იღებს - ჭეშმარიტს - და სწორედ ამიტომ არის ნაგულისხმევი ლიმიტი სწორი ერთი proxy-სთვის. თუ ჯაჭვში ორი proxy-ა (მაგალითად, CDN შენი proxy-ს წინ), ლიმიტი 2-ია და ორივე სანდო უნდა იყოს.
უსაფრთხოების მეორე ნახევარი KnownProxies-ია. middleware header-ებს მხოლოდ იმ კავშირებზე ამუშავებს, რომლებიც სანდო მისამართიდან მოდის, და ნაგულისხმევად ეს მხოლოდ loopback-ია. proxy ნებისმიერ სხვა მისამართზე იგნორირდება, ამიტომ ახლად კონფიგურირებული აპლიკაცია ხშირად თითქოს არაფერს აკეთებს: header-ები მოდის, middleware უცნობ წყაროს ხედავს და მათ ჩუმად გამოტოვებს.
იმის გასაგებად, რომელი მისამართიდან უკავშირდება proxy, ჩაწერე ის ლოგში middleware-ის გაშვებამდე:
app.Use(async (context, next) =>{ app.Logger.LogInformation("peer {Peer} xff {Xff}", context.Connection.RemoteIpAddress, context.Request.Headers["X-Forwarded-For"].ToString()); await next();});app.UseForwardedHeaders();გააკეთე ერთი მოთხოვნა დომენის გავლით, წაიკითხე მისამართი ლოგიდან, დაამატე ის KnownProxies-ში (ან დიაპაზონი KnownNetworks-ში) და წაშალე ლოგირება. restart-ის შემდეგ შეამოწმე, რომ მისამართი იგივეა; თუ proxy-ის წყარო მისამართი შეიძლება შეიცვალოს, ენდე მის ქსელს და არა ერთ მისამართს.
.NET 10-ში KnownNetworks მოძველებულადაა მონიშნული KnownIPNetworks-ის სასარგებლოდ, რომელიც უფრო ახალ System.Net.IPNetwork ტიპს იღებს. ქცევა იგივეა; ძველი property მაინც მუშაობს და მხოლოდ გაფრთხილებას იწვევს.
გარემოს ცვლადის მალსახმობი და მისი ფასი#
ASP.NET Core-ს შეუძლია middleware კოდის გარეშე ჩართოს:
ASPNETCORE_FORWARDEDHEADERS_ENABLED=trueეს ამატებს middleware-ს ჩართული XForwardedFor-ითა და XForwardedProto-თი - და ასევე ასუფთავებს KnownProxies-სა და KnownNetworks-ს, ამიტომ header-ებს ნებისმიერი წყაროდან იღებს. ის შეიქმნა პლატფორმებისთვის, სადაც აპლიკაცია მხოლოდ proxy-ს გავლით არის მიუწვდომელი, ამიტომ სხვა არავინაა, ვინც მათ გამოაგზავნის.
სერვერზე, სადაც აპლიკაციის პორტი პირდაპირაც მიუწვდომელია, ეს ზემოთ მოცემული გაფრთხილების ყალბი IP-ის პრობლემაა. სწრაფი ტესტისთვის ეს კარგია და მისაღებია, როცა კლიენტის IP მხოლოდ ლოგებისთვის გამოიყენება. ყველაფრისთვის, რაც კლიენტის IP-ზე გადაწყვეტილებას იღებს - rate limit-ები, IP allow-list-ები, თაღლითობის შეფასება, login-ის შეზღუდვა - ნაცვლად ამისა კოდში დააკონფიგურირე KnownProxies. Rate limit-ები და ბოროტად გამოყენება ხსნის, რატომ არის გაყალბებადი გასაღები საერთოდ rate limit-ის არქონაზე უარესი.
აი, რა არის ამაზე დამოკიდებული ტიპურ აპლიკაციაში. ჩაშენებული rate limiter ყოფს იმ გასაღებით, რომელსაც მისცემ, და ჩვეულებრივი გასაღები კლიენტის მისამართია:
builder.Services.AddRateLimiter(options =>{ options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(ctx => RateLimitPartition.GetFixedWindowLimiter( ctx.Connection.RemoteIpAddress?.ToString() ?? "unknown", _ => new FixedWindowRateLimiterOptions { PermitLimit = 100, Window = TimeSpan.FromMinutes(1) }));});middleware-ის გარეშე ეს გასაღები ყველა მოთხოვნისთვის proxy-ა, და წუთში მეასე მოთხოვნა ნებისმიერისგან ყველას ბლოკავს. middleware-ით, რომელიც ყველა წყაროს ენდობა, გასაღები ის არის, რასაც თავდამსხმელი დაწერს, და ის ერთი header-ის შეცვლით ახალ ას მოთხოვნას იღებს. მხოლოდ სწორად შემოსაზღვრული ვერსია გაძლევს ლიმიტს თითოეულ რეალურ კლიენტზე. UseRateLimiter pipeline-ში ასევე UseForwardedHeaders-ის შემდეგ უნდა მოდიოდეს, იმავე მიზეზით, რის გამოც ლოგირება.
HTTPS გადამისამართება გადამისამართების ციკლის გარეშე#
UseHttpsRedirection HTTP მოთხოვნებს 307-ით პასუხობს HTTPS URL-ზე. TLS-ის დამსრულებელი proxy-ს უკან მას ჩავარდნის ორი რეჟიმი აქვს.
ციკლი. X-Forwarded-Proto-ს დამუშავების გარეშე ყველა მოთხოვნა HTTP-ს ჰგავს, ამიტომ ყველა მოთხოვნა გადამისამართდება, მათ შორის ისინიც, რომლებიც HTTPS-ით მოვიდა. ზემოთ მოცემული forwarded headers კონფიგურაცია ამას ასწორებს.
ჩუმი უმოქმედობა. middleware-მა უნდა იცოდეს, რომელ პორტზე გადაამისამართოს. ის უყურებს HttpsRedirectionOptions.HttpsPort-ს, შემდეგ https_port პარამეტრს (ASPNETCORE_HTTPS_PORT გარემოში), შემდეგ HTTPS endpoint-ებს, რომლებსაც Kestrel უსმენს. proxy-ს უკან Kestrel-ს HTTPS endpoint არ აქვს, ამიტომ middleware ლოგში წერს "Failed to determine the https port for redirect" და არაფერს აკეთებს. თუ გინდა, რომ გადამისამართება აპლიკაციამ გააკეთოს, დააყენე პორტი, რომელიც ბრაუზერმა უნდა გამოიყენოს:
ASPNETCORE_HTTPS_PORT=443სანამ ამას გააკეთებ, შეამოწმე, ხომ არ გადაამისამართებს proxy უკვე უბრალო HTTP-ს HTTPS-ზე შენი დომენისთვის. თუ გადაამისამართებს, აპლიკაციის გადამისამართება არასოდეს ამოქმედდება და middleware-ის გამოტოვება შეგიძლია. ორი ფენა, რომელიც გადაამისამართებს, უვნებელია; ორი ფენა, რომელიც scheme-ზე ვერ თანხმდება, სწორედ ასე იწყებს ციკლს. RE:NODE-ზე აპლიკაციის გეგმის proxy slot შენი დომენის სერტიფიკატს ინახავს - მიუთითე A ჩანაწერი ნაჩვენებ მისამართზე და ის ავტომატურად გაიცემა და განახლდება 21-დღიან ფანჯარაში - ხოლო კლიენტის მისამართი X-Forwarded-For-ში მოდის. შენი დომენი და მისი სერტიფიკატი DNS-ის მხარეს განიხილავს.
HSTS#
HSTS ბრაუზერს ეუბნება, რომ შენი ჰოსტისთვის გარკვეული პერიოდის განმავლობაში HTTPS გამოიყენოს, უკითხავად. ASP.NET Core-ში:
builder.Services.AddHsts(options =>{ options.MaxAge = TimeSpan.FromDays(180); options.IncludeSubDomains = false; options.Preload = false;});if (!app.Environment.IsDevelopment()){ app.UseHsts();}ნაგულისხმევი MaxAge 30 დღეა. middleware header-ს მხოლოდ HTTPS პასუხებს ამატებს და არასოდეს ამატებს localhost-ის, 127.0.0.1-ის ან [::1]-ისთვის, რის გამოც ლოკალური ტესტირება არაფერს აჩვენებს.
ორ პარამეტრთან ფრთხილად იყავი. IncludeSubDomains პოლიტიკას ყველა subdomain-ზე ავრცელებს, მათ შორის იმაზეც, სადაც უბრალო HTTP-ზე ძველი ადმინ ხელსაწყო მუშაობს და რომელიც დაგავიწყდა. Preload ითხოვს ჩართვას სიაში, რომელსაც ბრაუზერები თან ატარებენ, რისი გაუქმებაც ძალიან რთულია. დაიწყე მოკლე MaxAge-ით, დარწმუნდი, რომ საიტის ყველა ნაწილი HTTPS-ზე მუშაობს, შემდეგ გაზარდე. თუ proxy უკვე აგზავნის Strict-Transport-Security-ს, ნუ გაგზავნი მას ორჯერ განსხვავებული მნიშვნელობებით.
WebSocket-ები და SignalR proxy-ს გავლით#
WebSocket იწყება როგორც HTTP მოთხოვნა Upgrade: websocket და Connection: Upgrade header-ებით, და proxy-მ ორივე უნდა გაატაროს, შემდეგ კი კავშირი ღიად შეინარჩუნოს. მიმდინარე proxy-ების უმეტესობა ამას HTTP/1.1-ისთვის აკეთებს. აპლიკაციის მხარეს:
app.UseWebSockets(new WebSocketOptions{ KeepAliveInterval = TimeSpan.FromSeconds(30)});ნაგულისხმევი KeepAliveInterval ორი წუთია. proxy, რომელიც უქმ კავშირებს 60 წამის შემდეგ ხურავს, მშვიდ WebSocket-ს გაწყვეტს გაცილებით ადრე, ვიდრე აპლიკაცია პირველ ping-ს გაგზავნის, და კლიენტი ყოველ წუთს აუხსნელ გათიშვას ხედავს. ინტერვალი უფრო მოკლე დატოვე, ვიდრე ყველაზე მოკლე idle timeout ბრაუზერსა და Kestrel-ს შორის.
SignalR ამას შენთვის გარკვეულწილად აგვარებს: სერვერი ნაგულისხმევად ყოველ 15 წამში keep-alive-ს აგზავნის (KeepAliveInterval) და კლიენტს წასულად თვლის, თუ 30 წამის განმავლობაში მისგან არაფერი მოსვლია (ClientTimeoutInterval). თუ WebSocket-ები proxy-ს გავლით ვერ მუშაობს, SignalR გადადის Server-Sent Events-ზე ან long polling-ზე, რომლებიც მუშაობს, მაგრამ მეტ მოთხოვნასა და მეტ დაყოვნებას ჯდება. შეამოწმე ბრაუზერის network ჩანართი: კავშირი, რომელიც negotiate-ს აკეთებს და შემდეგ long polling-ზე გადადის, ჩვეულებრივ ნიშნავს proxy-ს, რომელმაც upgrade არ გაატარა. WebSocket-ები reverse proxy-ს უკან proxy-ის მხარის დეტალებს შეიცავს, ხოლო SignalR real-time აპლიკაციები hub-ის კონფიგურაციას განიხილავს.
timeout-ები, body-ის ზომები და keep-alive#
proxy-საც და Kestrel-საც თავისი ლიმიტები აქვს, და მოთხოვნა ორივეში უნდა ჩაეტიოს.
| Kestrel-ის პარამეტრი | ნაგულისხმევი | შენიშვნა |
|---|---|---|
Limits.KeepAliveTimeout | 130 წამი | უქმი დრო, სანამ Kestrel კავშირს დახურავს |
Limits.RequestHeadersTimeout | 30 წამი | header-ების მისაღებად დაშვებული დრო |
Limits.MaxRequestBodySize | 30,000,000 ბაიტი | დაახლოებით 28.6 MB; გადაფარე endpoint-ის დონეზე |
Limits.MaxConcurrentConnections | შეუზღუდავი | proxy-ს უკან დაყენება იშვიათად ღირს |
Kestrel-ის ორწუთიანი keep-alive proxy-ების უმეტესობის idle timeout-ზე გრძელია, რაც უსაფრთხო მიმართულებაა: proxy უქმ კავშირებს პირველი ხურავს, ამიტომ არასოდეს გაგზავნის მოთხოვნას კავშირზე, რომლის გაწყვეტასაც Kestrel აპირებს. ეს რბოლა ხანდახან 502-ების კლასიკური მიზეზია, როცა აპლიკაციის ლოგში არაფერია, და Kestrel-თან ის Node-თან შედარებით ნაკლებად ხშირია ზუსტად ამ ნაგულისხმევის გამო. თუ KeepAliveTimeout შეამცირე, დააბრუნე.
ატვირთვებისთვის ორ body ლიმიტს შორის პატარა იმარჯვებს. Kestrel-ის ლიმიტი გაზარდე იმ ერთი endpoint-ისთვის, რომელსაც ეს სჭირდება, controller action-ზე [RequestSizeLimit]-ით ან მოთხოვნის დასაწყისში IHttpMaxRequestBodySizeFeature-ის გავლით, და არა გლობალურად; დიდი გლობალური ლიმიტი ერთი კლიენტისთვის შენი მეხსიერების ავსების მარტივი გზაა. მოთხოვნა, რომელიც proxy-ის read timeout-ზე ნელია - გრძელი ანგარიში, დიდი ექსპორტი - უნდა იქცეს ფონურ დავალებად, რომელსაც კლიენტი poll-ით ამოწმებს, და არა ორწუთიან HTTP მოთხოვნად. .NET worker service-ები და ფონური დავალებები ამ pattern-ს აჩვენებს.
შემოწმება, რომ მუშაობს#
დაამატე დროებითი დიაგნოსტიკური endpoint, გამოიძახე ის ერთხელ დომენის გავლით და წაშალე:
app.MapGet("/debug/request", (HttpContext ctx) => new{ ip = ctx.Connection.RemoteIpAddress?.ToString(), scheme = ctx.Request.Scheme, host = ctx.Request.Host.Value, originalFor = ctx.Request.Headers["X-Original-For"].ToString()});დომენის გავლით ip შენი საკუთარი საჯარო მისამართი უნდა იყოს, scheme - https, ხოლო originalFor proxy-ს უნდა აჩვენებდეს. შემდეგ გამოიძახე აპლიკაცია პირდაპირ მისი IP-ითა და პორტით ყალბი header-ით:
$ curl -H "X-Forwarded-For: 1.2.3.4" http://203.0.113.10:25571/debug/requestპასუხში არ უნდა იყოს 1.2.3.4. თუ არის, ენდობი წყაროებს, რომლებსაც არ უნდა ენდო. RE:NODE-ზე აპლიკაციის პორტი სერვერის მისამართზეა გამოყოფილი, ამიტომ ეს პირდაპირი ტესტი რეალური გზაა, რომელიც თავდამსხმელს შეუძლია გამოიყენოს, და არა ჰიპოთეტური.
ბოლო შემოწმება response header-ია: Strict-Transport-Security დომენის გავლით HTTPS პასუხებზე უნდა ჩანდეს, თუ HSTS ჩართე. თუ არაფერი მუშაობს და ლოგი არაფერს აჩვენებს, დაუბრუნდი ზემოთ მოცემულ ლოგირების middleware-ს და შეხედე peer მისამართს; ათიდან ცხრა შემთხვევაში ის KnownProxies-ში არ არის.
FAQ#
რატომ ჩანს, რომ UseForwardedHeaders არაფერს აკეთებს?
ან ForwardedHeaders ჯერ კიდევ None-ია, ან proxy-ის მისამართი არ არის KnownProxies-ში ან KnownNetworks-ში. middleware არასანდო წყაროებიდან header-ებს იგნორირებს ნაგულისხმევ დონეზე რაიმეს ლოგირების გარეშე, ამიტომ ჩაწერე peer მისამართი ლოგში და შეადარე.
უნდა დავაყენო ForwardLimit null-ზე, რომ მთელი header წავიკითხო?
არა. იმაზე მეტი ჩანაწერის აღება, რამდენი proxy-ც გაქვს, ნიშნავს კლიენტის დაწერილი მნიშვნელობების ნდობას. დააყენე ლიმიტი იმ proxy-ების რაოდენობაზე, რომლებიც გაქვს, რაც ერთი proxy slot-ისთვის ნაგულისხმევი 1-ია.
მჭირდება კიდევ UseHttpsRedirection, თუ proxy გადაამისამართებს?
არა, თუ proxy უკვე შენი დომენის ყველა HTTP მოთხოვნას გადაამისამართებს. მისი დატოვება უვნებელია, თუ X-Forwarded-Proto მუშავდება, რომ აპლიკაცია proxy-ს ეთანხმებოდეს იმაში, რომელი მოთხოვნებია უკვე HTTPS.
რატომ ვარდება გარე login-ები redirect URI-ის შეუსაბამობით?
აპლიკაციამ callback URL Request.Scheme-იდან ააგო, რომელიც http იყო, რადგან forwarded proto header არ დამუშავდა. გაასწორე forwarded headers და გენერირებული URL გახდება https://, რაც დაემთხვევა იმას, რაც provider-თან დაარეგისტრირე.
ცვლის proxy იმას, როგორ ვაკონფიგურირებ Kestrel-ის პორტებს?
Kestrel უსმენს უბრალო HTTP-ს გამოყოფილ პორტზე, ყველა ინტერფეისზე, სერტიფიკატის გარეშე. ASP.NET Core აპლიკაციის deploy GitHub-იდან ASPNETCORE_URLS-ის მოწყობას შეიცავს.




კომენტარები
სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.