Skip to content

Instantly share code, notes, and snippets.

@wikrie
Last active March 2, 2024 10:48
  • Star 84 You must be signed in to star a gist
  • Fork 23 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save wikrie/f1d5747a714e0a34d0582981f7cb4cfb to your computer and use it in GitHub Desktop.
Fritzbox Fritz!Box AVM SSL Letsencrypt automatically update
#!/bin/bash
## this little Gist is for Copy the Letsencrypt Cert from an Linux machine (e.g. Raspberry PI or Synology NAS)
## to the router (Fritzbox).
## It is usefull to be able to speak to the Router over DDNS without any Cert issue in the Browser.
## thanks to https://gist.github.com/mahowi for the perfect Idea
## put it in /etc/letsencrypt/renewal-hooks/post so it gets run after every renewal.
## since Fritz OS 7.25 it is needed to select a Username, from a security point of view
## it is always a good idea to have a non default user name. And as normaly a Fritz Box
## is connected to the Internet, the prefered method should be WITH Username.
# parameters
USERNAME="needed since Fritz OS 7.25"
PASSWORD="fritzbox-password"
CERTPATH="path to cert eg /etc/letsencrypt/live/domain.tld/"
CERTPASSWORD="cert password if needed"
HOST=http://fritz.box
# make and secure a temporary file
TMP="$(mktemp -t XXXXXX)"
chmod 600 $TMP
# login to the box and get a valid SID
CHALLENGE=`wget -q -O - $HOST/login_sid.lua | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//'`
HASH="`echo -n $CHALLENGE-$PASSWORD | iconv -f ASCII -t UTF16LE |md5sum|awk '{print $1}'`"
SID=`wget -q -O - "$HOST/login_sid.lua?sid=0000000000000000&username=$USERNAME&response=$CHALLENGE-$HASH"| sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//'`
# generate our upload request
BOUNDARY="---------------------------"`date +%Y%m%d%H%M%S`
printf -- "--$BOUNDARY\r\n" >> $TMP
printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n$SID\r\n" >> $TMP
printf -- "--$BOUNDARY\r\n" >> $TMP
printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n$CERTPASSWORD\r\n" >> $TMP
printf -- "--$BOUNDARY\r\n" >> $TMP
printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n" >> $TMP
printf "Content-Type: application/octet-stream\r\n\r\n" >> $TMP
cat $CERTPATH/privkey.pem >> $TMP
cat $CERTPATH/fullchain.pem >> $TMP
printf "\r\n" >> $TMP
printf -- "--$BOUNDARY--" >> $TMP
# upload the certificate to the box
wget -q -O - $HOST/cgi-bin/firmwarecfg --header="Content-type: multipart/form-data boundary=$BOUNDARY" --post-file $TMP | grep SSL
# clean up
rm -f $TMP
@iHaveAstream
Copy link

Amazing script! I run it of my Synology Diskstation NAS.
Just had to change iconv to uconv in line 16 as the Synology Linux flavour doesn't support it out of the box.

Hi,
I also wanna put it onto my DS but am not sure where to put it as the folder /etc/letsencrypt/renewal-hooks/post is not existing (currently).

Thanks for a hint.

@rozwad
Copy link

rozwad commented Jun 15, 2020

I also wanna put it onto my DS but am not sure where to put it as the folder /etc/letsencrypt/renewal-hooks/post is not existing (currently).

On my DS I am calling the script from Task Scheduler on weekly basis, same as the Synology's Let's Encrypt renew script (see /usr/syno/etc/synocron.d/syno-letsencrypt.conf)

@wikrie
Copy link
Author

wikrie commented Jul 6, 2020

I do a small Update to use it with Synology Diskstation directly check out
Synology Fritzbox Cert Transfer

@franzs
Copy link

franzs commented Jul 21, 2020

Thanks for your work.
I changed it a bit to

  • use curl
  • provide parameters via environment or command line
  • better error processing

The result can be found at

https://github.com/franzs/fritzbox_upload_certificate

@iHaveAstream
Copy link

I do a small Update to use it with Synology Diskstation directly check out
Synology Fritzbox Cert Transfer

Awesome! Many thanks for this - works like a charm!

@ant0nwax
Copy link

Hi All

i also use this script for a fritzbox 7390, soon i will have a 6590, 7490 or 7590, i don't know which i find used for good price

my question:

the myfritz app on android tries to connect to fritzbox with private IP from private network, i cannot type a DNS name there, even if i have one internal for the fritzbox... the app is kind of no DNS mode only and can only connect to an IP.
So in my LetsEncrypt can i somehow add the private IP of my fritzbox (192.168.1.1) or is that not possible
I was reading and did not find the exact issue online, i think about SAN ALTERNATE NAME or something...

Thanks for ideas...

@benniju
Copy link

benniju commented Apr 3, 2021

Hi All

i also use this script for a fritzbox 7390, soon i will have a 6590, 7490 or 7590, i don't know which i find used for good price

my question:

the myfritz app on android tries to connect to fritzbox with private IP from private network, i cannot type a DNS name there, even if i have one internal for the fritzbox... the app is kind of no DNS mode only and can only connect to an IP.
So in my LetsEncrypt can i somehow add the private IP of my fritzbox (192.168.1.1) or is that not possible
I was reading and did not find the exact issue online, i think about SAN ALTERNATE NAME or something...

Thanks for ideas...

Hi @ant0nwax,
No, first of all, Lets Encrypt does not offer Certificates for IP-Adresses (but is is theoretically posible eg. 1.1.1.1) and second a SSL verfification does not make sense for internal IP-Adresses. You could however probably generate a self signed Certificate and add that to the Android Certificate Storage.

PS: I would not recommend to use a Cable Fritzbox if you don't need the Cable. (Modem takes Power etc...)

@rikroe
Copy link

rikroe commented Apr 3, 2021

Hi @wikrie,

just found out that the script doesn't work for my 7590 with FRITZ!OS 07.25 - I am getting blocked when getting the SID in L19.

The reason seems to be that with 07.25, one can choose between a named user and a login without a user. However, logging in without a user still requires a user name.

This username can be extracted when retrieving the CHALLENGE (L17), as on my box the response looks like this:

<?xml version="1.0" encoding="utf-8"?>
<SessionInfo>
    <SID>0000000000000000</SID>
    <Challenge>2127f7cc</Challenge>
    <BlockTime>0</BlockTime>
    <Rights></Rights>
    <Users>
        <User last="1">fritz1416</User>
        <User>my_actual_user</User>
    </Users>
</SessionInfo>

When using fritz1416 as USERNAME, I can login again and update the certificate.

On an 7490 with 07.21 the whole <Users>...</Users> block is not there.

Unfortunately I'm no shell expert so I don't really know if this is something that would be possible to solve automatically.

EDIT: I should try the stuff out. Using fritz1416 as a fixed USERNAME seems to be working on the 7490 with 07.21 as well.

@ychromosome
Copy link

  1. you may want to change your default username of your fritzbox now, because of security reasons.
  2. indeed AVM changed the login to demand a username in certain cases.
  3. why don't you just use the username for now and everything is fine.

@rikroe
Copy link

rikroe commented Apr 5, 2021

Why should I change the default username? It is not allowed for logging in from the internet and when in the LAN it isn't required anyway.

I'll use the default username for now, but maybe could you add this into the gist?

@HanSolo72
Copy link

I have the same problem since I updated my 6490 to 7.25 and my 7490 to 7.26. How the script must be changed to work properly?

@wikrie
Copy link
Author

wikrie commented May 10, 2021 via email

@wikrie
Copy link
Author

wikrie commented May 10, 2021 via email

@JeroenTuinstra
Copy link

Just a question. Could some tell me where I can find /etc/letsencrypt/renewal-hooks/post on the Synology DMS 7.1?
I can't seem to find that folder at all.
If DSM 7.1 doesn't have this folder, how would I be able to automate copying of the key to FritzBox? What trigger would I use or where would I place this script?

@ant0nwax
Copy link

ant0nwax commented Dec 16, 2022 via email

@TGion
Copy link

TGion commented Dec 21, 2022

Hey mate,
thanks for your fine script. It works flawless with:

  • FRITZ!OS 7.39-101552 BETA on a FRITZ!Box 7530
  • FreeBSD 13.1-RELEASE-p1

I have two suggestions though, mabye its valuable for others:

  1. Your script runs fine with the standard shell /bin/sh. You might want to consider making that one the default, instead of bash. No need to add unnecessary dependencies in my opinion.
  2. This one was kinda tricky: Your script ran successfully as user. But when I called it with doas (similiar to sudo), iconv always returned unsupported encoding of UTF16LE.
    Turned out I have two different versions from iconv installed:
    /usr/bin/iconv (system)
    /usr/local/bin/iconv (installed from libiconv-1.17)

As user /usr/bin/iconv (the correct one) was used. With doas, /usr/local/bin/iconv was used and failed with unsupported encoding.
I have added the the following line to your script (line 18) to run iconv from the correct path:

export PATH=/usr/bin/:$PATH

Thanks again for your efforts. Great work!

@Eldiabolo21
Copy link

Hey there,
thanks so much for the script, it's serving me really well! It works already for multiple Fritzboxes.
However, I have an issue with one single Fritzbox, where I just can't get it to work. Fritzbox 7530, FritzOS 7.29. It did work at some point, I had to replace the server from which the cert was uploaded and now it's playing dump with me...

What i tried so far:

  • copied script from the source here on GH
  • verified username and password. I can login to the webinterface no problem.
  • removed -q from the wget commands to be able to better see whats happening.

In the last step it says: Das eingegebene Kennwort ist nicht gültig. Bitte geben Sie das richtige Kennwort ein.
Which I guarantee, is the right one.
Do you or anyone else have suggestions what it could be or how to better debugg this?

Cheers!

@wikrie
Copy link
Author

wikrie commented Jan 27, 2023

Hi Eldiabolo21,

das sieht doch wieder schwer nach unterschiedliche Netze aus, sind die 2 Geräte Fritzbox und das Gerät welches das Script ausführt auch im selben physikalischen Netzwerk? Wir hatten hier schon den Fall das einer das remote versucht hat. Wenn es das nicht ist dann könnte ich mir noch vorstellen das das Script Probleme hat bei Sonderzeichen im Passwort, das habe ich selbst noch nie probiert.

so meld dich mal wegen den 2 Sachen...

mfg Chris aka wikrie

@Eldiabolo21
Copy link

Hey, danke fuer die schnelle Antwort.

Server der das Script ausfuehrt und Fritzbox sind beide in 192.168.8.0/24. Passwort fuer den dedizierten cert-user der Fritzbox besteht das Passwort nur Zahlen, Gross- und Kleinbuchstaben. Daran kanns auch nicht liegen.

Sonst noch ne Idee?

Gruss!

@TGion
Copy link

TGion commented Jan 27, 2023

Hey Eldiabolo21,

ich hab auch die 7530 (zwar neueres OS) und das Skript funktioniert aktuell (auch aus verschiedenen Netzen).
Wie sieht es denn mit den Berechtigungen des cert-user's aus?
Ich sehe hier bei mir in der Box, dass Benutzern auch die Berechtigung für das Lesen und Ändern von Einstellungen gegeben werden kann (bei nur einem User kann dies nicht deaktiviert werden -> Admin).
Prüfe das mal bitte.
Dann könntest du nochmal Testweise einen neuen Benutzer / Passwort anlegen bzw. vergeben.
Vielleicht auch nochmal das Skript prüfen ob du dort aus Versehen (zu viel) Änderungen vorgenommen hast.

@Eldiabolo21
Copy link

Eldiabolo21 commented Jan 27, 2023

Hey, auch vielen Dank auch fuer deine Antwort, leider keine Erfolg.
Berechtigung zum Einstellungen aendern waren gesetzt. Neuen Nutzer angelegt, gleiches Problem. Passwort fuer den aktuellen Cert-user habe ich auch schon x-mal geaendert.

Was ich aber rausgefunden habe, dass wenn ich im Scipt wirklich das falsche Passwort angebe, kommt noch eine ganz andere Meldung vom Script...

Habe zum Testen mal die Einstellung "Zugang aus dem Internet erlaubt" eingeschaltet, aber auch keine Besserung.

Noch mehr Ideen?

Edit:
Ich werde bekloppt.... Ich habe beide Sites mit Wireguard verbunden. Also einmal 192.168.8.0/24 was Probleme macht und 192.168.16.0/24, wo es problemlos geht. Beide Sites koennen einander erreichen.

Ich habe gerade das Script einfach auf den Host in 192.168.16.0/24 kopiert, so wie es auf dem anderen host war, und es geht o.OOOOO
Das laesst mich irgendwie zu dem Schluss kommen, dass eins der Pakete borken ist... Allerdings sind beide Hosts Ubuntu 20.04.5... Ich vergleiche mal Paketversionen...

Edit 2:
the mystery has been solved!!! It was the f****ng certificate. I created a cert with a docker compose command from here: https://stackoverflow.com/a/66638930

Unfortunately that did something super weird with the created certs (I assume it's the --rsa-key-size 4096) and they can't be imported into the Fritzbox. I actually reached that conclusion when I compared the TMP-files the script creates. The private key on the non working machine was much shorter. I don't know what exactly went wrong, it still seemed like a valid cert, but didn't work for the Fritzbox. I ran certbox directly on the host, w/o any further key size args and imported the cert with success.... URGH, glad this is over...

@ant0nwax
Copy link

ant0nwax commented Jan 28, 2023 via email

@wikrie
Copy link
Author

wikrie commented Jan 28, 2023 via email

@TGion
Copy link

TGion commented Jan 28, 2023

Hey, auch vielen Dank auch fuer deine Antwort, leider keine Erfolg. Berechtigung zum Einstellungen aendern waren gesetzt. Neuen Nutzer angelegt, gleiches Problem. Passwort fuer den aktuellen Cert-user habe ich auch schon x-mal geaendert.

Was ich aber rausgefunden habe, dass wenn ich im Scipt wirklich das falsche Passwort angebe, kommt noch eine ganz andere Meldung vom Script...

Habe zum Testen mal die Einstellung "Zugang aus dem Internet erlaubt" eingeschaltet, aber auch keine Besserung.

Noch mehr Ideen?

Edit: Ich werde bekloppt.... Ich habe beide Sites mit Wireguard verbunden. Also einmal 192.168.8.0/24 was Probleme macht und 192.168.16.0/24, wo es problemlos geht. Beide Sites koennen einander erreichen.

Ich habe gerade das Script einfach auf den Host in 192.168.16.0/24 kopiert, so wie es auf dem anderen host war, und es geht o.OOOOO Das laesst mich irgendwie zu dem Schluss kommen, dass eins der Pakete borken ist... Allerdings sind beide Hosts Ubuntu 20.04.5... Ich vergleiche mal Paketversionen...

Edit 2: the mystery has been solved!!! It was the f****ng certificate. I created a cert with a docker compose command from here: https://stackoverflow.com/a/66638930

Unfortunately that did something super weird with the created certs (I assume it's the --rsa-key-size 4096) and they can't be imported into the Fritzbox. I actually reached that conclusion when I compared the TMP-files the script creates. The private key on the non working machine was much shorter. I don't know what exactly went wrong, it still seemed like a valid cert, but didn't work for the Fritzbox. I ran certbox directly on the host, w/o any further key size args and imported the cert with success.... URGH, glad this is over...

Great you managed to make it work.
I use acme.sh to automatically issue / renew a wildcard certificate (single domain certificates are even easier to setup) and automatically install them from my VPS onto my fritzbox via wireguard. If you are interested you can check it out here: https://gion.io/blog/automatic-renewal-of-wildcard-certificates/

Anyhow, I am glad you found the error and made it work.
Have a great weekend!

@ant0nwax
Copy link

This sentence comes to my mind:
Security breaking down our nerves (2023)

@doits
Copy link

doits commented Feb 18, 2023

I had the same problem and it was because the certificate's private key was an ecdsa key.

Changing key_type to rsa in /etc/letsencrypt/renewal/xxx.conf and renewing the certificate (certbot renew --force-renewal) generated a certificate with a rsa private key which could be imported again.

@mbo77
Copy link

mbo77 commented Sep 17, 2023

Since recently, I was using this script to update the certificate on both, a Fritz!Box and a Fritz!Repeater 1200 AX.
The certificate option is not exposed in the UI on repeaters, but worked via this script just fine.

The repeater is now on 7.57 and it stopped working.
The returning page indicates a problem:

`

Das Update ist fehlgeschlagen:

Invalid variable name.

`

Anyone experiencing the same problem?

@franzs
Copy link

franzs commented Sep 17, 2023

@mbo77 maybe you want to give fritzbox_upload_certificate a try. it was recently updated for 7.57.

@mbo77
Copy link

mbo77 commented Sep 17, 2023

@franzs

Looks nice and clean, but I suspect the problem is on the repeater side.
POSTing to the URL still gives the same message on the returing page:

Das Update ist fehlgeschlagen:

Invalid variable name.

Works fine with 7530AX on 7.57.

@mbo77
Copy link

mbo77 commented Sep 17, 2023

I suspect the repeater triggers the wrong function internally:

FRITZ!Repeater Update

Das Update ist fehlgeschlagen:

Invalid variable name.

Wiederholen Sie das Update oder starten Sie den FRITZ!Repeater neu.

This looks to me some message related to updating the firmware itself.
As the server-side binary is called "firmwarecfg", I suspect it was initially deployed for updating the firmware and later was extended to handle certificates as well. So maybe they messed it up internally on the repeater.
I will open a support case with AVM.

@franzs
Copy link

franzs commented Sep 17, 2023

@mbo77 unfortunately i don't have access to a Fritz!Repeater 1200 AX. i ran the fritzbox_upload_certificate sucessfully with a FRITZ!WLAN Repeater DVB-C. it's not possible to upload a certificate via the ui but the /cgi-bin/firmwarecfg url works well with at least 7.01.

let us know what AVM says. 😄

@mbo77
Copy link

mbo77 commented Sep 19, 2023

No luck..

Die Funktion für den Import der Zertifikate war für unsere Repeater nie geplant und über die Benutzeroberfläche auch nicht möglich.
Durch aktuelle FRITZ!OS-Änderungen im Unterbau ist es nun bei Repeatern auch komplett entfernt worden.

Gerne gebe ich Ihr Anliegen aber als Verbesserungsvorschlag an unsere Produktentwicklung weiter.

@franzs
Copy link

franzs commented Sep 19, 2023

@mbo77 What a pity. But thanks for sharing the answer. At least I'll list the Fritz!Repeater 1200 AX with 7.57 as unsupported. Unfortunately there is no easy way to get the product name and the OS version from a fritzbox to check it.

@xraver
Copy link

xraver commented Dec 28, 2023

Hello,
I want to share my experience.
The script worked well. At a certain point it looks not working properly: no error prompted and no certificate imported.
After troubleshooting it I've discovered the issue was related to letsencrypt that changed from rsa to ecdsa. I replaced in the letsencrypt configuration file as below
#key_type = ecdsa
key_type = rsa
And now is working well back.
hope my experience will help you.

(tested with fritz7590 and 7530ax both version 7.57)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment