From 7e9257c4133757fb77f327527c3dbf634194265c Mon Sep 17 00:00:00 2001 From: Torstein Lundervold Nesheim Date: Fri, 24 Jun 2022 14:28:17 +0200 Subject: [PATCH 1/3] Add TLS protection to broker --- broker/Dockerfile | 6 +++- broker/README.md | 8 +++++ broker/mosquitto/config/certs/ca-cert.pem | 35 +++++++++++++++++++ broker/mosquitto/config/certs/server-cert.pem | 34 ++++++++++++++++++ broker/mosquitto/config/mosquitto.conf | 10 +++--- broker/mosquitto/config/passwd_file | 2 +- docker-compose.yml | 7 ++-- 7 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 broker/mosquitto/config/certs/ca-cert.pem create mode 100644 broker/mosquitto/config/certs/server-cert.pem diff --git a/broker/Dockerfile b/broker/Dockerfile index d81ffd9c1..6314fdbca 100644 --- a/broker/Dockerfile +++ b/broker/Dockerfile @@ -2,7 +2,11 @@ FROM eclipse-mosquitto:latest COPY mosquitto/config/ mosquitto/config/ -EXPOSE 1883 9001 +ARG TLS_SERVER_KEY + +RUN echo "${TLS_SERVER_KEY}" > mosquitto/config/certs/server-key.pem + +EXPOSE 1883 USER 1883 diff --git a/broker/README.md b/broker/README.md index 1063aaf14..fb692697e 100644 --- a/broker/README.md +++ b/broker/README.md @@ -1,5 +1,13 @@ # Mosquitto MQTT Broker +## Set up the broker + +The broker expects a private key for its server x509 certificate used for TLS. +This must be provided through an environment variable called `FLOTILLA_BROKER_SERVER_KEY`. +This is a secret, and should be treated as such. +The best way to pass this is to store it in a `.env` file in the root of flotilla, and docker compose loads this by default on startup. +See [Using the “--env-file” option](https://docs.docker.com/compose/environment-variables/#using-the---env-file--option) for more information. + ## Running the broker From the flotilla root directory, run the following command: diff --git a/broker/mosquitto/config/certs/ca-cert.pem b/broker/mosquitto/config/certs/ca-cert.pem new file mode 100644 index 000000000..02b7b662a --- /dev/null +++ b/broker/mosquitto/config/certs/ca-cert.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +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 +NOEALuJzso8ziFZOqO+rGx5jX5ii +-----END CERTIFICATE----- diff --git a/broker/mosquitto/config/certs/server-cert.pem b/broker/mosquitto/config/certs/server-cert.pem new file mode 100644 index 000000000..94085c48f --- /dev/null +++ b/broker/mosquitto/config/certs/server-cert.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF+zCCA+OgAwIBAgIUMsBK/Fg9GkEJpInOnGuQScXxJ3MwDQYJKoZIhvcNAQEL +BQAwgZcxCzAJBgNVBAYTAk5PMQ8wDQYDVQQIDAZCZXJnZW4xDzANBgNVBAcMBkJl +cmdlbjEUMBIGA1UECgwLRXFfUm9ib3RpY3MxETAPBgNVBAsMCFJvYm90aWNzMRww +GgYDVQQDDBNSb2JvdGljc19TZWxmU2lnbmVkMR8wHQYJKoZIhvcNAQkBFhB0bG5l +QGVxdWlub3IuY29tMB4XDTIyMDYyODE0MTEyNVoXDTIyMDgyNzE0MTEyNVowgZgx +CzAJBgNVBAYTAk5PMQ8wDQYDVQQIDAZCZXJnZW4xDzANBgNVBAcMBkJlcmdlbjER +MA8GA1UECgwIRmxvdGlsbGExFDASBgNVBAsMC01RVFQgQnJva2VyMR0wGwYDVQQD +DBRGbG90aWxsYV9NUVRUX0Jyb2tlcjEfMB0GCSqGSIb3DQEJARYQdGxuZUBlcXVp +bm9yLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMe05p5K08ii +zG7dv/LJmIWCLGj6FUp5+Whpni9UIFATibXUl2yTl/FAtrxq/L4ZkVpiTDP5kQno +7/Y3MuWQumC2ivjfL3TSAKES5ec3Vmdolz16sCn1BZtKe68t4re9nF5AhS5i6WPh +NWyMN2o5UwBsEe3tZBowkAPLdfFaOc1JSEDbFnHqyiypeBbwfJ2qJe/J2yUN+m+5 +MY22kZDf2J+6k3MUJQvA8KF6usMjjMrenx7uvGE3OU9XtDU4qtvOMD8p5I4fDqBv +k7v2STOiOkuZOoBD98PpRFVjh+2HijiHfZqzKhNs6XWicOhYlJzp3nNr7+fJp+DJ +8PP+zqBKeK2TlKCJAISCVSNiTHbHaHwbCXzNzj9vfy8+8qplcUe35hF2fXlVr1zF +VObkWeVNVQxR726TF0l/pvFez6qNTMag4IZaZyi6joU+06OdBSO8Ch57hwIkkp46 +jGxs/zoSwevA0PU0vcYCbydQXkfgvmEzOvluUBwN/WgR+v8r0ubPTAl9I5EhuKU4 +7sBD43OZD1nSWEWGidTjzMcOOLSZLAP50eWxF486DuLf65UKoZ0oWB0LDH+N+lU8 +kSix4RJuGNIOSEd+AJCw9+zG6efL5cAy/yy4SsbL/QRxE0wk4pA+v3xzlBdDdxlu +PXPaMYClvHZDTiN7p0fx6IjYyBp49F8ZAgMBAAGjPDA6MDgGA1UdEQQxMC+HBAAA +AACCCWxvY2FsaG9zdIIGYnJva2VyghRob3N0LmRvY2tlci5pbnRlcm5hbDANBgkq +hkiG9w0BAQsFAAOCAgEAZpNWT1c99ClbW1N299u/fZ6CQ7aLFyDyMintK3SPjNgc +nUz78MAm/rP4tH2FjG7GLlXEaKKxzenqeWlF/LbndykrN9vRwkiz0C15MWRz3wLU +lYm8XaXRsA5CKOGKFyxpBAngHkiIVOBX2aAhWFUPHrxnoldgBK8MNdK3+xBy6Ck8 +04G7pbg/4sGDflDoo+e2NOeU69JCt5mrpLGIggPP+tvwn9jnxYOJfPxahdGHx44N +ajbjtJtKm9jMHxaHzfkAPIIdMITLiwNBIS0+JINwaJv4Er9zkK0jxeIwkKPxmfNI +za8f2dPxecOqLSvKo/+JFAu4Ib/NCcb9f+b8WykS52YuWaO+9KQsQV3HDx4EXDjv +KoMNw1YfViYn5vLTynukR4BGnessEm9+F3ZD8xEdSHzSOpZx2iAJB8xKjCthmhfz +9S508us7C3bkc6L+JFWg6ph4/5VC2gfUnKAUIqfygsLHYFSKmHKmz+m71yrLzviG +wCV11ca9e18GKUQohnA99fclCAwn7mI6Osr5WtaabcKJwh3PEmekQKic+OK3Yop0 +x54U+SbPbNbWIX/E3wCppL/s3zHA4/m1X5kardBcD5x9uJybcNpTY/TDJFV+XxOx +rGgbfAwb3QqJs8qt+45JFMQ8B2YAqQc+aozdd1Mvvv9h/9+ut6to3uV/pokhpX4= +-----END CERTIFICATE----- diff --git a/broker/mosquitto/config/mosquitto.conf b/broker/mosquitto/config/mosquitto.conf index 5fa762083..68f04d9b2 100644 --- a/broker/mosquitto/config/mosquitto.conf +++ b/broker/mosquitto/config/mosquitto.conf @@ -232,8 +232,8 @@ listener 1883 0.0.0.0 protocol mqtt -listener 9001 0.0.0.0 -protocol websockets +#listener 9001 0.0.0.0 +#protocol websockets # By default, a listener will attempt to listen on all supported IP protocol # versions. If you do not have an IPv4 or IPv6 interface you may wish to @@ -316,10 +316,10 @@ protocol websockets # TLS encryption. # Path to the PEM encoded server certificate. -#certfile +certfile mosquitto/config/certs/server-cert.pem # Path to the PEM encoded keyfile. -#keyfile +keyfile mosquitto/config/certs/server-key.pem # If you wish to control which encryption ciphers are used, use the ciphers # option. The list of available ciphers can be optained using the "openssl @@ -360,7 +360,7 @@ protocol websockets # containing the CA certificates. For capath to work correctly, the # certificate files must have ".crt" as the file ending and you must run # "openssl rehash " each time you add/remove a certificate. -#cafile +cafile mosquitto/config/certs/ca-cert.pem #capath diff --git a/broker/mosquitto/config/passwd_file b/broker/mosquitto/config/passwd_file index a521a5b9c..5e77b451f 100644 --- a/broker/mosquitto/config/passwd_file +++ b/broker/mosquitto/config/passwd_file @@ -1,3 +1,3 @@ -admin:$7$101$+GsirAIRSKXQLk5z$MMl/WR6vxmH0igzvtcYx3LmhLn168CJk2b8c1TiPheYtgK4A0eJrRxptz4iA6B62TLda1u2329ntw7YkLVrRUw== +admin:$7$101$0gb0/MsZQODnBqea$cKG/aB3kZ2ThPFKCxxwBMCfgIAgeLNsmiU4oljI82AOrNplnG41LJ224S2Gq4212M5cvZr4PoOCc5IjKgjIldA== flotilla:$7$101$tmAqSZNbVPjre70A$/yJ+WcI/749Q9SYpbBe+wii0pea5omHssdUIb3+NQiwLmy/ppwFVFTFvHLCoZasDNMGfkQ+FoLilLMtgjqwUZA== isar:$7$101$ge+J8JDGhAXCDgud$SKG7TYUaKzTcYhLBjOwNAP0G2tr25pMKneo4ehOF1I/15QjgjNsYqP8NZKu+n52F3B/7Oqv0KI3NFBg3xcTd1A== diff --git a/docker-compose.yml b/docker-compose.yml index adf2dbec6..683795ea4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,17 @@ version: "3.7" + services: frontend: build: frontend ports: - "3001:3001" broker: - build: broker + build: + context: broker + args: + - TLS_SERVER_KEY=${FLOTILLA_BROKER_SERVER_KEY} ports: - "1883:1883" - - "9001:9001" backend: build: backend ports: From 02c357773d985d58850085b229fd85ea584cf353 Mon Sep 17 00:00:00 2001 From: Torstein Lundervold Nesheim Date: Tue, 28 Jun 2022 14:07:34 +0200 Subject: [PATCH 2/3] Add TLS encryption for MQTT client --- backend/README.md | 7 ++--- backend/api/MQTT/MqttService.cs | 45 +++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/backend/README.md b/backend/README.md index 45917e4d5..89ddbb00b 100644 --- a/backend/README.md +++ b/backend/README.md @@ -35,11 +35,8 @@ Read more about the `launchSettings.json` file For the backend to work when dockerized, you need to have the client secret exposed as an environment variable named `FLOTILLA_CLIENT_SECRET`. -The simplest way to do this in bash is to run - -``` -export FLOTILLA_CLIENT_SECRET=SuperSecret -``` +The best way to do this is to store it in an `.env` file in the root of the flotilla repository. +See [Using the “--env-file” option](https://docs.docker.com/compose/environment-variables/#using-the---env-file--option) for more information. To run the backend in docker, run the following command in the root folder of flotilla: diff --git a/backend/api/MQTT/MqttService.cs b/backend/api/MQTT/MqttService.cs index 70750f067..64095e4ed 100644 --- a/backend/api/MQTT/MqttService.cs +++ b/backend/api/MQTT/MqttService.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Net.Security; +using System.Text; using System.Text.Json; using Api.Mqtt.Events; using Api.Mqtt.MessageModels; @@ -24,14 +25,16 @@ public class MqttService : BackgroundService private readonly ManagedMqttClientOptions _options; + private readonly bool _isDevelopment; + private readonly string _serverHost; private readonly int _serverPort; private readonly TimeSpan _reconnectDelay = TimeSpan.FromSeconds(5); private readonly int _maxRetryAttempts; private readonly bool _shouldFailOnMaxRetries; - private int _reconnectAttempts; + private CancellationToken _cancellationToken; public MqttService(ILogger logger, IConfiguration config) @@ -42,6 +45,9 @@ public MqttService(ILogger logger, IConfiguration config) _mqttClient = mqttFactory.CreateManagedMqttClient(); string password = config.GetValue("mqtt-broker-password"); + _isDevelopment = ( + config.GetValue("ASPNETCORE_ENVIRONMENT") ?? "Production" + ).Equals("Development", StringComparison.OrdinalIgnoreCase); var mqttConfig = config.GetSection("Mqtt"); string username = mqttConfig.GetValue("Username"); @@ -52,6 +58,15 @@ public MqttService(ILogger logger, IConfiguration config) var builder = new MqttClientOptionsBuilder() .WithTcpServer(_serverHost, _serverPort) + .WithTls( + o => + { + o.UseTls = true; + o.CertificateValidationHandler = CustomCertificateHandler; + if (_isDevelopment) + o.IgnoreCertificateChainErrors = true; + } + ) .WithCredentials(username, password); _options = new ManagedMqttClientOptionsBuilder() @@ -246,5 +261,31 @@ private void OnIsarTopicReceived(string content) where T : MqttMessage _logger.LogWarning("{msg}", e.Message); } } + + /// + /// A workaround for a bug in the MQTTNet framework where the IgnoreCertificateChainErrors option is not being considered. + /// + /// + /// Proposed solution in MQTTNet: + /// + /// + /// + private bool CustomCertificateHandler( + MqttClientCertificateValidationCallbackContext context + ) + { + bool approved; + if (context.ClientOptions.TlsOptions.IgnoreCertificateChainErrors) + approved = + context.SslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors + || context.SslPolicyErrors == SslPolicyErrors.None; + else + approved = context.SslPolicyErrors == SslPolicyErrors.None; + + if (!approved) + _logger.LogError("Error with remote certificate: {error}", context.SslPolicyErrors); + + return approved; + } } } From e648d74d483b68c63e4ea6b24e01b17bcd5f385a Mon Sep 17 00:00:00 2001 From: Torstein Lundervold Nesheim Date: Tue, 28 Jun 2022 16:26:28 +0200 Subject: [PATCH 3/3] Add settings for production --- backend/api/appsettings.Production.json | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 backend/api/appsettings.Production.json diff --git a/backend/api/appsettings.Production.json b/backend/api/appsettings.Production.json new file mode 100644 index 000000000..ec07e71b0 --- /dev/null +++ b/backend/api/appsettings.Production.json @@ -0,0 +1,30 @@ +{ + "AzureAd": { + "ClientId": "ea4c7b92-47b3-45fb-bd25-a8070f0c495c", + "ClientSecret": "Fill in ASP.NET Secret Manager" + }, + "KeyVault": { + "VaultUri": "https://PlaceboFlotillaDevKv.vault.azure.net/" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Warning" + } + }, + "AllowedHosts": "*", + "AllowedOrigins": "http://localhost:3001", + "Mqtt": { + "Host": "localhost", + "Port": 1883, + "Username": "flotilla", + "Topics": [ + "isar/+/mission", + "isar/+/task", + "isar/+/step" + ], + "MaxRetryAttempts": 15, + "ShouldFailOnMaxRetries": true + } +}