sergioperez / fronius-auth-proxy

Authentication proxy to avoid using the HTTP digest implementation used on the Fronius inverter controllers
3 stars 2 forks source link

A scripted alternative to this proxy #4

Open filmackay opened 1 day ago

filmackay commented 1 day ago

This might help someone, so I thought I'd post it here. It is a bash-script equivalent of what this proxy does. Note it does need: curl, awk, grep, sed, openssl:

#!/bin/bash

# Input parameters
USERNAME="service"
PASSWORD="***"
URL="http://***/config/exportlimit/?method=save"
DATA_ON=' {"powerLimits":{"exportLimits":{"activePower":{"hardLimit":{"enabled":false,"powerLimit":0},"mode":"entireSystem","softLimit":{"enabled":true, "powerLimit":0}},"failSafeModeEnabled":false},"visualization":{"exportLimits":{"activePower":{"displayModeHardLimit":"absolute","displayModeSoftLimit":"absolute"}},"wattPeakReferenceValue":0}}}'
DATA_OFF='{"powerLimits":{"exportLimits":{"activePower":{"hardLimit":{"enabled":false,"powerLimit":0},"mode":"off",         "softLimit":{"enabled":false,"powerLimit":0}},"failSafeModeEnabled":false},"visualization":{"exportLimits":{"activePower":{"displayModeHardLimit":"absolute","displayModeSoftLimit":"absolute"}},"wattPeakReferenceValue":0}}}'
METHOD="POST"

# Convert argument to uppercase to match variable names
ACTION=$(echo "$1" | tr '[:lower:]' '[:upper:]')

# Use indirect variable referencing to get the DATA
DATA_VAR="DATA_$ACTION"
DATA="${!DATA_VAR}"

# Check if argument is provided
if [ -z "$1" ]; then
  echo "Usage: $0 [ON|OFF]"
  exit 1
fi

# Step 1: Get the authentication challenge (x-www-authenticate header)
AUTH_CHALLENGE=$(curl -s -D - "$URL" -o /dev/null | grep -i 'x-www-authenticate:' | sed 's/x-www-authenticate: //I' | tr -d '\r')

# Check if AUTH_CHALLENGE is empty
if [ -z "$AUTH_CHALLENGE" ]; then
    echo "Failed to get authentication challenge from server."
    exit 1
fi

# Step 2: Parse the challenge parameters
realm=$(echo "$AUTH_CHALLENGE" | sed -n 's/.*realm="\([^"]*\)".*/\1/p')
nonce=$(echo "$AUTH_CHALLENGE" | sed -n 's/.*nonce="\([^"]*\)".*/\1/p')
qop=$(echo "$AUTH_CHALLENGE" | sed -n 's/.*qop="\([^"]*\)".*/\1/p')

# If qop is empty, default to 'auth'
if [ -z "$qop" ]; then
    qop="auth"
fi

# Other parameters
uri="/config/exportlimit/?method=save"
nc="00000001"
cnonce="abcdef0123456789"

# Step 3: Compute HA1, HA2, and Response using OpenSSL
# HA1 = MD5(username:realm:password)
ha1=$(printf "%s:%s:%s" "$USERNAME" "$realm" "$PASSWORD" | openssl md5 | awk '{print $2}')

# HA2 = MD5(method:uri)
ha2=$(printf "%s:%s" "$METHOD" "$uri" | openssl md5 | awk '{print $2}')

# Response = MD5(HA1:nonce:nc:cnonce:qop:HA2)
response=$(printf "%s:%s:%s:%s:%s:%s" "$ha1" "$nonce" "$nc" "$cnonce" "$qop" "$ha2" | openssl md5 | awk '{print $2}')

# Build the Authorization header
AUTH_HEADER="Digest username=\"$USERNAME\", realm=\"$realm\", nonce=\"$nonce\", uri=\"$uri\", algorithm=MD5, response=\"$response\", qop=$qop, nc=$nc, cnonce=\"$cnonce\""

# Step 4: Make the authenticated request
curl -X "$METHOD" "$URL" \
  -H "Content-Type: application/json;charset=utf-8" \
  -H "Authorization: $AUTH_HEADER" \
  -d "$DATA"
filmackay commented 1 day ago

Note this runs against:

Device: Fronius Primo 5.0-1 (with Smart Meter 63A) Software version: 3.31.1-7