mirror of
https://github.com/fergalmoran/bitchmin.git
synced 2025-12-22 09:27:53 +00:00
Add hosting and record update loop
This commit is contained in:
@@ -1 +1 @@
|
|||||||
VUE_APP_API_SERVER=https://admin-api.bitchmints.com
|
VUE_APP_API_SERVER=https://api.fergl.ie
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ from types import SimpleNamespace
|
|||||||
|
|
||||||
|
|
||||||
class Zone(object):
|
class Zone(object):
|
||||||
def __init__(self, serial, name, admin, hosts, nameservers, mailexchangers):
|
def __init__(self, serial, name, admin, hosts, nameservers, mailexchangers, ttl=30):
|
||||||
self._serial = serial
|
self._serial = serial
|
||||||
self._name = name
|
self._name = name
|
||||||
self._admin = admin
|
self._admin = admin
|
||||||
self._hosts = hosts
|
self._hosts = hosts
|
||||||
self._nameservers = nameservers
|
self._nameservers = nameservers
|
||||||
self._mailexchangers = mailexchangers
|
self._mailexchangers = mailexchangers
|
||||||
|
self._ttl = ttl
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_json(data):
|
def from_json(data):
|
||||||
@@ -63,6 +64,10 @@ class Zone(object):
|
|||||||
def mailexchangers(self):
|
def mailexchangers(self):
|
||||||
return self._mailexchangers
|
return self._mailexchangers
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ttl(self):
|
||||||
|
return self._ttl
|
||||||
|
|
||||||
|
|
||||||
class Nameserver(object):
|
class Nameserver(object):
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import requests
|
import requests
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer, reactor
|
||||||
from twisted.names import dns, error
|
from twisted.names import dns, error
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from twisted.plugins.twisted_reactors import wx
|
||||||
|
|
||||||
|
from models.zone import Zone
|
||||||
|
|
||||||
|
TIMER_ID = 100 # pick a number
|
||||||
|
|
||||||
|
|
||||||
class InvalidZoneException(Exception):
|
class InvalidZoneException(Exception):
|
||||||
pass
|
pass
|
||||||
@@ -14,8 +20,10 @@ class Resolver:
|
|||||||
query type and name.
|
query type and name.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, zones):
|
def __init__(self, api_host):
|
||||||
self._zones = zones
|
self._api_host = api_host
|
||||||
|
self._zones = self.load_zones(api_host)
|
||||||
|
self.reload_zones()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _build_host(record_type, host, ip, ttl):
|
def _build_host(record_type, host, ip, ttl):
|
||||||
@@ -27,6 +35,20 @@ class Resolver:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_zones(api_host):
|
||||||
|
response = requests.get('{}/dns/zones'.format(api_host))
|
||||||
|
zones = Zone.from_json(response.text)
|
||||||
|
return {
|
||||||
|
zone.name: zone
|
||||||
|
for zone in zones
|
||||||
|
}
|
||||||
|
|
||||||
|
def reload_zones(self):
|
||||||
|
logging.debug('Reloading zones')
|
||||||
|
self._zones = Resolver.load_zones(self._api_host)
|
||||||
|
reactor.callLater(60, self.reload_zones)
|
||||||
|
|
||||||
def _gen_axfr(self, zone_name):
|
def _gen_axfr(self, zone_name):
|
||||||
zone = self._zones[zone_name]
|
zone = self._zones[zone_name]
|
||||||
|
|
||||||
@@ -35,7 +57,7 @@ class Resolver:
|
|||||||
name=zone.name,
|
name=zone.name,
|
||||||
type=dns.SOA,
|
type=dns.SOA,
|
||||||
cls=dns.IN,
|
cls=dns.IN,
|
||||||
ttl=86400,
|
ttl=zone.ttl,
|
||||||
auth=True,
|
auth=True,
|
||||||
payload=dns.Record_SOA(
|
payload=dns.Record_SOA(
|
||||||
mname=next(iter(zone.nameservers)),
|
mname=next(iter(zone.nameservers)),
|
||||||
@@ -71,7 +93,7 @@ class Resolver:
|
|||||||
type=dns.NS,
|
type=dns.NS,
|
||||||
cls=dns.IN,
|
cls=dns.IN,
|
||||||
ttl=700,
|
ttl=700,
|
||||||
auth=False,
|
auth=True,
|
||||||
payload=record,
|
payload=record,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -79,10 +101,11 @@ class Resolver:
|
|||||||
host = zone.hosts[h]
|
host = zone.hosts[h]
|
||||||
record = dns.RRHeader(
|
record = dns.RRHeader(
|
||||||
'{}.{}'.format(host.name, zone.name),
|
'{}.{}'.format(host.name, zone.name),
|
||||||
dns.A,
|
type=dns.A,
|
||||||
dns.IN,
|
cls=dns.IN,
|
||||||
host.ttl,
|
ttl=host.ttl,
|
||||||
dns.Record_A(host.ip, host.ttl))
|
auth=True,
|
||||||
|
payload=dns.Record_A(host.ip, host.ttl))
|
||||||
records.append(record)
|
records.append(record)
|
||||||
|
|
||||||
records.append(soa)
|
records.append(soa)
|
||||||
@@ -152,6 +175,7 @@ class MemoryResolver(Resolver):
|
|||||||
record = self._zones[zone_name].hosts[host]
|
record = self._zones[zone_name].hosts[host]
|
||||||
answers.append(dns.RRHeader(
|
answers.append(dns.RRHeader(
|
||||||
name=str(query.name),
|
name=str(query.name),
|
||||||
|
auth=True,
|
||||||
payload=dns.Record_A(
|
payload=dns.Record_A(
|
||||||
address=record.ip,
|
address=record.ip,
|
||||||
ttl=record.ttl
|
ttl=record.ttl
|
||||||
@@ -171,6 +195,7 @@ class MemoryResolver(Resolver):
|
|||||||
name=str(query.name),
|
name=str(query.name),
|
||||||
type=dns.NS,
|
type=dns.NS,
|
||||||
ttl=self._zones[zone_name].nameservers[ns].ttl,
|
ttl=self._zones[zone_name].nameservers[ns].ttl,
|
||||||
|
auth=True,
|
||||||
payload=dns.Record_NS(
|
payload=dns.Record_NS(
|
||||||
name=self._zones[zone_name].nameservers[ns].name,
|
name=self._zones[zone_name].nameservers[ns].name,
|
||||||
ttl=self._zones[zone_name].nameservers[ns].ttl
|
ttl=self._zones[zone_name].nameservers[ns].ttl
|
||||||
@@ -185,6 +210,7 @@ class MemoryResolver(Resolver):
|
|||||||
name=str(query.name),
|
name=str(query.name),
|
||||||
type=dns.MX,
|
type=dns.MX,
|
||||||
ttl=self._zones[zone_name].mailexchangers[mx].ttl,
|
ttl=self._zones[zone_name].mailexchangers[mx].ttl,
|
||||||
|
auth=True,
|
||||||
payload=dns.Record_MX(
|
payload=dns.Record_MX(
|
||||||
name=self._zones[zone_name].mailexchangers[mx].name,
|
name=self._zones[zone_name].mailexchangers[mx].name,
|
||||||
ttl=self._zones[zone_name].mailexchangers[mx].ttl,
|
ttl=self._zones[zone_name].mailexchangers[mx].ttl,
|
||||||
@@ -200,13 +226,17 @@ class MemoryResolver(Resolver):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
logging.info(query)
|
logging.info(query)
|
||||||
zone_name, zone = self._get_authoritative_zone(query)
|
try:
|
||||||
if zone and zone_name:
|
zone_name, zone = self._get_authoritative_zone(query)
|
||||||
logging.debug('BitchNS: Authoritative for {}'.format(zone_name))
|
if zone and zone_name:
|
||||||
result = self._get_response(zone_name, query)
|
logging.debug('BitchNS: Authoritative for {}'.format(zone_name))
|
||||||
if result:
|
result = self._get_response(zone_name, query)
|
||||||
logging.debug('BitchNS: Resolving {} to {}'.format(zone_name, result))
|
if result:
|
||||||
return defer.succeed(result)
|
logging.debug('BitchNS: Resolving {} to {}'.format(zone_name, result))
|
||||||
|
return defer.succeed(result)
|
||||||
|
|
||||||
logging.error('Query failure')
|
logging.error('Query failure')
|
||||||
return defer.fail(error.DomainError())
|
return defer.fail(error.DomainError())
|
||||||
|
except TypeError as e:
|
||||||
|
logging.error('Invalid query - {}'.format(query))
|
||||||
|
return defer.fail(error.DNSFormatError())
|
||||||
|
|||||||
@@ -18,19 +18,8 @@ WORKER_PORT = int(os.environ.get('WORKER_PORT') or 10054)
|
|||||||
API_HOST = os.environ.get('API_HOST') or 'http://localhost:5000'
|
API_HOST = os.environ.get('API_HOST') or 'http://localhost:5000'
|
||||||
|
|
||||||
|
|
||||||
# TODO: This smells
|
|
||||||
def get_zones():
|
|
||||||
response = requests.get('{}/dns/zones'.format(API_HOST))
|
|
||||||
zones = Zone.from_json(response.text)
|
|
||||||
return {
|
|
||||||
zone.name: zone
|
|
||||||
for zone in zones
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
zones = get_zones()
|
memory_resolver = MemoryResolver(API_HOST)
|
||||||
memory_resolver = MemoryResolver(zones)
|
|
||||||
|
|
||||||
dns_factory = server.DNSServerFactory(
|
dns_factory = server.DNSServerFactory(
|
||||||
clients=[
|
clients=[
|
||||||
|
|||||||
7
bitchmin-server/tests/test_worker.py
Normal file
7
bitchmin-server/tests/test_worker.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import dns
|
||||||
|
import dns.zone
|
||||||
|
from dns.resolver import NoAnswer
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_record(resolver) -> None:
|
||||||
|
assert False
|
||||||
2
build.sh
2
build.sh
@@ -1,3 +1,3 @@
|
|||||||
cd bitchmin-server && ./build.sh && cd ..
|
cd bitchmin-server && ./build.sh && cd ..
|
||||||
cd bitchmin-api && ./build.sh && cd ..
|
cd bitchmin-api && ./build.sh && cd ..
|
||||||
cd bitchmin-web && ./build.sh && cd ..
|
cd bitchmin-client && ./build.sh && cd ..
|
||||||
|
|||||||
1
hosting/.machineenv
Normal file
1
hosting/.machineenv
Normal file
@@ -0,0 +1 @@
|
|||||||
|
podnoms-status
|
||||||
8
hosting/docker-compose.yml
Normal file
8
hosting/docker-compose.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
version: "3.8"
|
||||||
|
services:
|
||||||
|
server:
|
||||||
|
image: fergalmoran/bitchmin-server
|
||||||
|
ports:
|
||||||
|
- "53:53"
|
||||||
|
environment:
|
||||||
|
- API_HOST=https://api.fergl.ie
|
||||||
Reference in New Issue
Block a user