İLERİ
Dashboard & Güvenlik
Hangfire Dashboard, tüm job'ların durumunu gösteren web arayüzüdür. Production'da mutlaka yetkilendirme ile korunmalıdır.
Karar Rehberi
| Durum | Öneri | Örnek veya gerekçe |
|---|---|---|
| Production monitoring | Uygun: Read-only dashboard | Ops ekibi job durumlarını izler |
| Manuel retry/delete | Uygun: Admin-only write | Failed job'ı yeniden tetikle |
| Public erişim | Uygun değil: Hassas veri sızar | Method adları, argümanlar görünür |
| Her developer erişimi | Uygun değil: Role-based kısıtla | Junior dev yanlışlıkla job siler |
Production-Safe Dashboard Configuration
// Program.cs
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new HangfireAuthorizationFilter() },
IsReadOnlyFunc = context =>
{
var httpContext = context.GetHttpContext();
// Sadece Admin rolü write yapabilir
return !httpContext.User.IsInRole("Admin");
},
DashboardTitle = "MyApp - Background Jobs",
DisplayStorageConnectionString = false // Connection string gizle
});Authorization Filter (Cookie/JWT)
public class HangfireAuthorizationFilter : IDashboardAuthorizationFilter
{
public bool Authorize(DashboardContext context)
{
var httpContext = context.GetHttpContext();
// Option 1: Cookie-based (MVC app)
if (!httpContext.User.Identity?.IsAuthenticated ?? true)
return false;
return httpContext.User.IsInRole("Admin") ||
httpContext.User.IsInRole("DevOps");
}
}
// Option 2: IP-based restriction (internal network only)
public class IpRestrictionFilter : IDashboardAuthorizationFilter
{
private readonly (IPAddress Network, int PrefixLength)[] _allowedRanges =
{
(IPAddress.Parse("10.0.0.0"), 8),
(IPAddress.Parse("192.168.0.0"), 16)
};
public bool Authorize(DashboardContext context)
{
var httpContext = context.GetHttpContext();
var remoteIp = httpContext.Connection.RemoteIpAddress;
if (remoteIp == null) return false;
// IPv4-mapped IPv6 normalize
if (remoteIp.IsIPv4MappedToIPv6)
remoteIp = remoteIp.MapToIPv4();
return IsInAllowedRange(remoteIp);
}
private bool IsInAllowedRange(IPAddress ip)
{
foreach (var (network, prefixLength) in _allowedRanges)
{
var networkBytes = network.GetAddressBytes();
var ipBytes = ip.GetAddressBytes();
if (networkBytes.Length != ipBytes.Length) continue;
var maskBytes = prefixLength / 8;
var remainBits = prefixLength % 8;
bool match = true;
for (int i = 0; i < maskBytes && match; i++)
match = networkBytes[i] == ipBytes[i];
if (match && remainBits > 0)
{
var mask = (byte)(0xFF << (8 - remainBits));
match = (networkBytes[maskBytes] & mask) == (ipBytes[maskBytes] & mask);
}
if (match) return true;
}
return false;
}
}Endpoint Routing (.NET 8+)
// .NET 6 öncesi pattern — artık gerekli değil
app.UseEndpoints(endpoints =>
{
endpoints.MapHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new HangfireAuthorizationFilter() }
}).RequireAuthorization("HangfirePolicy");
endpoints.MapControllers();
});// Minimal API style — .NET 8'de önerilen
app.MapHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new HangfireAuthorizationFilter() },
DisplayStorageConnectionString = false
}).RequireAuthorization("HangfirePolicy");
// Policy tanımı
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("HangfirePolicy", policy =>
policy.RequireRole("Admin", "DevOps"));
});
// RequireAuthorization için Authentication middleware gerekir!
// Cookie, JWT veya Identity — hangisini kullanıyorsanız pipeline'da olmalı:
// builder.Services.AddAuthentication("Bearer").AddJwtBearer(...);
// veya: builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(...);
// app.UseAuthentication(); // UseAuthorization()'dan ÖNCE
// app.UseAuthorization();Güvenlik: Dashboard, job metot adlarını, argümanlarını (serialized JSON), exception stack trace'lerini ve connection string bilgilerini gösterir. Asla public erişime açmayın. DisplayStorageConnectionString = false mutlaka ayarlayın.
Örnek: Bir fintech şirketinde Dashboard sadece VPN üzerinden erişilebilir. DevOps ekibi read-only görüntüler, sadece 2 senior engineer write (retry/delete) yetkisine sahiptir. Her dashboard erişimi audit log'a yazılır.