Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
@heikoh81
Copy link

heikoh81 commented Aug 11, 2017

Thanks for your script, works like charm!

One question: Is there any chance to fork this script to activate/deactivate call forwards?
Or does it only work for submitting the upload form?

Unfortunately, I don't have advanced programming skills, but I think your script already does the most complicated part by getting the SID.
On my German Fritzbox 7580 running firmware 6.83, it is found at: Telefonie -> Rufbehandlung -> Rufumleitung
There I have 1 call forward, and I'd like to switch on/off the checkbox "Aktiv".
If on/off is not possible, then at least I'd like to toggle this checkbox.

Thanks for your answer,
Heiko (Germany)

@wikrie
Copy link
Author

wikrie commented Sep 12, 2017

Hi Heikh81,

your right this script is only for uploading a new Cert to Fritzbox. But I duckduck your request and here is what I found:

http://guido.vonrudorff.de/fritzbox-rufumleitung-kommandozeile/

the most important fact is
fritz.box/cgi-bin/webcm
this where we should send the request to and we can activate and/or deactivate it with
telcfg%3Asettings%2FCallerIDActions0%2FActive=STATUS&sid=SID

you now how to transfer this to my wget command and it should work.

@palto42
Copy link

palto42 commented Jul 25, 2018

Thanks for this script, I added it as a renew-hook to my let's encrypt cron job on my local server so that it get's automatically updated.
/usr/local/bin/certbot-auto renew --renew-hook /path-to/fritz_lets_encrypt.sh

@rikroe
Copy link

rikroe commented Oct 2, 2018

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.

@FvdLaar
Copy link

FvdLaar commented Nov 8, 2018

Working like charm! But ....
... only when I run the script using internal IP-addresses.

It didn't work using https://example.com as HOST although this URL does let me login to my Fritz!BOX.

Why? Well, because the certificate wasn't valid any more !!!!

TIP: make sure you run this script using HTTP:// to connect to your Fritz!BOX or make sure your run the script before your certificate becomes invalid.

The response for succesfull processing is : "Import of the SSL certificate was successful."
Non succesfull processing doesn't give you any response.

@anoxi
Copy link

anoxi commented Mar 6, 2019

Thanks, im using this with certbot (0.28) as post hook:
certbot renew --post-hook "fritzbox-cert-update.sh"

@ychromosome
Copy link

ychromosome commented Jun 6, 2019

@FvdLaar
do you really want to send your private cert through an unencrypted connection?
Please DON'T listen to that "TIP" and upload the certificate manually for the first time.

@wikrie
Copy link
Author

wikrie commented Jun 6, 2019

Working like charm! But ....
... only when I run the script using internal IP-addresses.

It didn't work using https://example.com as HOST although this URL does let me login to my Fritz!BOX.

Why? Well, because the certificate wasn't valid any more !!!!

TIP: make sure you run this script using HTTP:// to connect to your Fritz!BOX or make sure your run the script before your certificate becomes invalid.

The response for succesfull processing is : "Import of the SSL certificate was successful."
Non succesfull processing doesn't give you any response.

Thats not the design, the script should only be used in 1 network.
Its not designed for transmittion over the world wide web.

mfg
wikrie

@FvdLaar
Copy link

FvdLaar commented Jun 8, 2019

do you really want to send your private cert through an unencrypted connection?
Please DON'T listen to that "TIP" and upload the certificate manually for the first time.

I full agree with @ychromosome : DO NOT send private keys or certificates over unsecured connection except is it is in your own DMZ / private (home) network

@FvdLaar
Copy link

FvdLaar commented Jun 8, 2019

That's not the design, the script should only be used in 1 network.
Its not designed for transmittion over the world wide web.

I fully agree with @wikrie

@mahowi
Copy link

mahowi commented Mar 3, 2020

Thank you for this script. 👍
I have put it in /etc/letsencrypt/renewal-hooks/post so it gets run after every renewal.

@wikrie
Copy link
Author

wikrie commented Mar 3, 2020

Thank you for this script. 👍
I have put it in /etc/letsencrypt/renewal-hooks/post so it gets run after every renewal.

Many thanks I insert your idea into the script that is much bettern than cronjob ...

@iHaveAstream
Copy link

iHaveAstream commented Mar 10, 2020

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

iHaveAstream commented Oct 23, 2020

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

ant0nwax commented Nov 10, 2020

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

ychromosome commented Apr 4, 2021

  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

HanSolo72 commented May 9, 2021

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

@wikrie
Copy link
Author

wikrie commented May 10, 2021

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