API requires 2FA

Hi. I just upgraded my box to 0.51, with the extremely nice addition of the TOTP support.

However, I use the API to automatically create accounts, and this API now requires my TOTP. Obviously, I want this to be fully automated (it’s in an ansible playbook), so I can’t enter my TOTP multiple times in the play. Not enforcing TOTP in the API would clearly be a security leak, but could there be a way to call it privately without TOTP, maybe on localhost only?

Thanks for your answer

If you are on machine itself, you can pass the api key located in /var/lib/mailinabox/api.key as Authorization header in which case no totp code should be required.

Hope this helps!

Edit: value for Authorization header should look like Basic ${apiKey}. Note that the apiKey changes after a restart

1 Like

Thank you very much!

Does this mean that passing username+password isn’t needed anymore with this solution?

Yes. apiKey replaces user:password. If you can read python, the case is handled here in the code.

1 Like

Just finding out that this also seems to be the need for the curl DNS api… which means we can’t use it remotely the same way it is described in the otherwise very helpful docs on the admin page for Custom DNS.

So to do Dynamic DNS from a remote machine, seems I’ll have to figure out some workaround… is there a way to generate valid apiKey per application/use/machine etc… that I could install on my local machine and send via curl to the MiaB machine?

Looking at the code that @fspoettel linked above, it seems the python code does this to run internally on initial user validation, but since the master apiKey is generated on restart there wouldn’t be a solution that works for a remote host.

If that sounds right, I can file an enhancement request/bug in GitHub

Yeah that is correct and the biggest caveat of the current solution. There is an open feature request for api keys already here. Feel free to add a +1 to it and describe your use case to help scope out the feature.

Here is what I have working for my Let’s Encrypt DNS updates via curl to my MIAB system with 2FA…

CREATE_DOMAIN="_acme-challenge.$CERTBOT_DOMAIN"

TOTP=“X-Auth-Token: $(oathtool --totp -b -d 6 <add MIAB 2FA secret key here>)”

curl -X PUT \
-d “$CERTBOT_VALIDATION” \
–user ‘<userid>:<password>’ \
-H “$TOTP” \
“https://<website>/admin/dns/custom/$CREATE_DOMAIN/TXT”

https://mailinabox.email/api-docs.html#tag/User

The moment you authenticate, you get a long lived API key which you can use for future transactions without further need of password/email

I got around to learning about the api_key. For those that don’t have much experience with HTTP (me included), here is what I hacked up to do dyn-DNS using the long-lived API key. $file follows the curl netrc file format with the TOTP secret appended to the end.

#!/bin/bash

# initialize list of API keys
declare -A APIkeylist

# get userid, password, and public name for server
# multiple names can map to the same server

for file in /etc/rc.local.d/*
do
  read -r machine server logincmd username passwordcmd password secret<"$file"
  sitename=`basename $file`

  #make sure that we have not already authenticated to target MIAB userid
  if [ -z ${APIkeylist[$username]} ]; then

    # Set 2FA authentication token.  Fix in future to not expose secret in "ps".
    TOTP="X-Auth-Token: $(oathtool --totp -b -d 6 $secret)"

    # authenticate to MIAB server.  Get long lived API key. 
    APIkeylist[$username]="$(curl -s -X GET --netrc-file $file -H "$TOTP" "https://$server/admin/me" | jq -r '.api_key')"

  fi

  # set IPv4 and IPV6 names
  curl -4 -X PUT -u "$username:${APIkeylist[$username]}" "https://$server/admin/dns/custom/$sitename/A"
  curl -6 -X PUT -u "$username:${APIkeylist[$username]}" "https://$server/admin/dns/custom/$sitename/AAAA"
done
1 Like

These are some great answers above. I’m new to using API’s to perform tasks, so I was able to resolve this most simply with the following:

curl -X GET “https://$your.box.domain/admin/me” -u “$AdminUserMail:$Password” -H “X-Auth-Token: $TOTPcode”

This will then return your API key, which can be used in place of the $Password in other API calls. For security reasons, I would not recommend using this key publicly, but from within your box, it should be fine. (use at your own risk)

1 Like