blog_content/network/dyndns_api.rst

192 lines
6.6 KiB
ReStructuredText
Raw Normal View History

2021-11-01 23:45:02 +01:00
DNS dynamique avec l'api de Scaleway et de python
#################################################
2021-10-27 22:04:23 +02:00
:date: 2021-10-27 21:00
2021-11-01 23:45:02 +01:00
:modified: 2021-11-01 23:30
2021-10-28 17:31:30 +02:00
:tags: DNS, API, python
2021-10-27 22:04:23 +02:00
:category: network
:slug: dyndns_online
:authors: Milka64
2021-11-01 23:32:51 +01:00
:summary: Comment faire du DNS dynamique avec Scaleway et python
2021-10-27 22:04:23 +02:00
:status: draft
2021-10-28 21:08:53 +02:00
2021-10-28 17:29:00 +02:00
Cette année j'ai été obligé de changer de FAI, je suis passé chez Orange et j'ai (re)découvert les joies d'une ip dynamique...
2021-10-27 22:04:23 +02:00
2021-11-01 23:45:02 +01:00
Étant auto-hebergé, il m'a fallu trouver une solution.
2021-10-28 17:29:00 +02:00
Solutions
---------
2021-11-01 23:45:02 +01:00
J'ai bien commencé à chercher des solutions de dynDNS mais je me suis vite rendu compte que ça ne me convenait pas (panne à répétition, temps de MAJ, etc ...).
2021-10-28 17:29:00 +02:00
2021-11-01 23:45:02 +01:00
Ensuite, je me suis dit que j'allais me monter un bind auto-hebergé, mais par manque de temps, j'ai mis cette solution de côté.
2021-10-28 17:29:00 +02:00
2021-11-01 23:45:02 +01:00
Et par le plus grand des hasards en mettant à jour une entrée DNS, je me suis rendu compte que Scaleway avait une API pour gérer ses services.
2021-10-28 17:29:00 +02:00
Dyndns.py
---------
2021-11-01 23:45:02 +01:00
J'ai donc écrit `ce petit script <https://gitlab.com/Milka64/dyndns_online.net>`_ (qui est utilisable par tous) dont je vais détailler les différentes parties.
2021-10-28 17:29:00 +02:00
2021-11-01 23:32:51 +01:00
Il n'y a besoin que du token de l'api (disponible `ici <https://console.online.net/fr/api/access>`_)
Fonction args
2021-10-28 17:29:00 +02:00
~~~~~~~~~~~~~
2021-11-01 23:32:51 +01:00
Elle parse les argument du scripts, et permets d'avoir un `--help`.
2021-10-28 17:29:00 +02:00
2021-10-30 17:50:04 +02:00
.. code-block:: python
2021-10-28 21:08:53 +02:00
2021-10-30 17:55:42 +02:00
def get_args():
"""
parse agrs
"""
parser = argparse.ArgumentParser(description="Update dns zone with online.net API")
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("-c", "--clean", help="clean old unused zones", action="store_true")
parser.add_argument("-t", "--token", help="token's API (https://console.online.net/fr/api/access)")
parser.add_argument("-u", "--url", help="url to get public ip, default=http://ifconfig.me", default="http://ifconfig.me")
parser.add_argument("-r", "--records", help="records to update, comma separated list")
parser.add_argument("domain", help="domain to update")
return parser.parse_args()
2021-10-28 21:08:53 +02:00
2021-11-01 23:32:51 +01:00
Fonction clear
2021-10-28 17:29:00 +02:00
~~~~~~~~~~~~~~
2021-11-01 23:32:51 +01:00
Efface les versions innutilisé:
.. code-block:: python
2021-10-28 17:29:00 +02:00
2021-11-01 23:32:51 +01:00
def clear_versions(args, session):
"""
clear inactive versions
"""
all_versions = session.get("https://api.online.net/api/v1/domain/" + args.domain + "/version").json()
versions_to_remove = [x for x in session.get("https://api.online.net/api/v1/domain/" + args.domain + "/version").json() if not x['active']]
for version in versions_to_remove:
if args.verbose:
print("Deleting : " + version['name'])
api_session.delete("https://api.online.net/api/v1/domain/" + args.domain + "/version/" + version["uuid_ref"])
Fonction update
2021-10-28 17:29:00 +02:00
~~~~~~~~~~~~~~~
Crée une nouvelle zone, la peuple (avec les entrées de la version active) et l'active.
2021-11-01 23:32:51 +01:00
.. code-block:: python
def create_new_version(args, records, records_to_update):
"""
Create new zone version with name YYYYMMDDhhmm
"""
new_name = datetime.now().strftime("%Y%m%d%H%M")
if args.verbose:
print("Generating new zone called : " + new_name)
url = "https://api.online.net/api/v1/domain/" + args.domain + "/version"
data = {
'name' : new_name
}
res = api_session.post(url, data)
if "error" in res.json():
exit(res.json()['error_description'])
id_to_update = res.json()["uuid_ref"]
url = "https://api.online.net/api/v1/domain/" + args.domain + "/version/" + id_to_update + "/zone"
for record in records:
if record['name'] in records_to_update:
record['data'] = public_ip
data = {
'name' : record['name'],
'type' : record['type'],
'ttl' : record['ttl'],
'data' : record['data'],
'priority' : 0,
}
res = api_session.post(url, data)
if "error" in res.json():
exit(res['error_description'])
url = "https://api.online.net/api/v1/domain/" + args.domain + "/version/" + id_to_update + "/enable"
res = api_session.patch(url)
try:
res_json = res.json()
except:
res_json = {}
if "error" in res_json:
exit(res['error_description'])
Main
2021-10-28 17:29:00 +02:00
~~~~
2021-11-01 23:45:02 +01:00
Appelé lors de l'exécution du scipt
2021-10-28 17:29:00 +02:00
2021-11-01 23:32:51 +01:00
.. code-block:: python
if __name__ == "__main__":
## PARSE AGRS ##
args = get_args()
## Check if Token is present
if not args.token:
exit("token is required")
## Get public IP ##
try:
public_ip = requests.get(args.url).text
except:
exit("Can't get public IP")
if not public_ip:
exit("Can't get public IP")
if args.verbose:
print("Public IP is : " + public_ip)
## Init api session ##
api_session = requests.session()
api_session.headers['Authorization'] = 'Bearer ' + args.token
## Check api connection ##
try:
response = api_session.get("https://api.online.net/api/v1/domain/list")
except:
exit("Can't connect to api")
if "error" in response.json():
exit(response.json()['error_description'])
## Get records and compare IP ##
records = api_session.get("https://api.online.net/api/v1/domain/" + args.domain + "/zone").json()
if "error" in records:
exit(records['error_description'])
records_to_update = args.records.split(',')
old_ip = [x['data'] for x in records if x['name'] in records_to_update and not public_ip in x["data"]]
if not old_ip:
exit()
else:
create_new_version(args, records, records_to_update)
## Clear inactive versions ##
if args.clean:
clear_versions(args, api_session)
2021-10-28 17:29:00 +02:00
BONUS : acme.sh + api online
----------------------------
2021-11-01 23:32:51 +01:00
En me documentant sur l'api d'online, je me suis rendu compte qu'acme.sh permet d'utiliser l'api de Scaleway.
2021-11-01 23:45:02 +01:00
Jusqu'à présent je mettais à jour mes certificats à la main tous les trois mois (avec quelques raté à l'occasion...).
2021-11-01 23:32:51 +01:00
2021-11-01 23:45:02 +01:00
Il y a juste besoin du token.
2021-10-27 22:04:23 +02:00
2021-11-01 23:32:51 +01:00
.. code-block:: console
2021-10-27 22:04:23 +02:00
2021-11-01 23:32:51 +01:00
$ acme.sh --issue --dns dns_online -d 0w.tf -d "*.0w.tf" --reloadcmd "nginx -s reload" --server letsencrypt --force