home-assistant / core

:house_with_garden: Open source home automation that puts local control and privacy first.
https://www.home-assistant.io
Apache License 2.0
74.15k stars 31.13k forks source link

verify_ssl seems to be ignored on rest_command #118396

Open sathia-musso opened 6 months ago

sathia-musso commented 6 months ago

The problem

I am trying to make a request to a device in my network which supports https. something like this:

rest_command:
  chiama_inim_porta:
    verify_ssl: false
    url: "https://192.168.0.107/cgi-bin/api.cgi?apikey=BLAH&cmd=set_outputs_mode&p1=29&p2=1"
    content_type: "application/json; charset=utf-8"

I can verify with curl that the server answers correctly, even from HA terminal

curl --header 'Content-type: application/json' -k -v https://192.168.0.107/cgi-bin/api.cgi\?apikey\=BLAH\&cmd\=set_outputs_mode\&p1\=29\&p2\=1
*   Trying 192.168.0.107:443...
* Connected to 192.168.0.107 (192.168.0.107) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / AES256-GCM-SHA384 / UNDEF / UNDEF
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: C=IT; ST=Italy; L=Monteprandone (AP); O=Inim Electronics; CN=www.inim.biz; emailAddress=info@inim.biz
*  start date: Jan 11 15:09:13 2022 GMT
*  expire date: Jan  9 15:09:13 2032 GMT
*  issuer: C=IT; ST=Italy; L=Monteprandone (AP); O=Inim Electronics; CN=www.inim.biz; emailAddress=info@inim.biz
*  SSL certificate verify result: self-signed certificate (18), continuing anyway.
*   Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
* using HTTP/1.x
> GET /cgi-bin/api.cgi?apikey=BLAH&cmd=set_outputs_mode&p1=29&p2=1 HTTP/1.1
> Host: 192.168.0.107
> User-Agent: curl/8.5.0
> Accept: */*
> Content-type: application/json
>
< HTTP/1.1 200 OK
< Date: Wed, 29 May 2024 17:10:39 GMT
< Server: Boa/0.94.14rc21
< Accept-Ranges: bytes
< Connection: close
< Cache-Control: no-cache, no-store, must-revalidate
< Pragma: no-cache
< Expires: 0
< Content-Type: application/json
<
* Closing connection
* TLSv1.2 (OUT), TLS alert, close notify (256):
{ "Status" : 0, "Data": "OK"}%

Whenever I call the rest_command I receive this in the trace, the logs don't have any other information:

Error: Client error occurred when calling resource "https://192.168.0.107/cgi-bin/api.cgi?apikey=BLAH&cmd=set_outputs_mode&p1=29&p2=1" 

That's all it says, if I use http the automation works perfectly, this makes me think that verify_ssl: false is ignored, as far as I can see there is not a way to debug the error further.

could it be here the issue?

    vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean,

I'm not sure

home-assistant[bot] commented 6 months ago

Hey there @jpbede, mind taking a look at this issue as it has been labeled with an integration (rest_command) you are listed as a code owner for? Thanks!

Code owner commands Code owners of `rest_command` can trigger bot actions by commenting: - `@home-assistant close` Closes the issue. - `@home-assistant rename Awesome new title` Renames the issue. - `@home-assistant reopen` Reopen the issue. - `@home-assistant unassign rest_command` Removes the current integration label and assignees on the issue, add the integration domain after the command. - `@home-assistant add-label needs-more-information` Add a label (needs-more-information, problem in dependency, problem in custom component) to the issue. - `@home-assistant remove-label needs-more-information` Remove a label (needs-more-information, problem in dependency, problem in custom component) on the issue.

(message by CodeOwnersMention)


rest_command documentation rest_command source (message by IssueLinks)

jpbede commented 6 months ago

I actually doubt that verify_ssl is ignored, it gets passed here into a function we use all over the code base. You could try to enable debug log for rest_command:

logger:
  logs:
    homeassistant.components.rest_command: debug
sathia-musso commented 6 months ago

I've enabled the logs and alls I see in /config/home-assistant.log is ERROR MainThread .... Error executing script. Error for call_service at pos 1: Client error occurred when calling resource "https://...." and nothing else. any way to get a stack trace?

sathia-musso commented 6 months ago

great, I hope this patch is merged soon. Thank you very much for looking into this and acknowledging that the debugging is a bit difficult right now. Out of curiosity, is there a way to monkey patch your class in order to quickly debug it? I wouldn't mind editing the file with vim for this test. thanks

jpbede commented 6 months ago

Sure, you can change the file in the container. It is located at /usr/src/homeassistant/homeassistant/components/rest_command/__init__.py inside the condainer.

Then you need to restart the container

sathia-musso commented 6 months ago

Hi, I've managed to monkey patch your class and I see this

2024-05-30 10:44:37.791 ERROR (MainThread) [homeassistant.components.rest_command] Error fetching data: Cannot connect to host 192.168.0.107:443 ssl:default [[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1000)]

it looks like it's not ignoring wrong certs.

jpbede commented 6 months ago

Are you absolutely sure that you've verify_ssl: false in your configuration while testing that? Did you reload the config after changing? I've tested this on my system, when I set this, I can call a URL with a self-signed certificate, when I remove it, it fails.

Which version of HA do use?

sathia-musso commented 6 months ago

Atm it looks like this, and you can ignore my warnings about it not taking the correct variable, I was making tests and forgot it set to true, but the problem is still here:

Core 2024.5.5
Supervisor 2024.05.1
Operating System 12.3
Frontend 20240501.1

--

rest_command:
  chiama_inim_porta:
    verify_ssl: false
    url: "https://192.168.0.107/cgi-bin/api.cgi?apikey=BLAH&cmd=set_outputs_mode&p1=29&p2=1"
    content_type: "application/json; charset=utf-8"

--

2024-05-30 11:47:35.179 ERROR (MainThread) [homeassistant.components.rest_command] Error fetching data: Cannot connect to host 192.168.0.107:443 ssl:default [[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1000)]
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 1025, in _wrap_create_connection
    return await self._loop.create_connection(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/base_events.py", line 1147, in create_connection
    transport, protocol = await self._create_connection_transport(
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/base_events.py", line 1180, in _create_connection_transport
    await waiter
  File "/usr/local/lib/python3.12/asyncio/sslproto.py", line 578, in _on_handshake_complete
    raise handshake_exc
  File "/usr/local/lib/python3.12/asyncio/sslproto.py", line 560, in _do_handshake
    self._sslobj.do_handshake()
  File "/usr/local/lib/python3.12/ssl.py", line 917, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1000)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/rest_command/__init__.py", line 143, in async_service_handler
    async with getattr(websession, method)(
  File "/usr/local/lib/python3.12/site-packages/aiohttp/client.py", line 1197, in __aenter__
    self._resp = await self._coro
                 ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/aiohttp/client.py", line 581, in _request
    conn = await self._connector.connect(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 544, in connect
    proto = await self._create_connection(req, traces, timeout)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 944, in _create_connection
    _, proto = await self._create_direct_connection(req, traces, timeout)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 1257, in _create_direct_connection
    raise last_exc
  File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 1226, in _create_direct_connection
    transp, proto = await self._wrap_create_connection(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 1029, in _wrap_create_connection
    raise ClientConnectorSSLError(req.connection_key, exc) from exc
aiohttp.client_exceptions.ClientConnectorSSLError: Cannot connect to host 192.168.0.107:443 ssl:default [[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1000)]

in my curl request there are SSL specifics. maybe it's an old protocol that is no longer working on aiohttp?

jpbede commented 6 months ago

Could you run curl from within the Home Assistant container?

sathia-musso commented 6 months ago

yes, that's how I originally implemented it, but I also wanted to get the json output for my automation. curl as a command returns a stdout and stderr string which looks overly complicated to parse within an automation.

I just realized that this system is using a Boa server, last release 2005 :) so it's understandable that python doesn't want to talk with it. I'm writing the vendor (Inim) to see if it's possible to upgrade to a modern web server

alas, aiohttp lies when it says that it can ignore SSL

jpbede commented 6 months ago

alas, aiohttp lies when it says that it can ignore SSL

Well, it ignores self-signed certificates and so on... but if the server uses an old cipher that we don't support, then no handshake can be performed and then there is nothing to ignore :)

If you want to really ignore SSL than use plain HTTP :)

sathia-musso commented 6 months ago

I realize that I could use plain HTTP but what I wanted to do is to open a mechanical door and I'm not too keen on sending an important request such as this in clear within the network.

sathia-musso commented 6 months ago

If someone stumbles into this issue here's how I've "fixed" it, I've made a proxy with Nginx to upastrem requests from cloudflare to the Inim server so I don't have to play with certificates.

upstream inim{
  server 192.168.0.107:443;
  keepalive 360;
}

server {
    server_name your.domain.com;
    access_log /var/log/nginx/your.domain.com.access.log;
    error_log /var/log/nginx/your.domain.com.error.log error;

    location = /cgi-bin/api.cgi{
      proxy_pass https://inim;
      proxy_ssl_verify off;
    }

    location / {
        rewrite ^/(.*)$ https://www.google.com/ permanent;
    }
    listen 192.168.1.121:80;
}

this way everything works and I'm sure there's no data passing in plain text anywhere. You can also make it slightly different to not use cloudflare and/or to block to your ip

allow 123.456.123.456;
deny all;
screenagerbe commented 3 months ago

I would like to jump in into this conversation as I'm also using an Inim web server in my local network. I managed to get https working in the RESTful integration. I did not get it to work in RESTful Command though. I believe this is related to a missing configuration variable in the RESTfull Command integration.

The error message I receive:

Logger: homeassistant.components.rest_command
Source: components/rest_command/__init__.py:203
integration: RESTful Command ([documentation](https://www.home-assistant.io/integrations/rest_command), [issues](https://github.com/home-assistant/core/issues?q=is%3Aissue+is%3Aopen+label%3A%22integration%3A+rest_command%22))
First occurred: 23 August 2024 at 23:55:31 (1 occurrences)
Last logged: 23 August 2024 at 23:55:31
Error fetching data: Cannot connect to host 192.168.x.x:443 ssl:default [[SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] ssl/tls alert handshake failure (_ssl.c:1000)

in RESTfull you have a specific configuration variable available to handle this error:

Once this variable is set to intermediate I can send https calls over RESTful, but this configuration variable is not available in RESTfull Command. Is it possible to add it?

issue-triage-workflows[bot] commented 1 week ago

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates. Please make sure to update to the latest Home Assistant version and check if that solves the issue. Let us know if that works for you by adding a comment 👍 This issue has now been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.