Skip to content

Instantly share code, notes, and snippets.

@jimangel
Last active December 29, 2023 05:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jimangel/77bd9393e0097e748005fb8ee44db6bf to your computer and use it in GitHub Desktop.
Save jimangel/77bd9393e0097e748005fb8ee44db6bf to your computer and use it in GitHub Desktop.
Home Assistant with ecowitt: HTTP to HTTPS nginx reverse proxy secure DIY solution

Home Assistant with ecowitt: HTTP to HTTPS nginx reverse proxy secure DIY solution

Shoutout @del13r for posting a great tutorial on the community forum and indepth feedback on GitHub issues.

Problem

Ecowitt can only send stats to HTTP API endpoints and we want to keep our Home Assistant secure via HTTP/s access only.

Resolution

Run a docker container on home assistant that restarts automatically*

*I'm running it on the same server as Home Assistant, but it can be ran anywhere on anything running nginx.

Prereqs

  • docker
  • host (home assistant)

Why is it more secure?

  • Can limit traffic modification to HTTPS endpoint
  • Can restrict traffic by IP (even locally) or IP range
  • Can enforce nginx HTTPS/SSL validation on the HTTPS endpoint connection
  • Doesn't require Home Assistant changes
  • Easy to tweak to match your security model, know what's running (where and why!)

Setup nginx

Create NGINX Configuration File

mkdir -p ~/nginx-reverse-proxy

Create the NGINX configuration template

cat <<'EOF' > ~/nginx-reverse-proxy/nginx.conf.template
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
    worker_connections 1024;
}
http {
    log_format main '$remote_addr - [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent"';
    access_log /var/log/nginx/access.log main;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    server_tokens off; # disable sharing the nginx version
    keepalive_timeout 65;
    types_hash_max_size 2048;
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    server {
        server_name localhost;
        location / {
            allow ${ECOWITT_GW1000_IP}; # Replace with your allowed IP
            deny all; # Deny all other IPs
            proxy_pass https://${PROXY_SERVER_IP}:${PROXY_SERVER_PORT}${PROXY_PATH};
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;      
            proxy_ssl_verify off;  # Use with caution
            proxy_ssl_server_name on;
            proxy_ssl_protocols TLSv1.3;
            proxy_ssl_session_reuse on;
            proxy_http_version 1.1;
        }
    }
}
EOF
# export env vars

export PROXY_SERVER_IP="external-url-or-internal-ip.ui.nabu.casa"
export PROXY_PATH="/api/webhook/9asd82kmf8vh23iyam3f9"
export PROXY_SERVER_PORT="443"
# or range to test (ie: 192.168.7.0/24)
export ECOWITT_GW1000_IP='192.168.7.163'

Create a final NGINX configuration file from the template

When you tell envsubst exactly which variables to swap (like $PROXY_SERVER_IP, $PROXY_PATH, $PROXY_SERVER_PORT, $ECOWITT_GW1000_IP), it only replaces those and leaves the remaing variables alone. This lets us tweak just what we need without messing up the other settings that Nginx needs to do its thing.

envsubst '$PROXY_SERVER_IP,$PROXY_PATH,$PROXY_SERVER_PORT,$ECOWITT_GW1000_IP' < ~/nginx-reverse-proxy/nginx.conf.template > ~/nginx-reverse-proxy/nginx.conf

Run NGINX Docker Container

# Run the NGINX Docker container with the final configuration
docker run -d --name nginx-reverse-proxy-container \
--restart unless-stopped \
-p 1337:80 \
-v ~/nginx-reverse-proxy/nginx.conf:/etc/nginx/nginx.conf:ro \
nginx:latest

Check if it's up:

# docker ps

# check logs by id

# docker logs ID

image

One major downside to running it on my managed Home Assistant, I get the message that I'm running unsupported software:

image

I'm going to yolo ignore it for now and see how things go. Many folks have said this is ok and updates will not stop, I just cannot expect support to provide support for my customizaitons. That's fair!

Data consumed by the post:

Looking at the logs from the nginx reverse proxy, I see that I'm POSTing 2 bytes per minute to my external HTTPS endpoint.

At 2 bytes per minute over the internet, i'll send approximately 87,667 bytes (about 87.7 kilobytes) of data on average per month.

TODO:

I would like to validate the SSL via nginx with something like: https://stackoverflow.com/questions/66111292/nginx-proxy-ssl-trusted-certificate-with-letsencrypt-upstream

But, thinking "what if" the backend was redirect malicously, it would be able to intercept 2 bytes of my environment post / body data and not really much more.

It might be worth it to try later.

# Download the Certificates
You can download the Let's Encrypt certificates using the curl command. The URLs for the root and intermediate certificates can be found on the Let's Encrypt website or through their documentation. As of my last update, you can use the following commands:

# Download the ISRG Root X1 Certificate
curl -o isrgrootx1.pem https://letsencrypt.org/certs/isrgrootx1.pem

# Download the Let’s Encrypt R3 Intermediate Certificate
curl -o letsencrypt-r3.pem https://letsencrypt.org/certs/lets-encrypt-r3.pem

# Combine the Certificates into a Bundle
cat isrgrootx1.pem letsencrypt-r3.pem > letsencrypt-ca-bundle.pem

# Configure Nginx
#proxy_ssl_trusted_certificate /path/to/your/letsencrypt-ca-bundle.pem;
#proxy_ssl_verify on;
#proxy_ssl_verify_depth 2;

Successful failed request:

Shows that we can hit the api endpoint, but it's a GET not a POST - so it's Not Allowed

% curl -vvv 192.168.7.139:1337/    
*   Trying 192.168.7.139:1337...
* Connected to 192.168.7.139 (192.168.7.139) port 1337 (#0)
> GET / HTTP/1.1
> Host: 192.168.7.139:1337
> User-Agent: curl/8.1.2
> Accept: */*
> 
< HTTP/1.1 405 Method Not Allowed
< Server: nginx
< Date: Wed, 20 Dec 2023 15:28:31 GMT
< Content-Type: application/octet-stream
< Content-Length: 0
< Connection: keep-alive
< Referrer-Policy: no-referrer
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< 
* Connection #0 to host 192.168.7.139 left intact
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment