From 980089d7e0c7234910f049a0f18adb75e053cc3d Mon Sep 17 00:00:00 2001 From: Noe Date: Wed, 29 Nov 2023 21:28:11 +0100 Subject: [PATCH] Rabbitmq on top of VPN network, add users and passwords to rabbitmq Created skript for automatic update of all the installations --- csharp/App/Backend/Program.cs | 3 +- .../App/Backend/Websockets/RabbitMQManager.cs | 148 ++++++++++++++++-- .../Backend/Websockets/WebsockerManager.cs | 55 +++++++ .../App/SaliMax/deploy_all_installations.sh | 34 ++++ csharp/App/SaliMax/src/Program.cs | 35 ++++- 5 files changed, 262 insertions(+), 13 deletions(-) create mode 100755 csharp/App/SaliMax/deploy_all_installations.sh diff --git a/csharp/App/Backend/Program.cs b/csharp/App/Backend/Program.cs index 1d68676f8..5350fe411 100644 --- a/csharp/App/Backend/Program.cs +++ b/csharp/App/Backend/Program.cs @@ -11,6 +11,7 @@ using Microsoft.OpenApi.Models; using InnovEnergy.Lib.Utils; using RabbitMQ.Client; + namespace InnovEnergy.App.Backend; public static class Program @@ -21,7 +22,7 @@ public static class Program Watchdog.NotifyReady(); Db.Init(); var builder = WebApplication.CreateBuilder(args); - + RabbitMqManager.InitializeEnvironment(); RabbitMqManager.StartRabbitMqConsumer(); Console.WriteLine("Queue declared"); diff --git a/csharp/App/Backend/Websockets/RabbitMQManager.cs b/csharp/App/Backend/Websockets/RabbitMQManager.cs index a928f6e84..3c74a7d6c 100644 --- a/csharp/App/Backend/Websockets/RabbitMQManager.cs +++ b/csharp/App/Backend/Websockets/RabbitMQManager.cs @@ -1,5 +1,9 @@ +using System.Drawing.Printing; using System.Net; +using System.Net.Security; using System.Net.Sockets; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; using System.Text; using System.Text.Json; using InnovEnergy.App.Backend.Database; @@ -20,8 +24,130 @@ public static class RabbitMqManager public static void InitializeEnvironment() { - string vpnServerIp = "194.182.190.208"; - Factory = new ConnectionFactory { HostName = vpnServerIp}; + //string vpnServerIp = "194.182.190.208"; + string vpnServerIp = "10.2.0.11"; + + // ConnectionFactory factory = new ConnectionFactory(); + // factory.HostName = vpnServerIp; + // factory.AutomaticRecoveryEnabled = true; + // //factory.UserName = ""; + // //factory.Password = ""; + // factory.VirtualHost = "/"; + // factory.Port = 5672; + // + // //factory.AuthMechanisms = new IAuthMechanismFactory[] { new ExternalMechanismFactory() }; + // + // System.Diagnostics.Debug.WriteLine("2 "); + // + // X509Certificate2Collection certCollection = new X509Certificate2Collection(); + // X509Certificate2 certificate = new X509Certificate2("/etc/rabbitmq/testca/ca_certificate.pem"); + // certCollection.Add(certificate); + // + // factory.Ssl.Certs = certCollection; + // factory.Ssl.Enabled = true; + // factory.Ssl.ServerName = "Webserver-FrontAndBack"; + // factory.Ssl.Version = SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13; + // factory.Ssl.AcceptablePolicyErrors = SslPolicyErrors.RemoteCertificateChainErrors; + // factory.Ssl.CertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => + // { + // if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors) + // { + // // Log or debug information about the chain + // foreach (var chainElement in chain.ChainElements) + // { + // Console.WriteLine($"Element Subject: {chainElement.Certificate.Subject}"); + // Console.WriteLine($"Element Issuer: {chainElement.Certificate.Issuer}"); + // // Add more details as needed + // } + // } + // + // // Your custom validation logic + // return sslPolicyErrors == SslPolicyErrors.None; + // }; + + + Factory = new ConnectionFactory + { + HostName = vpnServerIp, + Port = 5672, + VirtualHost = "/", + UserName = "consumer", + Password = "faceaddb5005815199f8366d3d15ff8a", + //AuthMechanisms = new IAuthMechanismFactory[] { new ExternalMechanismFactory() }, + + // Ssl = new SslOption + // { + // Enabled = true, + // ServerName = "Webserver-FrontAndBack", + // //Roots = new X509Certificate2Collection { caCertificate }, + // //CertPath = "/etc/rabbitmq/testca/ca_certificate.pem", + // CertPath = "/etc/rabbitmq/client/client_certificate.pem", + // + // + // // CertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => + // // { + // // //X509Certificate2 clientCertificate = new X509Certificate2("/etc/rabbitmq/client/client_certificate.pem"); + // // + // // //X509Certificate2 caCertificate = new X509Certificate2("/etc/openvpn/client/ca-certificate"); + // // X509Certificate2 caCertificate = new X509Certificate2("/etc/rabbitmq/testca/ca_certificate.pem"); + // // + // // + // // + // // Console.WriteLine(certificate.Subject); + // // Console.WriteLine("---------------------------------"); + // // //Console.WriteLine(certificate.GetPublicKey()); + // // // Your custom validation logic using the CA certificate + // // // Return true if the certificate is valid, false otherwise + // // return certificate.Issuer == caCertificate.Subject; + // // } + // CertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => + // { + // X509Certificate2 caCertificate = new X509Certificate2("/etc/rabbitmq/testca/ca_certificate.pem"); + // + // // Add the CA certificate to the chain policy's extra store + // chain.ChainPolicy.ExtraStore.Add(caCertificate); + // + // // Check if the chain builds successfully + // bool chainIsValid = chain.Build((X509Certificate2)certificate); + // + // if (!chainIsValid) + // { + // Console.WriteLine("Certificate chain validation failed:"); + // + // // Print details of each chain status + // foreach (var chainStatus in chain.ChainStatus) + // { + // Console.WriteLine($"Chain Status: {chainStatus.Status}"); + // Console.WriteLine($"Chain Status Information: {chainStatus.StatusInformation}"); + // // Add more details as needed + // // Check if the failure is due to UntrustedRoot + // if (chainStatus.Status == X509ChainStatusFlags.UntrustedRoot) + // { + // // Manually check if the root certificate is the expected one + // if (certificate.Issuer == caCertificate.Subject) + // { + // Console.WriteLine("Manually trusting the root certificate."); + // chainIsValid = true; + // } + // } + // } + // + // + // } + // + // // Additional validation logic if needed + // Console.WriteLine($"Certificate Subject: {certificate.Subject}"+chainIsValid); + // + // // Return true if the certificate is valid + // return chainIsValid; + // } + + + //} + }; + + + Connection = Factory.CreateConnection(); Channel = Connection.CreateModel(); Console.WriteLine("Middleware subscribed to RabbitMQ queue, ready for receiving messages"); @@ -36,9 +162,8 @@ public static class RabbitMqManager var body = ea.Body.ToArray(); var message = Encoding.UTF8.GetString(body); StatusMessage? receivedStatusMessage = JsonSerializer.Deserialize(message); - var InstallationConnections = WebsocketManager.InstallationConnections; - lock (InstallationConnections) + lock (WebsocketManager.InstallationConnections) { //Consumer received a message if (receivedStatusMessage != null) @@ -78,6 +203,7 @@ public static class RabbitMqManager //Traverse the Alarm list, and store each of them to the database if (receivedStatusMessage.Alarms != null) { + Console.WriteLine("Add an alarm for installation "+receivedStatusMessage.InstallationId); foreach (var alarm in receivedStatusMessage.Alarms) { Error newError = new Error @@ -95,11 +221,14 @@ public static class RabbitMqManager } } + var prevStatus = 0; + //This installation id does not exist in our data structure, add it. - if (!InstallationConnections.ContainsKey(installationId)) + if (!WebsocketManager.InstallationConnections.ContainsKey(installationId)) { + prevStatus = -2; Console.WriteLine("Create new empty list for installation: " + installationId); - InstallationConnections[installationId] = new InstallationInfo + WebsocketManager.InstallationConnections[installationId] = new InstallationInfo { Status = receivedStatusMessage.Status, Timestamp = DateTime.Now @@ -107,13 +236,14 @@ public static class RabbitMqManager } else { - InstallationConnections[installationId].Status = receivedStatusMessage.Status; - InstallationConnections[installationId].Timestamp = DateTime.Now; + prevStatus = WebsocketManager.InstallationConnections[installationId].Status; + WebsocketManager.InstallationConnections[installationId].Status = receivedStatusMessage.Status; + WebsocketManager.InstallationConnections[installationId].Timestamp = DateTime.Now; } //Console.WriteLine("----------------------------------------------"); //Update all the connected front-ends regarding this installation - if(InstallationConnections[installationId].Connections.Count > 0) + if(prevStatus != receivedStatusMessage.Status && WebsocketManager.InstallationConnections[installationId].Connections.Count > 0) { WebsocketManager.InformWebsocketsForInstallation(installationId); } diff --git a/csharp/App/Backend/Websockets/WebsockerManager.cs b/csharp/App/Backend/Websockets/WebsockerManager.cs index 24e6c60bb..643308e57 100644 --- a/csharp/App/Backend/Websockets/WebsockerManager.cs +++ b/csharp/App/Backend/Websockets/WebsockerManager.cs @@ -1,12 +1,65 @@ +using System.Net; +using System.Net.Sockets; using System.Net.WebSockets; using System.Text; using System.Text.Json; +using InnovEnergy.App.Backend.Database; +using InnovEnergy.App.Backend.DataTypes; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; namespace InnovEnergy.App.Backend.Websockets; public static class WebsocketManager { public static Dictionary InstallationConnections = new Dictionary(); + + public static void InformInstallationsToSubscribeToRabbitMq() + { + var installationIps = Db.Installations.Select(inst => inst.VpnIp).ToList(); + Console.WriteLine("Count is "+installationIps.Count); + var maxRetransmissions = 2; + + UdpClient udpClient = new UdpClient(); + udpClient.Client.ReceiveTimeout = 2000; + int port = 9000; + //Send a message to each installation and tell it to subscribe to the queue + using (udpClient) + { + for (int i = 0; i < installationIps.Count; i++) + { + if(installationIps[i]==""){continue;} + Console.WriteLine("-----------------------------------------------------------"); + Console.WriteLine("Trying to reach installation with IP: " + installationIps[i]); + //Try at most MAX_RETRANSMISSIONS times to reach an installation. + for (int j = 0; j < maxRetransmissions; j++) + { + string message = "This is a message from RabbitMQ server, you can subscribe to the RabbitMQ queue"; + byte[] data = Encoding.UTF8.GetBytes(message); + udpClient.Send(data, data.Length, installationIps[i], port); + + Console.WriteLine($"Sent UDP message to {installationIps[i]}:{port}: {message}"); + IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Parse(installationIps[i]), port); + + try + { + byte[] replyData = udpClient.Receive(ref remoteEndPoint); + string replyMessage = Encoding.UTF8.GetString(replyData); + Console.WriteLine("Received " + replyMessage + " from installation " + installationIps[i]); + break; + } + catch (SocketException ex) + { + if (ex.SocketErrorCode == SocketError.TimedOut){Console.WriteLine("Timed out waiting for a response. Retry...");} + else{Console.WriteLine("Error: " + ex.Message);} + } + } + } + } + + Console.WriteLine("Start RabbitMQ Consumer"); + + } //Every 1 minute, check the timestamp of the latest received message for every installation. //If the difference between the two timestamps is more than one minute, we consider this installation unavailable. @@ -26,6 +79,8 @@ public static class WebsocketManager } } + + //Inform all the connected websockets regarding installation "installationId" public static void InformWebsocketsForInstallation(int installationId) { diff --git a/csharp/App/SaliMax/deploy_all_installations.sh b/csharp/App/SaliMax/deploy_all_installations.sh new file mode 100755 index 000000000..a136a5879 --- /dev/null +++ b/csharp/App/SaliMax/deploy_all_installations.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +dotnet_version='net6.0' +salimax_ip="$1" +username='ie-entwicklung' +root_password='Salimax4x25' + +set -e + +echo -e "\n============================ Build ============================\n" + +dotnet publish \ + ./SaliMax.csproj \ + -p:PublishTrimmed=false \ + -c Release \ + -r linux-x64 + +echo -e "\n============================ Deploy ============================\n" +ip_addresses=("10.2.3.115" "10.2.3.104" "10.2.4.33" "10.2.4.32" "10.2.4.36" "10.2.4.35" "10.2.4.154" "10.2.4.113" "10.2.4.29") + +for ip_address in "${ip_addresses[@]}"; do + rsync -v \ + --exclude '*.pdb' \ + ./bin/Release/$dotnet_version/linux-x64/publish/* \ + $username@"$ip_address":~/salimax + + ssh "$username"@"$ip_address" "cd salimax && echo '$root_password' | sudo -S ./restart" + + +echo "Deployed and ran commands on $ip_address" +done + + + diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index 88fb7e913..97dda2c66 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -4,6 +4,7 @@ using System.Net.NetworkInformation; using System.Net.Sockets; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; +using System.Security.Cryptography.X509Certificates; using System.Text; using Flurl.Http; using InnovEnergy.App.SaliMax.Devices; @@ -50,8 +51,9 @@ internal static class Program private static readonly Channel RelaysChannel ; private static readonly Channel BatteriesChannel ; - private const String VpnServerIp = "194.182.190.208"; - + //private const String VpnServerIp = "194.182.190.208"; + private const String VpnServerIp = "10.2.0.11"; + private static IPAddress? _controllerIpAddress; private static UdpClient _udpListener = null!; private static ConnectionFactory? _factory ; @@ -314,7 +316,34 @@ internal static class Program { try { - _factory = new ConnectionFactory { HostName = VpnServerIp }; + //_factory = new ConnectionFactory { HostName = VpnServerIp }; + + _factory = new ConnectionFactory + { + HostName = VpnServerIp, + Port = 5672, + VirtualHost = "/", + UserName = "producer", + Password = "b187ceaddb54d5485063ddc1d41af66f", + // Ssl = new SslOption + // { + // Enabled = true, + // ServerName = VpnServerIp, // Disable hostname validation + // CertPath = "/etc/openvpn/client/client-certificate", + // + // CertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => + // { + // X509Certificate2 caCertificate = new X509Certificate2("/etc/openvpn/client/ca-certificate"); + // //Console.WriteLine(caCertificate); + // //Console.WriteLine("---------------------------------"); + // //Console.WriteLine(certificate.GetPublicKey()); + // // Your custom validation logic using the CA certificate + // // Return true if the certificate is valid, false otherwise + // return certificate.Issuer == caCertificate.Subject; + // } + // } + }; + _connection = _factory.CreateConnection(); _channel = _connection.CreateModel(); _channel.QueueDeclare(queue: "statusQueue", durable: true, exclusive: false, autoDelete: false, arguments: null);