From 5a4b1a3bf1ed11d5fc0ef481e4e1c62cd57e646c Mon Sep 17 00:00:00 2001 From: Sam Hadow Date: Fri, 24 Jul 2026 21:44:42 +0200 Subject: [PATCH] new scripts freebox: reboot, get token --- .gitignore | 1 + README.md | 19 +++++++++ freebox/freebox_ecc_root_ca.pem | 15 +++++++ freebox/freebox_reboot.sh | 72 +++++++++++++++++++++++++++++++++ freebox/get_token.sh | 61 ++++++++++++++++++++++++++++ 5 files changed, 168 insertions(+) create mode 100644 .gitignore create mode 100644 freebox/freebox_ecc_root_ca.pem create mode 100755 freebox/freebox_reboot.sh create mode 100755 freebox/get_token.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0790ae4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +freebox_app_token.txt diff --git a/README.md b/README.md index add393a..e1220a8 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,25 @@ It assumes wireguard connection names start with "wg-" You can check for DNS leaks with [this website](https://www.dnsleaktest.com/) +## freebox + +Scripts to interact with a Freebox OS using the official API. + +### get_token.sh + +Registers a new application with the Freebox and requests authorisation. +After approving the request on the Freebox, the generated app token is saved to `freebox_app_token.txt`. +By default generates a token for the `APP_ID` `fr.bash.reboot` +curl and jq are needed. + +### freebox_reboot.sh + +Authenticates using the app token from `freebox_app_token.txt` and sends a reboot request to the Freebox over the HTTPS Freebox OS API. +Requires the Freebox ECC Root CA certificate (`freebox_ecc_root_ca.pem`) to be in the same directory as the script. +By default the token should be for the `APP_ID` `fr.bash.reboot`. +curl, openssl and jq are needed. +By default an app is not allowed to reboot the freebox, to allow it, go in "paramètre de la freebox -> gestion des accès -> application", then modify the permissions for your app and check "modification des réglages de la Freebox". + ## Other random scripts ### belter_tension_calculator.sh diff --git a/freebox/freebox_ecc_root_ca.pem b/freebox/freebox_ecc_root_ca.pem new file mode 100644 index 0000000..bc1a5ac --- /dev/null +++ b/freebox/freebox_ecc_root_ca.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIJAMaRcLnIgyukMAoGCCqGSM49BAMCMGExCzAJBgNVBAYT +AkZSMQ8wDQYDVQQIDAZGcmFuY2UxDjAMBgNVBAcMBVBhcmlzMRMwEQYDVQQKDApG +cmVlYm94IFNBMRwwGgYDVQQDDBNGcmVlYm94IEVDQyBSb290IENBMB4XDTE1MDkw +MTE4MDIwN1oXDTM1MDgyNzE4MDIwN1owYTELMAkGA1UEBhMCRlIxDzANBgNVBAgM +BkZyYW5jZTEOMAwGA1UEBwwFUGFyaXMxEzARBgNVBAoMCkZyZWVib3ggU0ExHDAa +BgNVBAMME0ZyZWVib3ggRUNDIFJvb3QgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AASCjD6ZKn5ko6cU5Vxh8GA1KqRi6p2GQzndxHtuUmwY8RvBbhZ0GIL7bQ4f08ae +JOv0ycWjEW0fyOnAw6AYdsN6y1eNvH2DVfoXQyGoCSvXQNAUxla+sJuLGICRYiZz +mnijYzBhMB0GA1UdDgQWBBTIB3c2GlbV6EIh2ErEMJvFxMz/QTAfBgNVHSMEGDAW +gBTIB3c2GlbV6EIh2ErEMJvFxMz/QTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBhjAKBggqhkjOPQQDAgNoADBlAjA8tzEMRVX8vrFuOGDhvZr7OSJjbBr8 +gl2I70LeVNGEXZsAThUkqj5Rg9bV8xw3aSMCMQCDjB5CgsLH8EdZmiksdBRRKM2r +vxo6c0dSSNrr7dDN+m2/dRvgoIpGL2GauOGqDFY= +-----END CERTIFICATE----- diff --git a/freebox/freebox_reboot.sh b/freebox/freebox_reboot.sh new file mode 100755 index 0000000..167d97e --- /dev/null +++ b/freebox/freebox_reboot.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +set -euo pipefail + +FREEBOX_HOST="${FREEBOX_HOST:-mafreebox.freebox.fr}" +API_BASE="${API_BASE:-https://${FREEBOX_HOST}/api/v4}" +APP_ID="${APP_ID:-fr.bash.reboot}" +TOKEN_FILE="${TOKEN_FILE:-./freebox_app_token.txt}" +CA_FILE="${CA_FILE:-$(dirname "$0")/freebox_ecc_root_ca.pem}" + +for cmd in curl jq openssl; do + command -v "$cmd" >/dev/null 2>&1 || { echo "Missing dependency: $cmd" >&2; exit 1; } +done + +if [[ ! -f "$TOKEN_FILE" ]]; then + echo "Missing token file: $TOKEN_FILE" >&2 + exit 1 +fi + +if [[ ! -f "$CA_FILE" ]]; then + echo "Missing CA file: $CA_FILE" >&2 + exit 1 +fi + +APP_TOKEN="$(tr -d '\r\n' < "$TOKEN_FILE")" +if [[ -z "$APP_TOKEN" ]]; then + echo "Empty app token in $TOKEN_FILE" >&2 + exit 1 +fi + +echo "Fetching challenge..." +login_response="$(curl --cacert "$CA_FILE" -fsS "${API_BASE}/login/")" +challenge="$(jq -r '.result.challenge // empty' <<<"$login_response")" + +if [[ -z "$challenge" ]]; then + echo "Could not get challenge:" >&2 + echo "$login_response" >&2 + exit 1 +fi + +echo "Computing password..." +password="$({ printf '%s' "$challenge" | openssl dgst -sha1 -hmac "$APP_TOKEN"; } | awk '{print $2}')" + +echo "Opening session..." +session_response="$({ + curl --cacert "$CA_FILE" -fsS -X POST "${API_BASE}/login/session/" \ + -H 'Content-Type: application/json' \ + -d "{\"app_id\":\"${APP_ID}\",\"password\":\"${password}\"}" +})" + +session_token="$(jq -r '.result.session_token // empty' <<<"$session_response")" + +if [[ -z "$session_token" ]]; then + echo "Authentication failed:" >&2 + echo "$session_response" >&2 + exit 1 +fi + +echo "Rebooting..." +reboot_response="$({ + curl --cacert "$CA_FILE" -fsS -X POST "${API_BASE}/system/reboot/" \ + -H "X-Fbx-App-Auth: ${session_token}" \ + -H 'Content-Type: application/json' \ + -d '{}' +})" + +if jq -e '.success == true' >/dev/null 2>&1 <<<"$reboot_response"; then + echo "Reboot requested." +else + echo "Reboot failed:" >&2 + echo "$reboot_response" >&2 + exit 1 +fi diff --git a/freebox/get_token.sh b/freebox/get_token.sh new file mode 100755 index 0000000..a395f1c --- /dev/null +++ b/freebox/get_token.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +set -euo pipefail + +FREEBOX_HOST="${FREEBOX_HOST:-mafreebox.freebox.fr}" +API_BASE="${API_BASE:-http://${FREEBOX_HOST}/api/v8}" + +APP_ID="${APP_ID:-fr.bash.reboot}" +APP_NAME="${APP_NAME:-RebootScript}" +APP_VERSION="${APP_VERSION:-1.0}" +DEVICE_NAME="${DEVICE_NAME:-bash-script}" +TOKEN_FILE="${TOKEN_FILE:-./freebox_app_token.txt}" + +command -v curl >/dev/null || { echo "curl is required" >&2; exit 1; } +command -v jq >/dev/null || { echo "jq is required" >&2; exit 1; } + +echo "Requesting app authorization..." +auth_response="$( + curl -fsS -X POST "${API_BASE}/login/authorize/" \ + -H "Content-Type: application/json" \ + -d "{ + \"app_id\":\"${APP_ID}\", + \"app_name\":\"${APP_NAME}\", + \"app_version\":\"${APP_VERSION}\", + \"device_name\":\"${DEVICE_NAME}\" + }" +)" + +app_token="$(jq -r '.result.app_token // empty' <<<"$auth_response")" +track_id="$(jq -r '.result.track_id // empty' <<<"$auth_response")" + +if [[ -z "$app_token" || -z "$track_id" ]]; then + echo "Authorization request failed:" + echo "$auth_response" + exit 1 +fi + +echo "Approve the request on the Freebox now." +echo "track_id: $track_id" + +while true; do + status_response="$(curl -fsS "${API_BASE}/login/authorize/${track_id}")" + status="$(jq -r '.result.status // empty' <<<"$status_response")" + + case "$status" in + granted) + echo "$app_token" > "$TOKEN_FILE" + chmod 600 "$TOKEN_FILE" 2>/dev/null || true + echo "App token saved to: $TOKEN_FILE" + echo "APP_ID=$APP_ID" + echo "APP_TOKEN=$app_token" + exit 0 + ;; + denied) + echo "Authorization denied." + exit 1 + ;; + *) + sleep 2 + ;; + esac +done