atxbyea/samsungrac

SSL errors all of a sudden, not sure of its SSL Lib, or Certificate

tinstep opened this issue · 10 comments

Hi
I run HA in ubuntu 20.04, climate_ip stopped working somewhere in the last month. I've made no changes and it was controlling two Samsung AC units perfectly. When it stopped working added debug clauses for climate_ip to HA and didn't really see any additional details, however after updating HA today to 20210303 I can now see all these errors related to climate_ip coming through the logs.
This is using 3.5.1 of Climate Ip installed via HACS - this has not been updated for a while and was working fine on this version.
As per title, not sure if this is a new SSL library that now enforces something stronger, or the cert is at fault.
I pasted all the certs into a decoder and they all expire in 2059 - but my knowledge is pretty limited with this so hopefully you can help.
(Note I replaced part of token with xxxxx)

2021-03-10 16:28:49 WARNING (MainThread) [homeassistant.loader] You are using a custom integration climate_ip which has not been tested by Home Assistant. This component might cause stability problems, be sure to disable it if you experience issues with Home Assistant
2021-03-10 16:28:49 WARNING (MainThread) [homeassistant.loader] No 'version' key in the manifest file for custom integration 'climate_ip'. This will not be allowed in a future version of Home Assistant. Please report this to the maintainer of 'climate_ip'
2021-03-10 16:28:51 INFO (MainThread) [roonapi] Connecting to Roon server 192.168.0.10:9100
2021-03-10 16:28:51 INFO (Dummy-7) [roonapi] Connection with roon websockets (re)created.
2021-03-10 16:28:51 INFO (Dummy-7) [roonapi] Confirming previous registration with Roon...
2021-03-10 16:28:51 INFO (Thread-4) [roonapi] Registered to Roon server zotac
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] climate_ip: async setup platform
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Loading configuration file: /home/homeassistant/.homeassistant/custom_components/climate_ip/samsung_2878.yaml
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] ip_address: 192.168.0.40
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] token: 8a16xxxx-xxxx-xxxx-xxxx-e4599exxxxx
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Validate properties: False (False)
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Configuration, host: 192.168.0.40:2878
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Configuration, token: 8a16xxxx-xxxx-xxxx-xxxx-e4599exxxxx
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Configuration, duid: bc8ccdd79b0a
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Configuration, cert: /home/homeassistant/.homeassistant/custom_components/climate_ip/ac14k_m.pem
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Updating state...
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Updating getter...
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Executing params: {'duid': 'bc8ccdd79b0a', 'token': '8a16b480-8103-4ba9-a7ba-e4599e20f905', 'host': '192.168.0.40', 'connection_template': '<Request Type="AuthToken"><User Token="{{token}}" /></Request>', 'power_template': '{% if value != none %}{% for key, value in device_state.items() %}{% if key == "AC_FUN_POWER" %}{% if value == "Off" %}<Request Type="DeviceControl"><Control CommandID="AC_FUN_POWER" DUID="{{duid}}"><Attr ID="AC_FUN_POWER" Value="On" /></Control></Request>{% endif %}{% endif %}{% endfor %}{% endif %}', 'value': None, 'device_state': 'unknown'}
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Checking power on template: <Template memory:7f6bde953070>
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Power on template found, rendering
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Power on message:
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Executing command: <Request Type="DeviceState" DUID="bc8ccdd79b0a"></Request>

2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Getting socket connection
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Connection invalid, creating!
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Creating ssl context
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Setting up ciphers
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Setting up verify mode
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Setting up verify location: /home/homeassistant/.homeassistant/custom_components/climate_ip/ac14k_m.pem
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Setting up load cert chain: /home/homeassistant/.homeassistant/custom_components/climate_ip/ac14k_m.pem
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Sending command failed
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Traceback (most recent call last):
  File "/home/homeassistant/.homeassistant/custom_components/climate_ip/samsung_2878.py", line 219, in send_socket_command
    sslSocket = self.socket
  File "/home/homeassistant/.homeassistant/custom_components/climate_ip/samsung_2878.py", line 281, in socket
    self.create_connection()
  File "/home/homeassistant/.homeassistant/custom_components/climate_ip/samsung_2878.py", line 257, in create_connection
    sslContext.load_cert_chain(cfg.cert)
ssl.SSLError: [SSL: CA_MD_TOO_WEAK] ca md too weak (_ssl.c:4022)

2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Retrying sending command...
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Getting socket connection
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Connection invalid, creating!
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Creating ssl context
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Setting up ciphers
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Setting up verify mode
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Setting up verify location: /home/homeassistant/.homeassistant/custom_components/climate_ip/ac14k_m.pem
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Setting up load cert chain: /home/homeassistant/.homeassistant/custom_components/climate_ip/ac14k_m.pem
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Sending command failed
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Traceback (most recent call last):
  File "/home/homeassistant/.homeassistant/custom_components/climate_ip/samsung_2878.py", line 219, in send_socket_command
    sslSocket = self.socket
  File "/home/homeassistant/.homeassistant/custom_components/climate_ip/samsung_2878.py", line 281, in socket
    self.create_connection()
  File "/home/homeassistant/.homeassistant/custom_components/climate_ip/samsung_2878.py", line 257, in create_connection
    sslContext.load_cert_chain(cfg.cert)
ssl.SSLError: [SSL: CA_MD_TOO_WEAK] ca md too weak (_ssl.c:4022)

2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Getter updated with value: {}
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Updating operations...
2021-03-10 16:28:54 INFO (MainThread) [custom_components.climate_ip.climate] Updating properties...

How are you running your HA ? Is it a docker container ? supervised ? core pip install ?

Hi Im running in
"pip install"
image

Just updated to Latest Core HA with same result.
image

The AC uses older certificates, try editing /etc/ssl/openssl.cnf and changing CipherString = DEFAULT@SECLEVEL=2 to CipherString = DEFAULT@SECLEVEL=1

I tried your suggestion in on the host machine - however that line didn't exist, and when i added it and restarted it made no difference. I looked for an etc directory in the python virt environment and didn't see one.
Any other ideas?

OK I think I have fixed it.
Its possible that this error came about when updating to "openssl 1.1.1f"

There are 2 files that need to change.
The first is line 246 of samsung_2878.py

I changed this line "sslContext = ssl.SSLContext(ssl.PROTOCOL_TLSv1)"
to read
sslContext = ssl.SSLContext(ssl.PROTOCOL_TLS)

I also tried using PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2 and PROTOCOL_TLSv1_3 and they all failed with ssl.SSLError: [SSL: CA_MD_TOO_WEAK] ca md too weak (_ssl.c:4022)

The only one to work was "TLS" ( or you can put "SSLv23" as its the same thing according to here:
https://docs.python.org/3/library/ssl.html#ssl.SSLContext

The second file I needed to change was /etc/ssl/openssl.cnf
At the very top of the file I put :
openssl_conf = default_conf
and at the end of the file :

[ default_conf ]
ssl_conf = ssl_sect
[ssl_sect]
system_default = system_default_sect
[system_default_sect]
CipherString = DEFAULT:@SECLEVEL=0

Great, thanks @tinstep I will try to get this mentioned in the readme as a possible solution.

I experience the same problem but with a different (newer?) model.

@@ -102,7 +102,10 @@
             with requests.sessions.Session() as session:
                 self.logger.info(self._params)
                 try:
+                    original_ciphers = requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS
+                    requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = 'ALL:@SECLEVEL=0'
                     resp = session.request(**self._params)
+                    requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = original_ciphers
                     self.logger.info(
                         "Command executed with code: {}, text: {}".format(
                             resp.status_code, resp.text

This is a very quick and dirty hack, but it seems to work.

I do notice the following in the logs, but I think that's the request itself, and not modifying the ciphers.

homeassistant_1         | 2021-05-21 18:03:58 WARNING (MainThread) [homeassistant.util.async_] Detected I/O inside the event loop. This is causing stability issues. Please report issue to the custom component author for climate_ip doing I/O at custom_components/climate_ip/connection_request.py, line 107: resp = session.request(**self._params)
homeassistant_1         | 2021-05-21 18:03:58 ERROR (MainThread) [custom_components.climate_ip.climate] Request execution failed. Stack trace:
homeassistant_1         | Traceback (most recent call last):
homeassistant_1         |   File "/home/homeassistant/.homeassistant/custom_components/climate_ip/connection_request.py", line 107, in execute_internal
homeassistant_1         |     resp = session.request(**self._params)
homeassistant_1         |   File "/opt/homeassistant/lib/python3.8/site-packages/requests/sessions.py", line 542, in request
homeassistant_1         |     resp = self.send(prep, **send_kwargs)

My python (especially async) is a bit rusty, but could someone confirm this? And if so, I think the better solution is to "mount" a specific adapter for this as described here
https://stackoverflow.com/questions/40373115/how-to-select-specific-the-cipher-while-sending-request-via-python-request-modul

I will try to see if I can implement this, but a confirmation on my line of thought would be great.

this work, for me:

--- a/custom_components/climate_ip/samsung_2878.py
+++ b/custom_components/climate_ip/samsung_2878.py
@@ -245,7 +245,7 @@ class ConnectionSamsung2878(Connection):
         self.logger.info("Creating ssl context")
         sslContext = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
         self.logger.info("Setting up ciphers")
-        sslContext.set_ciphers("HIGH:!DH:!aNULL")
+        sslContext.set_ciphers("ALL:@SECLEVEL=0")
         self.logger.info("Setting up verify mode")
         sslContext.verify_mode = (
             ssl.CERT_REQUIRED if cfg.cert is not None else ssl.CERT_NONE