christoph ender's

blog

wednesday the 3rd of april, 2024

minimal inwx certbot handler

I've been trying to implement the ACME DNS-01 challenge with certbot and INWX. I'm doing this for a MFA / MobileTAN protected account, which limits the choice of API clients to the INWX PHP client.

This is a prototype. Don't use in production. It's in a “works-for-me” state, but there's no error checking and not even any thourough testing involved.

Installation of this PHP API client is best done via composer:

apt-get install composer
composer require inwx/domrobot

Install certbot and certbot-external-auth plugin – no idea if “future” is required everywhere, however I got an error complaining about a missing “past” module without it.

apt install certbot
pip3 install future
pip3 install certbot-external-auth

Minimum handler for Let's Encrypt/INWX, to be placed above Composer's vendor directory:

certbot-inwx-minimum-handler.php
#!/usr/bin/php
<?php

error_reporting(E_ALL);
require 'vendor/autoload.php';

function login() {
  $username = 'my-username';
  $password = 'my-password';
  $token = 'my-token';
  $domrobot = new \INWX\Domrobot();
  $result = $domrobot->setLanguage('en')
    ->useJson() ->useLive() ->setDebug(true)
    ->login($username, $password, $token);
  return $domrobot;
}

function getLeftAndRightSide($domain) {
  $domainParts = explode('.', $domain);
  $domainPartCount = count($domainParts);
  $leftSide = "";
  $i=0;
  while ($i < $domainPartCount - 2) {
    if (strlen($leftSide) > 0) { $leftSide .= "."; }
    $leftSide .= $domainParts[$i];
    $i++;
  }
  $rightSide
    = $domainParts[$domainPartCount - 2] . "."
    . $domainParts[$domainPartCount - 1];
  return [ 'leftSide' => $leftSide, 'rightSide' => $rightSide ];
}

if ($argv[1] == "perform") {
  $validation = getenv('validation');
  $bothSides = getLeftAndRightSide(getenv('txt_domain'));
  $domrobot = login();
  $result = $domrobot->call('nameserver', 'createRecord', [
    'domain' => $bothSides['rightSide'],
    'type' => 'TXT',
    'name' => $bothSides['leftSide'],
    'content' => $validation,
    'ttl' => 300,
    'testing' => false ]);
  $domrobot->logout();
  sleep(30);
}
elseif ($argv[1] == "cleanup") {
  $bothSides = getLeftAndRightSide(getenv('domain'));
  $domrobot = login();
  $validation = getenv('validation');
  $result = $domrobot->call('nameserver', 'info', [
    'domain' => $bothSides['rightSide'],
    'type' => 'TXT',
    'content' => $validation ]);
  $resData = $result['resData'];
  if (count($resData['record'] == 1)) {
    $id = $resData['record'][0]['id'];
    $result = $domrobot->call('nameserver', 'deleteRecord', [ 'id' => $id ]);
  }
  $domrobot->logout();
}
?>

The script above can be used in certbot's “handler mode“ like this:

run-certbot.sh
#!/bin/sh

certbot \
 certonly \
 --text \
 --keep-until-expiring
 --configurator certbot-external-auth:out \
 --preferred-challenges dns \
 --certbot-external-auth:out-public-ip-logging-ok \
 --certbot-external-auth:out-handler ${MY_DIR}/certbot-external-handler.php
 -d 'example.org' \