[PYTHON] Créez votre propre serveur DNS avec Twisted

introduction

Avez-vous déjà pensé "Je veux créer mon propre serveur DNS"? Il ne s'agit pas d'apt-get install bind9, mais de créer un programme de serveur DNS.

«Je n'ai pas besoin de cela, j'ai besoin de créer mes propres bloquer l'accès aux ressources inutiles sur les pages Web et [la productivité au travail. [Http://engineer.typemag.jp/article/fukuyuki41) N'est-ce pas à peu près l'heure? ”N'est-ce pas?

Cependant, si vous pouviez contrôler librement la réponse DNS avec Python, vous pourriez faire quelque chose de plus intéressant.

Créer un serveur DNS avec Twisted

Soudain, j'ai cherché sur Google comment créer mon propre serveur DNS avec Python.

Il existe une méthode pour créer un serveur DNS en utilisant Twisted dans l'article trouvé dans Qiita. http://qiita.com/futoase/items/174883d7fa8d2dd4240a

Après avoir examiné la dernière documentation, j'ai trouvé qu'il était plus facile que prévu de créer un serveur DNS personnalisé qui assemble dynamiquement les réponses.

http://twistedmatrix.com/documents/current/names/howto/custom-server.html

C'est fantastique! Vos rêves se répandront!

À quoi peut-il servir

Puisque le contenu de la réponse peut être assemblé avec du code Python, il semble que diverses choses intéressantes puissent être faites.

Par exemple, Retty a créé son propre serveur DNS pour faciliter le passage à un environnement de test. En combinant avec les paramètres LAN sans fil, la même demande que la production peut être envoyée à l'environnement de test simplement en se connectant à un point d'accès spécifique.

Dans le réseau de l'entreprise, un serveur DNS pour l'environnement de test créé par Twisted est en cours d'exécution, et lorsque vous demandez le nom d'hôte de l'environnement de production, l'adresse IP du résultat de la demande du nom d'hôte de l'environnement de test est renvoyée. En utilisant un LAN sans fil qui prend en charge les VLAN et plusieurs ESSID, les clients connectés à un nom de point d'accès spécifique interrogent automatiquement le serveur DNS pour l'environnement de test. Par conséquent, même si la demande concerne la même URL que l'environnement de production, si vous contactez le serveur DNS pour l'environnement de test, vous serez dirigé vers l'environnement de test. C'est DNS Spoo ... Oh, on dirait que quelqu'un est venu.

En outre, il peut être possible de se lier à d'autres services tels qu'AWS pour créer un serveur DNS qui renvoie une réponse appropriée en fonction de la configuration et de l'état du service.

Faisons-le tout de suite

Créons donc un serveur DNS qui fonctionne avec l'API AWS.

Si vous demandez l'ID d'une instance EC2 comme ʻi-abcd1234.ec2.local`, il retournera l'adresse IP attribuée à l'instance.

Préparation du colis

Après avoir rendu Python 2 et pip disponibles, insérez Twisted. À partir de Twisted-15.5.0, cela ne semblait pas fonctionner car une partie du code lié au serveur DNS ne prenait pas en charge Python 3.

[user@localhost]$ pip2 install twisted

Dans cet exemple, nous accéderons à AWS, donc installez Boto 3 et l'AWS CLI et définissez les informations d'identification.

[user@localhost]$ pip2 install boto3 awscli
[user@localhost]$ aws configure

Les exemples suivants ont été confirmés pour fonctionner avec Python-2.7.10, Twisted-15.5.0 et boto3-1.2.3.

Créer un résolveur

Selon Twisted Documents (http://twistedmatrix.com/documents/current/names/howto/custom-server.html#a-server-which-computes-responses-dynamically), résolution de nom dynamique Il semble que le moyen le plus rapide d'y parvenir soit de créer et d'enregistrer un résolveur personnalisé. Twisted gère le traitement du protocole, c'est donc très simple.

Donc, pour les demandes au domaine ʻec2.local`, nous considérerons la première étiquette comme l'ID d'instance EC2 et créerons un résolveur qui répond avec cette adresse IP.

Vous pouvez le faire en héritant de twisted.names.common.ResolverBase et en remplaçant lookupAddress ().

resolver.py


# coding=utf-8

import boto3
import botocore
from twisted.internet import defer
from twisted.logger import Logger
from twisted.names import common, dns, error

log = Logger()

ec2 = boto3.resource('ec2')


def find_ec2_address(instance_id):
    if instance_id:
        try:
            instance = ec2.Instance(instance_id)
            #Instance si vous avez besoin d'une adresse IP publique.public_ip_Peut être obtenu à partir de l'adresse.
            address = instance.private_ip_address
            log.debug('Found {instance!r} address={address}', instance=instance, address=address)
            return address
        except botocore.exceptions.ClientError:
            log.failure('Failed to find {instance_id}', instance_id=instance_id)


class EC2ResourceResolver(common.ResolverBase):
    #Domaine à renommer dynamiquement
    _suffix = '.ec2.local'
    _ttl = 30

    def _should_resolve(self, name):
        return name.lower().endswith(self._suffix)

    def lookupAddress(self, name, timeout=None):
        log.debug('Looking up address {name}', name=name)

        if self._should_resolve(name):
            #Considérez la première étiquette comme l'ID d'instance EC2.
            instance_id = name.split('.', 1)[0]
            address = find_ec2_address(instance_id)

            answer, authority, additional = common.EMPTY_RESULT
            if address:
                answer = [
                    dns.RRHeader(
                            name=name,
                            ttl=self._ttl,
                            payload=dns.Record_A(address=b'%s' % (address,), ttl=self._ttl),
                            auth=True)
                ]

            return defer.succeed((answer, authority, additional))

        # error.DomainError()Si vous échouez, il contactera le résolveur suivant.
        return defer.fail(error.DomainError(name))

    lookupAllRecords = lookupAddress

    #Pour éviter les requêtes répétées à d'autres résolveurs
    #D'autres types de requêtes sont également résolus en tant que résultats vides dans le domaine cible.
    def _lookup(self, name, cls, type, timeout):
        if self._should_resolve(name):
            return defer.succeed(common.EMPTY_RESULT)
        return defer.fail(error.DomainError(name))

Oups, Twisted est un framework de programmation asynchrone, mais il est bloqué en appelant boto ... Je crains qu'il ne se comporte pas bien, mais cette fois c'est un échantillon, alors passons à autre chose.

Rendre le corps du serveur

Ensuite, créez un serveur de cache DNS qui utilise ce résolveur pour la résolution de noms.

ec2-dns-server.py


#!/usr/bin/env python2
# coding=utf-8

import sys

from twisted.internet import reactor
from twisted.names import client, dns, server
from twisted.python import log

from resolver import EC2ResourceResolver


def main():
    log.startLogging(sys.stderr)

    factory = server.DNSServerFactory(
        clients=[
            EC2ResourceResolver(),
            client.Resolver(servers=[('8.8.8.8', 53), ('8.8.4.4', 53)])
        ]
    )
    protocol = dns.DNSDatagramProtocol(factory)

    reactor.listenUDP(10053, protocol)
    reactor.listenTCP(10053, factory)

    reactor.run()


if __name__ == '__main__':
    raise SystemExit(main())

Dans cet exemple, la requête DNS est écoutée sur le port 10053 et la requête non résolue est relayée vers Google Public DNS ("8.8.8.8", "8.8.4.4").

Si vous voulez obtenir cette destination de relais depuis / etc / resolv.conf

            client.Resolver(servers=[('8.8.8.8', 53), ('8.8.4.4', 53)])

Partie de

            client.Resolver(resolv='/etc/resolv.conf')

Ce n'est pas grave si vous le réécrivez.

Essayez de bouger

Déplaçons-le.

[user@localhost]$ python2 ec2-dns-server.py
2015-12-24 20:35:49+0900 [-] Log opened.
2015-12-24 20:35:49+0900 [-] DNSDatagramProtocol starting on 10053
2015-12-24 20:35:49+0900 [-] Starting protocol <twisted.names.dns.DNSDatagramProtocol object at 0x6fffe4bccd0>
2015-12-24 20:35:49+0900 [-] DNSServerFactory starting on 10053
2015-12-24 20:35:49+0900 [-] Starting factory <twisted.names.server.DNSServerFactory instance at 0x6fffe4c61b8>

Je vais le tester en utilisant dig pour voir s'il fonctionne correctement

[user@localhost]$ dig -p 10053 @localhost i-abcd1234.ec2.local +norecurse +short
10.11.25.25

Si l'adresse de l'instance EC2 est renvoyée comme ceci, cela fonctionne correctement.

Les possibilités sont infinies

Le DNS peut être un service abordable à utiliser comme fenêtre de découverte de service, car les points de terminaison AWS fournissent divers noms DNS (xxxx.amazonaws.com).

[RFC 2782](https: // www.) Utilisé par Active Directory est un moyen de trouver l'emplacement d'un service par nom abstrait en utilisant DNS. Aliase utilisant l'enregistrement SRV (ietf.org/rfc/rfc2782.txt) ou, plus concis, CNAME comme dans RFC 2219 Il semble y avoir un moyen. La méthode d'attribution d'un alias avec CNAME a l'avantage d'être très polyvalente, et en fait, dans AWS, [RDS for Oracle Database Multi-AZ](http://aws.typepad.com/aws_japan/2012/05/multi-az- option-for-amazon-rds-for-oracle-database.html) et Détection automatique du nœud ElastiCache ) Semble être utilisé.

Cependant, même si vous essayez d'abstraire l'emplacement du service avec CNAME, il existe diverses restrictions avec CNAME. Par exemple, dans une configuration où l'environnement change dynamiquement en augmentant ou en abaissant l'instance, le problème de la façon de maintenir et de gérer la correspondance avec l'hôte réel demeure.

Par conséquent, ["Terms from settings"](https://ja.wikipedia.org/wiki/%E8%A8%AD%E5%AE%9A%E3%82%88%E3%82%8A%E8%A6% 8F% E7% B4% 84) Si vous attribuez à une ressource telle qu'une instance EC2 un nom facile à gérer à partir du programme, vous pouvez créer un service DNS qui répond dynamiquement à l'aide des métadonnées de la ressource. C'est vrai. Excluez les esclaves qui ont des retards de réplication tels que MySQL et provoquent un basculement (Je peux le faire avec HAProxy), plus Vos rêves peuvent se propager.

Recommended Posts

Créez votre propre serveur DNS avec Twisted
Créez votre propre valeur composite avec SQLAlchemy
Mémo pour créer votre propre Box avec le Python de Pepper
Créez votre propre middleware Django
Apprentissage par renforcement 23 Créez et utilisez votre propre module avec Colaboratory
Résolvez votre propre labyrinthe avec Q Learning
Créez votre propre service de résolution de noms
[Django] Créez votre propre page d'erreur 403, 404, 500
Entraînez UGATIT avec votre propre jeu de données
Résolvez votre propre labyrinthe avec DQN
Votre propre client Twitter réalisé avec Django
Créez vos propres commandes Linux en Python
Créez facilement un serveur DNS en utilisant Twisted
[Renforcer l'apprentissage] DQN avec votre propre bibliothèque
Créez wordcloud à partir de votre tweet avec python3
[LLDB] Créez votre propre commande avec Python
Pour importer votre propre module avec jupyter
Création de la première application avec Django startproject
Publiez votre propre bibliothèque Python sur Homebrew
Créez votre propre caméra virtuelle avec Python + OpenCV et appliquez des effets originaux
Essayez de créer votre propre AWS-SDK avec bash
Créez rapidement votre propre module avec setuptools (python)
Entraînez Stanford NER Tagger avec vos propres données
[Apprentissage automatique] Créez un modèle d'apprentissage automatique en effectuant un apprentissage par transfert avec votre propre ensemble de données
Créez une roue de votre propre module OpenCV
Créer un serveur "Hello World" (HTTP) dans Tornado
Créez rapidement un serveur API avec Python + Falcon
Créez votre propre lecteur de musique avec Bottle0.13 + jPlayer2.5!
Étapes pour installer votre propre bibliothèque avec pip
Flux de création de votre propre package avec setup.py avec python
Appelez votre propre bibliothèque de langage C avec Go en utilisant cgo
Créez votre propre Big Data en Python pour validation
Créez votre propre stéréogramme aléatoire (RDS) en Python.
Écrivez votre propre fonction d'activation avec Pytorch (sigmoïde dur)
Appelons votre propre bibliothèque C ++ avec Python (Préférences)
Définissez votre propre fonction de distance avec k-means de scikit-learn
[Blender x Python] Créez votre propre fonction et résumé jusqu'à présent
Créez un serveur de musique domestique avec Centos8 + Universal Media Server
Créez un faux serveur Minecraft en Python avec Quarry
Créez des jeux avec Pygame
Serveur proxy avec Docker
Créer un filtre avec scipy
Serveur DNS en Python ....
Serveur local avec python
Jusqu'à ce que vous puissiez installer votre propre bibliothèque Python avec pip
Essayez de trier vos propres objets avec des files d'attente prioritaires en Python
Exécutez l'intelligence de votre propre bibliothèque python avec VScode.
Configurez votre propre serveur Web dans votre projet d'application Pepper
Apprenez «l'architecture x86 apprise avec votre propre émulateur» avec Manjaro (Linux)