예제 #1
0
 def __init__(self, api_url, api_token):
     self.api_url = api_url
     self.api_token = api_token
     payload = jwt.decode(api_token, verify=False)  # do not trust
     if not "dns_server_name" in payload.keys(
     ) or not payload["dns_server_name"]:
         logger.critical("no dns_server_name on api token")
         raise Exception("no dns_server_name on api token")
     self.dns_server_name = payload["dns_server_name"]
예제 #2
0
 def get(self, url, params=None, fail=True):
     params = params or {}
     headers = self.get_default_headers()
     res = requests.get(self.url(url), headers=headers)
     if fail:
         if res.status_code != 200:
             logger.critical(str(res.json()))
         res.raise_for_status()
     return res.json()
예제 #3
0
def token_has_one_required_scopes(token_payload: TokenPayload,
                                  scopes: List[str]):
    token_scopes = token_payload.scopes
    required_scopes = scopes or []
    for required_scope in required_scopes:
        for token_scope in token_scopes:
            if token_scope == required_scope:
                return True
    logger.critical(f"auth token missing at least one scope: {required_scope}")
    return False
예제 #4
0
파일: parser.py 프로젝트: slooppe/bountydns
    def from_zone(cls, zone):
        records = []
        logger.info(
            f"[email protected] - Loading zone: {zone.domain}/{zone.ip} ({zone.id})"
        )
        dns_records = zone.dns_records or []
        # if the zone has no records, create some default ones
        if not dns_records:
            logger.warning(
                f"[email protected] - Zone has no dns_records. loading defaults: {zone.domain}/{zone.ip} ({zone.id})"
            )
            rrs = RR.fromZone(
                ZONE_TEMPLATE.format(domain_name=zone.domain, domain_ip=zone.ip)
            )
            zone_records = [Record.make(zone, rr) for rr in rrs]
            for zr in zone_records:
                # TODO: make this clean on output
                rrstr = str(dedent(str(zr.rr)))
                logger.debug(f"[email protected] - Loading record entry: {rrstr}")
                logger.debug(
                    "[email protected] - Loaded record details - name: {} | rtype: {} | rr: {}".format(
                        str(zr.rr.rname), str(QTYPE[zr.rr.rtype]), str(zr.rr)
                    )
                )
        else:
            # loop over each dns_record of the zone and convert it to RR record
            dns_records = sorted(dns_records, key=lambda x: x.sort)
            zone_records = []
            for dns_record in dns_records:
                try:
                    rrs = RR.fromZone(dns_record.record)
                    _zone_records = [Record.make(zone, rr) for rr in rrs]
                    for zr in _zone_records:
                        rrstr = str(dedent(str(zr.rr)))
                        logger.debug(
                            f"[email protected] - Loading record: {str(dns_record.record)}"
                        )
                        logger.debug(
                            f"[email protected] - Loading record entry: {rrstr}"
                        )
                        logger.debug(
                            "[email protected] - Loaded record details - name: {} | rtype: {} | rr: {}".format(
                                str(zr.rr.rname), str(QTYPE[zr.rr.rtype]), str(zr.rr)
                            )
                        )
                    zone_records = zone_records + _zone_records
                except Exception as e:
                    logger.critical(
                        f'[email protected] - Error processing line ({e.__class__.__name__}: {e}) "{dns_record.id}:{dns_record.record}" '
                    )
                    raise e

        # add the records for the zone to the rest of the records
        records = records + zone_records
        return cls(records)
예제 #5
0
    def post(self, url: str, data=None, fail=True):
        data = data or {}
        headers = self.get_default_headers()
        res = requests.post(self.url(url), json=data, headers=headers)
        logger.info("Posting URL: " + str(self.url(url)))

        if fail:
            if res.status_code != 200:
                logger.critical(f"Error posting API {self.url(url)}: " +
                                str(res.json()))
            res.raise_for_status()
        return res.json()
예제 #6
0
 def label(self, key):
     if "." not in key:
         return getattr(self.model(), key)
     parent = self.model()
     parts = key.split(".")
     final_index = len(parts) - 1
     for i, part in enumerate(parts):
         if i == final_index:
             logger.critical("LABEL " + str(getattr(parent, part)))
             return getattr(parent, part)
         parent = self._get_relationship_model(part, parent)
     raise Exception(f"Unable to decipher label for key: {key}")
예제 #7
0
    async def run(self):
        env = self.option("env")
        self.load_env(f"api.{env}")
        from bountydns.db.checks import is_db_up, is_db_setup

        self.db_register()
        db_up = is_db_up()
        if not db_up:
            logger.critical(
                "run@api_server.py - Database not up error. please check logs"
            )
            return self.exit(1)
        return 0
예제 #8
0
    def get(self, url: str, params=None, fail=True):
        params = params or {}
        headers = self.get_default_headers()
        res = requests.get(self.url(url), headers=headers, params=params)

        logger.info("Getting URL: " + str(self.url(url)))

        if fail:
            if res.status_code != 200:
                logger.critical(f"Error getting API {self.url(url)}: " +
                                str(res.json()))
            res.raise_for_status()
        return res.json()
예제 #9
0
async def index(
        sort_qs: SortQS = Depends(SortQS),
        pagination: PaginationQS = Depends(PaginationQS),
        api_token_repo: ApiTokenRepo = Depends(ApiTokenRepo()),
        token: TokenPayload = Depends(ScopedTo("api-token:list")),
        includes: List[str] = Query(None),
):
    logger.critical("LOOK AT MEEEEE")
    print("HELLOOOOOOOOOOO")
    includes = only(includes, ["dns_server"], values=True)

    pg, items = (api_token_repo.loads("dns_server").strict().sort(
        sort_qs).paginate(pagination).includes(includes).data())
    return ApiTokensResponse(pagination=pg, api_tokens=items)
예제 #10
0
    def make(cls, zone, rr):
        if not getattr(rr, "rtype", None):
            logger.critical(
                f"No rtype found for rr: {str(rr)} - {str(rr.__class__)}")

        # RR's don't have knowledge of domiain so replace "." with zone domain
        if rr.rname == ".":
            zone_rname = zone.domain + "."
        else:
            zone_rname = "." + zone.domain + "."
        new_label_name = str(rr.rname).replace(".", zone_rname)
        logger.debug(
            f"Replacing RR's rname {str(rr.rname)} with {new_label_name}")
        rr.set_rname(new_label_name)
        return cls(zone, rr)
예제 #11
0
async def broadcast_auth(websocket: WebSocket):
    try:
        await websocket.accept()
    except Exception as e:
        logger.critical(
            f"[email protected] - Accept Error: {str(type(e))}")
        logger.critical(
            f"[email protected] - Accept Trace: {str(e)}")
        return 1

    params = parse_qs(urlparse(str(websocket.url)).query)
    token = verify_jwt_token(params["ws_access_token"][0])
    if not token_has_required_scopes(token, []):  # TODO: check scopes later
        raise HTTPException(403, detail="Forbidden")
    user_repo = UserRepo(session())
    user = await current_user(token, user_repo)

    subscriber, channel = await make_subscriber("auth")

    try:
        while await channel.wait_message():
            logger.info("[email protected] - Waiting for message")
            msg = await channel.get(encoding="utf-8")
            logger.info("[email protected] - Received message: " +
                        str(msg))
            data = json.loads(msg)
            logger.info("[email protected] - Sending message: " +
                        str(data))
            await websocket.send_json(data)
    except Exception as e:
        logger.critical(
            f"[email protected] - Receieve/Send: Error: {str(type(e))}"
        )
        logger.critical(
            f"[email protected] - Receieve/Send: Trace:{str(e)}")
        logger.critical(
            f"[email protected] - Receieve/Send: Not closing or unsubscribing"
        )

        return 1

    logger.info(f"[email protected] - Attempting to unsuscribe")
    await subscriber.unsubscribe("channel:auth")
    logger.info(
        f"[email protected]: Websocket - Attempting to close socket"
    )
    await websocket.close()
예제 #12
0
    async def run(self):
        args = ["bountydns.api.main:api"]
        kwargs = self.get_kwargs()
        self.load_env("api")

        if self.should_import_check():
            logger.info("performing import check")
            from bountydns.api.main import api

        logger.critical("starting api server with options: {}".format(
            str(kwargs)))
        from bountydns.db.checks import is_db_up, is_db_setup

        if self.should_db_check():
            self.db_register()
            db_up = is_db_up()
            if not db_up:
                logger.critical("database not up error. please check logs")
                return self.exit(1)

        if self.option("db_setup"):
            logger.critical("running database migration")
            db_setup_options = self._args_to_dict(self.options)
            if self.option("db_seed"):
                db_setup_options["seed"] = True
            await DbSetup(db_setup_options).run()

        if self.should_db_check():
            db_setup = is_db_setup()
            if not db_setup:
                logger.critical("database not setup error. please check logs")
                return self.exit(1)

        from bountydns.broadcast import is_broadcast_up

        if self.should_bcast_check():
            bcast_up = await is_broadcast_up()
            if not bcast_up:
                logger.critical(
                    "broadcast (queue) not up error. please check logs")
                return self.exit(1)

        if self.option("db_seed_env", False):
            self.seed_from_env()

        return uvicorn.run(*args, **kwargs)
예제 #13
0
def token_has_required_scopes(token_payload: TokenPayload, scopes: List[str]):
    token_scopes = token_payload.scopes
    required_scopes = scopes or []
    for required_scope in required_scopes:
        satisfied = False
        for token_scope in token_scopes:
            if token_scope == required_scope:
                satisfied = True
            # probably bad / too generous
            # a:b in a:b:c
            elif token_scope in required_scope:
                satisfied = True
        if not satisfied:
            logger.critical(f"auth token missing scope: {required_scope}")

            return False
    return True
예제 #14
0
파일: checks.py 프로젝트: slooppe/bountydns
async def is_broadcast_up():
    seconds = 0
    while True:
        if seconds > 60:
            logger.critical("could not start api. broadcast (queue) not up")
            return False
        logger.debug("checking for broadcast (queue) status")
        try:
            redis = await make_redis()
            await redis.set("up-key", "value")
            val = await redis.get("up-key")
            val = await redis.delete("up-key")
            return True
        except Exception as e:
            logger.critical(
                "broadcast (queue) check not ready after {} seconds: {}".
                format(str(seconds), str(e.__class__.__name__)))
        seconds = seconds + 2
        sleep(2)
예제 #15
0
    async def run(self):
        port = self.get_port()
        listen = self.get_listen()

        # TODO: thread issues?
        api_client = ApiClient(self.get_api_url(), self.get_api_token())

        if not api_client.wait_for_up():
            logger.critical("could not connect to api. quitting")
            self.exit(1)

        if self.option("no_sync"):
            logger.info("skipping syncing api token")
        else:
            api_client.sync()

        resolver = Resolver(api_client)
        udp_server = DNSServer(
            resolver,
            address=listen,
            port=port,
            handler=DNSHandler,
            logger=DNSLogger(api_client),
        )
        tcp_server = DNSServer(
            resolver,
            address=listen,
            port=port,
            tcp=True,
            handler=DNSHandler,
            logger=DNSLogger(api_client),
        )

        logger.info("starting DNS server on port %d", port)
        udp_server.start_thread()
        tcp_server.start_thread()

        try:
            while udp_server.isAlive():
                sleep(1)
        except KeyboardInterrupt:
            pass
예제 #16
0
    def __init__(self, api_url, api_token, verify_ssl=True):
        self.api_url = api_url
        self.api_token = api_token
        self.zones = []
        self.verify_ssl = verify_ssl
        if not self.verify_ssl:
            logger.warning(
                "__init__@dns_server.py - Disabling SSL Warnings. I hope this was intentional"
            )
            requests.packages.urllib3.disable_warnings(
                category=InsecureRequestWarning)

        payload = jwt.decode(api_token, verify=False)  # do not trust
        if not "dns_server_name" in payload.keys(
        ) or not payload["dns_server_name"]:
            logger.critical(
                f"__init__@api_client.py - No dns_server_name on api token payload: {str(payload)}"
            )
            raise Exception("no dns_server_name on api token")
        self.dns_server_name = payload["dns_server_name"]
예제 #17
0
    async def run(self):
        self.load_env("api")
        self.db_register()
        failed = []
        if self.option("confirm"):
            for model in models:
                for item in self.session().query(model).all():
                    logger.info(f"deleting {item}")
                    try:
                        self.session().delete(item)
                        self.session().commit()
                    except Exception as e:
                        failed.append((item, e))
        else:
            logger.warning("You must confirm to drop data")

        if len(failed) > 0:
            logger.critical("encountered errors")
            for f in failed:
                print("Failed:", item[0])
                print("Error", item[1])
예제 #18
0
def is_db_up():
    seconds = 0
    while True:
        if seconds > 60:
            logger.critical("could not start api. database not up")
            return False
        logger.debug("checking for db status")
        try:
            session().execute("SELECT 1")
            return True
        except KeyError as e:
            logger.critical(
                "database has not be registered. please call db_register(db_url)"
            )
            return False
        except Exception as e:
            logger.critical(
                "database check not ready after {} seconds: {}".format(
                    str(seconds), str(e.__class__.__name__)))
        seconds = seconds + 1
        sleep(1)
예제 #19
0
def is_db_setup():
    seconds = 0
    while True:
        if seconds > 60:
            logger.critical("could not start api. database not setup")
            return False
        logger.debug("checking for db migrations")
        try:
            session().execute("SELECT * from alembic_version")
            return True
        except KeyError as e:
            logger.critical(
                "database has not be registered. please call db_register(db_url)"
            )
            return False
        except Exception as e:
            logger.critical(
                "database has not been migrated. please run: bdnsctl.py db-setup"
            )
            return False
        seconds = seconds + 1
        sleep(1)
예제 #20
0
    async def run(self):
        app = "bountydns.api.main:api"
        kwargs = self.get_kwargs()
        env = self.option("env")
        self.load_env(f"api.{env}")

        if self.should_import_check():
            logger.info("performing import check")
            from bountydns.api.main import api

        logger.critical("starting api server with options: {}".format(
            str(kwargs)))
        from bountydns.db.checks import is_db_up, is_db_setup

        if self.should_db_check():
            self.db_register()
            db_up = is_db_up()
            if not db_up:
                logger.critical("database not up error. please check logs")
                return self.exit(1)

        if self.option("db_setup"):
            logger.critical("running database migration")
            db_setup_options = self._args_to_dict(self.options)
            if self.option("db_seed"):
                db_setup_options["seed"] = True
            await DbSetup(db_setup_options).run()

        if self.should_db_check():
            db_setup = is_db_setup()
            if not db_setup:
                logger.critical("database not setup error. please check logs")
                return self.exit(1)

        from bountydns.broadcast import is_broadcast_up

        if self.should_bcast_check():
            bcast_up = await is_broadcast_up()
            if not bcast_up:
                logger.critical(
                    "broadcast (queue) not up error. please check logs")
                return self.exit(1)

        if self.option("db_seed_env", False):
            self.seed_from_env()

        # taken from uvicorn/main.py:run
        config = UvicornConfig(app, **kwargs)
        server = UvicornServer(config=config)

        if isinstance(app, str) and (config.debug or config.reload):
            sock = config.bind_socket()
            supervisor = StatReload(config)
            logger.warning(f"running bountydns api in dev mode...")
            return supervisor.run(server.run, sockets=[sock])
        elif config.workers > 1:
            sock = config.bind_socket()
            supervisor = Multiprocess(config)
            logger.warning(f"running bountydns api in worker mode...")
            return supervisor.run(server.run, sockets=[sock])
        else:
            sockets = None
            logger.warning(f"running bountydns api in standard mode...")
            return await server.serve(sockets=sockets)
예제 #21
0
 def get_workers(self):
     if self.option("debug", None) or self.option("reload", None):
         logger.critical(
             "Canot use debug or reload with workers. Skipping.")
         return None
     return self.option("workers", 5)
예제 #22
0
    async def run(self):

        # TODO: thread issues?
        verify_ssl = True
        if bool(self.option("no_ssl_verify")):
            verify_ssl = False

        self.api_client = ApiClient(self.get_api_url(),
                                    self.get_api_token(),
                                    verify_ssl=verify_ssl)

        if not self.api_client.wait_for_up():
            logger.critical(
                "run@dns_server.py - Could not connect to api. quitting")
            self.exit(1)

        if self.option("no_sync"):
            logger.info("run@dns_server.py - Skipping syncing api token")
        else:
            self.api_client.sync()

        self.boot()

        self.start_servers()

        try:
            count = 0
            while self.udp_server.isAlive():
                if count > 0 and count % self.option("refresh_ttl") == 0:
                    if self.api_client.refresh_zones_if_needed():
                        logger.critical(
                            "run@dns_server.py - API Client found new or changed zones. Stopping servers..."
                        )
                        # TODO: figure out why "stop" does not release the address
                        self.stop_servers()

                        sleep(1)

                        stop_count = 0
                        logger.critical(
                            "run@dns_server.py - Waiting for UDP Server to stop..."
                        )
                        while self.udp_server.thread and self.udp_server.isAlive(
                        ):
                            if stop_count > 30:
                                logger.critical(
                                    "run@dns_server.py - UDP Server did not stop while reloading zones"
                                )
                                raise Exception(
                                    "run@dns_server.py - UDP Server threads went rogue during zone reload"
                                )
                            logger.info(
                                "run@dns_server.py - Waiting for DNS Server to stop before reloading zones"
                            )
                            stop_count = stop_count + 1
                            sleep(1)
                        stop_count = 0
                        logger.critical(
                            "run@dns_server.py - Waiting for TCP Server to stop..."
                        )
                        while self.tcp_server.thread and self.tcp_server.isAlive(
                        ):
                            if stop_count > 30:
                                logger.critical(
                                    "run@dns_server.py - TCP Server did not stop while reloading zones"
                                )
                                raise Exception(
                                    "run@dns_server.py - TCP Server threads went rogue during zone reload"
                                )
                            logger.info(
                                "run@dns_server.py - Waiting for DNS Server to stop before reloading zones"
                            )
                            stop_count = stop_count + 1
                            sleep(1)
                        logger.critical(
                            "run@dns_server.py - Rebooting server with fresh zones..."
                        )
                        self.boot()
                        self.start_servers()

                count = count + 1
                sleep(1)
        except KeyboardInterrupt:
            pass
예제 #23
0
파일: logger.py 프로젝트: rdsece/bountydns
 def log_request(self, handler, request, request_uuid):
     logger.critical(f"log_request: {handler}, {request}, {request_uuid}")
     self.api.create_dns_request(handler, request, request_uuid)
     super().log_request(handler, request)
예제 #24
0
    async def run(self):
        env = self.option("env")
        self.load_env(f"api.{env}")
        self.db_register()

        if self.option("target", False) == False:
            self.set_option("target", "dev")

        if self.option("target") == "env":
            # self.load_env("seed")
            logger.info(f"run@db_seed.py - Seeding {self.option('target')}")
            raise NotImplementedError()  # seed based on env vars

        elif self.option("target") == "dev":
            logger.info(f"run@db_seed.py - Seeding {self.option('target')}")
            logger.info("run@db_seed.py - reating superuser")
            super = factory("SuperUserFactory").create(email="*****@*****.**")

            logger.info("run@db_seed.py - Creating normal user")
            norm = factory("UserFactory").create(email="*****@*****.**")

            logger.info("run@db_seed.py - Creating dns_server")

            dns_server = factory("DnsServerFactory").create()

            logger.info("run@db_seed.py - Creating zones")

            zone = factory("ZoneFactory").create(domain="othersite.com",
                                                 ip="127.0.1.1")

            zone2 = factory("ZoneFactory").create(domain="friends4life.com",
                                                  ip="127.0.1.1",
                                                  dns_server=dns_server)

            zone3 = factory("ZoneFactory").create(domain="differentzone.com",
                                                  ip="127.0.1.1")

            logger.info("run@db_seed.py - Creating api_tokens")

            for i in range(65):
                factory("ApiTokenFactory").create(dns_server=dns_server)

            logger.info("run@db_seed.py - Creating dns_requests")

            for i in range(35):
                factory("DnsRequestFactory").create(dns_server=dns_server,
                                                    zone=zone2)

            for i in range(35):
                factory("DnsRequestFactory").create(dns_server=dns_server,
                                                    zone=zone3)

            logger.info("run@db_seed.py - Creating dns_records")

            for i in range(3):
                factory("DnsRecordFactory").create(zone=zone)

            for i in range(3):
                factory("DnsRecordFactory").create(zone=zone2)

            for i in range(3):
                factory("DnsRecordFactory").create(zone=zone3)
        else:
            logger.critical("run@db_seed.py - invalid target set for seeder")
            self.exit(1)
예제 #25
0
    async def run(self):
        app = "bountydns.api.main:api"
        kwargs = self.get_kwargs()
        env = self.option("env")
        self.load_env(f"api.{env}")

        if self.should_import_check():
            logger.info("run@api_server.py - Performing import check")
            from bountydns.api.main import api

        logger.critical(
            "run@api_server.py - Starting api server with options: {}".format(
                str(kwargs)))
        from bountydns.db.checks import is_db_up, is_db_setup

        # alembic just destroys the loggers, it's annoying
        if self.should_db_check():
            logger.info(
                "run@api_server.py - Waiting for database service to be up")
            db_wait_options = self._args_to_dict(self.options)
            await DbWait(db_wait_options).run()

        if self.option("db_setup"):
            logger.critical("run@api_server.py - Running database migration")
            db_setup_options = self._args_to_dict(self.options)
            if self.option("db_seed"):
                db_setup_options["seed"] = True
            await DbSetup(db_setup_options).run()

        if self.should_db_check():
            logger.info(
                "run@api_server.py - Checking if application database is setup and configured"
            )

            db_setup = is_db_setup()
            if not db_setup:
                logger.critical(
                    "run@api_server.py - Database not setup error. please check logs"
                )
                return self.exit(1)

        from bountydns.broadcast import is_broadcast_up

        if self.should_bcast_check():
            bcast_up = await is_broadcast_up()
            if not bcast_up:
                logger.critical(
                    "run@api_server.py - Broadcast (queue) not up error. please check logs"
                )
                return self.exit(1)

        if self.option("db_seed_env", False):
            self.seed_from_env()

        # taken from uvicorn/main.py:run

        logger.debug("run@api_server.py - Building Uvicorn Config and Server")
        config = UvicornConfig(app,
                               log_config=self.get_uvicorn_logging(),
                               **kwargs)
        server = UvicornServer(config=config)
        if self.option("force_exit"):
            server.force_exit = True

        if isinstance(app, str) and (config.debug or config.reload):
            logger.warning(
                f"run@api_server.py - Running bountydns api in dev mode...")
            sock = config.bind_socket()
            supervisor = StatReload(config)
            return supervisor.run(server.run, sockets=[sock])
        elif config.workers > 1:
            sock = config.bind_socket()
            supervisor = Multiprocess(config)
            logger.warning(
                f"run@api_server.py - Running bountydns api in worker mode...")
            return supervisor.run(server.run, sockets=[sock])
        else:
            sockets = None
            logger.warning(
                f"run@api_server.py - Running bountydns api in standard mode..."
            )
            return await server.serve(sockets=sockets)
예제 #26
0
async def broadcast_index(websocket: WebSocket):
    try:
        logger.info("[email protected] - Accepting websockets")
        await websocket.accept()
    except Exception as e:
        logger.critical(
            f"[email protected] - Accept: Error: {str(type(e))}")
        logger.critical(
            f"[email protected] - Accept: Trace: {str(e)}")
        logger.critical(
            f"[email protected] - Accept:Not closing or unsubscribing"
        )
        return 1

    try:
        while True:
            logger.info("[email protected] - Receiving json")
            data = await websocket.receive_json()
            logger.info("[email protected] - Received json: " +
                        str(data))
            logger.info("[email protected] - Sending message: " +
                        str({"message": "greetings"}))
            await websocket.send_json({"message": "greetings"})
    except Exception as e:
        logger.critical(
            f"[email protected] - Receieve/Send: Error: {str(type(e))}"
        )
        logger.critical(
            f"[email protected] - Receieve/Send: Trace: {str(e)}")
        logger.critical(
            f"[email protected] - Receieve/Send: Not closing or unsubscribing"
        )
        return 1

    await websocket.close()