Have you ever thought "I want to make my own DNS server"? ʻApt-get install bind9`, not a DNS server program.
"That's not necessary, you need to create your own block access to unnecessary resources on web pages or work productivity It's about the time when you can't access the site that drops the data. "
However, if you could control the DNS response freely with Python, you could do something more interesting.
Suddenly I was curious about how to make my own DNS server with Python.
There is a method to build a DNS server using Twisted in the article found on Qiita. http://qiita.com/futoase/items/174883d7fa8d2dd4240a
After reviewing the latest documentation, I found that it was easier than I expected to create a custom DNS server that dynamically assembles responses.
http://twistedmatrix.com/documents/current/names/howto/custom-server.html
This is fantastic! Your dreams will spread!
Since the response contents can be assembled with Python code, it seems that various interesting things can be done.
For example, Retty created its own DNS server to make it easier to switch to a test environment. By combining with the wireless LAN settings, the same request as the production can be sent to the test environment just by connecting to a specific access point.
In the corporate network, the DNS server for the test environment created by Twisted is running, and when you inquire about the host name of the production environment, the IP address of the result of inquiring the host name of the test environment is returned. By using a wireless LAN that supports VLANs and multiple ESSIDs, clients connected to a specific access point name will automatically query the DNS server for the test environment. As a result, even if the request is for the same URL as the production environment, if you contact the DNS server for the test environment, you will be directed to the test environment. This is basically DNS Spoo ... Oh, it looks like someone has come.
In addition, it may be possible to link with other services such as AWS to create a DNS server that returns an appropriate response according to the configuration and status of the service.
So let's create a DNS server that works with the AWS API.
If you ask for the ID of an EC2 instance like ʻi-abcd1234.ec2.local`, try to return the IP address assigned to the instance.
After making Python 2 and pip available, put Twisted in. As of Twisted-15.5.0, it didn't work because some of the DNS server related code didn't seem to support Python 3.
[user@localhost]$ pip2 install twisted
In this example, we will access AWS, so install Boto 3 and the AWS CLI and set the credentials.
[user@localhost]$ pip2 install boto3 awscli
[user@localhost]$ aws configure
The following samples have been confirmed to work with Python-2.7.10, Twisted-15.5.0 and boto3-1.2.3.
According to Twisted Documents (http://twistedmatrix.com/documents/current/names/howto/custom-server.html#a-server-which-computes-responses-dynamically), dynamic name resolution It seems that the quickest way to achieve this is to create and register a custom resolver. It's very easy because Twisted handles the protocol processing.
So, in response to a query to the ʻec2.local` domain, we will consider the first label as the EC2 instance ID and create a resolver that responds with that IP address.
You can do this by inheriting twisted.names.common.ResolverBase
and overridinglookupAddress ()
.
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 if you need a public IP address.public_ip_Can be obtained from address.
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):
#Domain to be dynamically resolved
_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):
#Consider the first label as the EC2 instance ID.
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()If you fail with, it will contact the next resolver.
return defer.fail(error.DomainError(name))
lookupAllRecords = lookupAddress
#To avoid repeated queries to other resolvers
#Other query types are also resolved as empty results in the target domain.
def _lookup(self, name, cls, type, timeout):
if self._should_resolve(name):
return defer.succeed(common.EMPTY_RESULT)
return defer.fail(error.DomainError(name))
Oops, Twisted is an asynchronous programming framework, but it's blocked by calling boto ... I'm worried that it's not well-behaved, but this time it's a sample, so let's move on.
Next, create a DNS cache server that uses this resolver for name resolution.
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())
In this example, it listens for DNS queries on port 10053 and relays unresolved queries to Google Public DNS (8.8.8.8
, 8.8.4.4
).
If you want to get this relay destination from /etc/resolv.conf
client.Resolver(servers=[('8.8.8.8', 53), ('8.8.4.4', 53)])
Part of
client.Resolver(resolv='/etc/resolv.conf')
It is OK if you rewrite it to.
Let's move it.
[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>
I will test it with dig to see if it works properly
[user@localhost]$ dig -p 10053 @localhost i-abcd1234.ec2.local +norecurse +short
10.11.25.25
If the EC2 instance address is returned like this, it is working properly.
DNS may be an affordable service to use as a service discovery window, as AWS endpoints provide various DNS names (xxxx.amazonaws.com
).
[RFC 2782](https: // www.) Used by Active Directory is a way to find the location of a service by abstract name using DNS. Aliase using an SRV record (ietf.org/rfc/rfc2782.txt) or, more simply, a CNAME as in RFC 2219 There seems to be a way. The method of assigning an alias with CNAME has the advantage of being highly versatile, and in fact, on AWS [Multi-AZ of RDS for Oracle Database](http://aws.typepad.com/aws_japan/2012/05/multi-az- option-for-amazon-rds-for-oracle-database.html) and Autodiscover ElastiCache Nodes (https://docs.aws.amazon.com/ja_jp/AmazonElastiCache/latest/UserGuide/AutoDiscovery.HowAutoDiscoveryWorks.html) ) Seems to be used.
However, even if you try to abstract the location of the service with CNAME, there are various restrictions with CNAME. For example, in a configuration where the environment changes dynamically by raising or lowering the instance, the problem of how to maintain and manage the correspondence to the real host remains.
So ["Convention over Configuration"](https://ja.wikipedia.org/wiki/%E8%A8%AD%E5%AE%9A%E3%82%88%E3%82%8A%E8%A6% If you give a resource such as an EC2 instance a name that is easy to handle from the program based on 8F% E7% B4% 84), you can create a DNS service that responds dynamically using the resource's meta data. That's right. Failover by excluding slaves that have replication delays such as MySQL (I can do that with HAProxy), more Your dreams may spread.
Recommended Posts