Bu yazıda clusterlarda TLS nasıl kurulur vs anlatmayacağım. Mevcut yaşadığım problemin sebebini ve çözümü anlatacağım.
Neden TLS? Neden Şimdi?
Biliyorsunuz ki RabbitMQ, sistemlerimizin belkemiği haline geldi. Ama çoğumuz şu gerçeği görmezden geliyoruz: cluster yapımız ne kadar güçlü olursa olsun, güvenli değilse, aslında camdan bir kule inşa ediyoruz demektir.
TLS (Transport Layer Security), RabbitMQ cluster’ınızı adeta bir zırh gibi korurken, mesajlarınızın şifrelenerek güvenli bir şekilde iletilmesini sağlar. Bu sadece bir “güzel özellik” değil, günümüz uygulamaları için bir zorunluluk.
RabbitMQ Client 7.0.0 versiyonunda clusterlara TLS’siz bağlanınca önceki yazımda yaptığım tüm kodlar çalışıyordu. Aşağıdaki linkten bu yazıya erişebilirsiniz.
Fakat burada TLS ile bağlantı yapmaya çalışınca DefaultEndpointResolver hostnameleri ve sertifikaları eklediğimde çalışmadı. Şöyle bir çözüm buldum . List<AmqpTcpEndpoint> sınıfından bir instance oluşturup connectionFactoryde mevcut listeyi verdiğimde sorunsuz bir şekilde TLS clusterlara bağlantı yapabildim. Test ortamında tls ya da cluster gibi durumlar genelde olmuyor bu yüzden mevcut kod blogunda bunun ayrımını Ssl ile ayırıyorum.
Tüm kod bloğu aşağıdadır. Baya uğraştırmıştı. Ben uğraştım siz zaman kazanın istedim 🙂
Fikir alışverişinde bulunmaya her zaman hazırım. Herkese iyi çalışmalar
using Microsoft.Extensions.Logging; using Polly; using RabbitMQ.Client.Events; using RabbitMQ.Client; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; namespace WAP.RabbitMQ.Services { public class RabbitMqConnectionService { private readonly SemaphoreSlim _semaphore = new(1, 1); private readonly AsyncPolicy _connectionRetryPolicy; private IConnection? _connection; private readonly IConnectionFactory _connectionFactory; private readonly ILogger<RabbitMqConnectionService> _logger; private readonly RabbitMqSetting _rabbitMqSetting; public RabbitMqConnectionService(IConnectionFactory connectionFactory, ILogger<RabbitMqConnectionService> logger, RabbitMqSetting projectSetting) { _connectionFactory = connectionFactory; _logger = logger; _rabbitMqSetting = projectSetting; _connectionRetryPolicy = Policy .Handle<Exception>() .WaitAndRetryAsync(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (exception, timeSpan, _, retryCount) => { _logger.LogError(exception, "RabbitMQ connection failed, retrying in {TimeOut}ms RetryCount: {RetryCount}", timeSpan.TotalMilliseconds, retryCount); }); } private bool IsConnected => _connection is { IsOpen: true }; public async Task<IChannel> GetChannelAsync() { if (!IsConnected) { await HandleConnectionAsync(); } var channel = await _connection!.CreateChannelAsync(); return channel; } private async Task HandleConnectionAsync() { try { await _semaphore.WaitAsync(); if (IsConnected) { return; } await _connectionRetryPolicy.ExecuteAsync(async () => { if (_connection != null) { _connection.ConnectionShutdownAsync -= OnConnectionShutdownAsync; await _connection.CloseAsync(); await _connection.DisposeAsync(); } // CLUSTER'a bağlanmak için birden fazla endpoint olusturuyoruz ve SSL KULLANIYORUZ. var endpoints = _rabbitMqSetting.Hosts .Select(host => { if (_rabbitMqSetting.UseSsl) { var sslOption = new SslOption { Enabled = true, ServerName = _rabbitMqSetting.ServerName!, Version = SslProtocols.Tls12 }; // Sertifika dosyası belirtilmişse, X509Certificate2 olarak yükle if (!string.IsNullOrEmpty(_rabbitMqSetting.ClientCertPath)) { try { var cert = new X509Certificate2( _rabbitMqSetting.ClientCertPath, _rabbitMqSetting.CertPassphrase, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable ); _logger.LogInformation("Sertifika yüklendi: {Subject}, Geçerlilik: {NotBefore} - {NotAfter}", cert.Subject, cert.NotBefore, cert.NotAfter); sslOption.Certs = new X509CertificateCollection(new X509Certificate[] { cert }); } catch (Exception ex) { _logger.LogError(ex, "Sertifika yükleme hatası, CertPath ve CertPassphrase kullanılacak"); sslOption.CertPath = _rabbitMqSetting.ClientCertPath; sslOption.CertPassphrase = _rabbitMqSetting.CertPassphrase; } } return new AmqpTcpEndpoint(host, _rabbitMqSetting.Port, sslOption); } else { return new AmqpTcpEndpoint(host, _rabbitMqSetting.Port); } }) .ToList(); _connection = await _connectionFactory.CreateConnectionAsync(endpoints, _rabbitMqSetting.ConnectionName); _connection.ConnectionShutdownAsync += OnConnectionShutdownAsync; }); } finally { _semaphore.Release(); } } private async Task OnConnectionShutdownAsync(object? sender, ShutdownEventArgs e) { _logger.LogError("RabbitMQ connection shutdown. Reason: {Reason}", e.ReplyText); await HandleConnectionAsync(); } } }