HHangfire Handbook

İ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.

Dashboard Auth Flow Request /hangfire/* Auth Middleware IDashboardAuthorizationFilter Artı: Dashboard Admin erişimi Eksi: 401 Unauthorized Erişim engellendi authorized denied

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+)

// 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.