예제 #1
0
def add_firewall_rule(fw_rule):
    # ensuring all necessary fields are present in the namespace before continuing.
    valid_fields = [
        'action', 'dst_ip', 'dst_netmask', 'dst_port', 'position', 'protocol',
        'src_ip', 'src_netmask', 'tab', 'zone'
    ]
    if not all([hasattr(fw_rule, x) for x in valid_fields]):
        raise ValidationError('Invalid form.')

    # grabbing list of configured iptable rules for the specified chain.
    output = run(f'sudo iptables -nL {fw_rule.zone} --line-number',
                 shell=True,
                 capture_output=True).stdout.splitlines()[1:]

    rule_count = len(output)
    fw_rule.position = convert_int(fw_rule.position)
    if (not rule_count and fw_rule.position != 1):
        raise ValidationError('First firewall rule must have position 1.')

    if (not 0 < fw_rule.position <= rule_count + 1):
        raise ValidationError(
            f'Position outside of valid range. (1-{rule_count+1})')

    if (fw_rule.protocol not in ['any', 'tcp', 'udp', 'icmp']):
        raise ValidationError('Network protocol is not valid.')

    if (fw_rule.protocol in ['any', 'icmp'] and fw_rule.dst_port):
        raise ValidationError('Only TCP/UDP use destination port field.')
예제 #2
0
def syslog_dropdown(syslog_time):
    syslog_time = convert_int(syslog_time)
    if (syslog_time):
        raise ValidationError('Dropdown values must be an integer.')

    if (syslog_time not in [5, 10, 60]):
        raise ValidationError('Dropdown values can only be 5, 10, or 60.')
예제 #3
0
def set_dhcp_reservation(dhcp_settings, action):
    with ConfigurationManager('dhcp_server') as dnx:
        dhcp_server_settings = dnx.load_configuration()

        leases = dhcp_server_settings['leases']
        reservations = dhcp_server_settings['reservations']
        reserved_ips = set(
            [info['ip_address'] for info in reservations.values()])

        if (action is CFG.ADD):

            # preventing reservations being created for ips with an active dhcp lease
            if (dhcp_settings['ip'] in leases):
                raise ValidationError(
                    f'There is an active lease with {dhcp_settings["ip"]}. Clear the lease and try again.'
                )

            # ensuring mac address and ip address are unique
            if (dhcp_settings['mac'] in reservations
                    or dhcp_settings['ip'] in reserved_ips):
                raise ValidationError(
                    f'{dhcp_settings["ip"]} is already reserved.')

            reservations.update({
                dhcp_settings['mac']: {
                    'zone': dhcp_settings['zone'],
                    'ip_address': dhcp_settings['ip'],
                    'description': dhcp_settings['description']
                }
            })

        elif (action is CFG.DEL):
            reservations.pop(dhcp_settings['mac'], None)

        dnx.write_configuration(dhcp_server_settings)
예제 #4
0
def log_settings(log_settings):
    if (log_settings['length'] not in [30, 45, 60, 90]):
        raise ValidationError('Invalid log settings.')

    try:
        LOG(log_settings['level'])
    except ValueError:
        raise ValidationError('Invalid log settings.')
예제 #5
0
def default_gateway(ip_addr):
    try:
        ip_addr = IPv4Address(ip_addr)
    except:
        raise ValidationError('Default gateway is not valid.')

    if (ip_addr.is_loopback):
        raise ValidationError('Default gateway cannot be 127.0.0.1/loopback.')
예제 #6
0
def dns_record_remove(dns_record_name):
    dns_server = load_configuration('dns_server')

    if (dns_record_name == 'dnx.firewall'):
        raise ValidationError('Cannot remove dnxfirewall dns record.')

    if (dns_record_name not in dns_server['records']):
        raise ValidationError(INVALID_FORM)
예제 #7
0
def _ip_address(ip_addr):
    try:
        ip_addr = IPv4Address(ip_addr)
    except:
        raise ValidationError('IP address is not valid.')

    if (ip_addr.is_loopback):
        raise ValidationError(
            '127.0.0.0/24 is reserved ip space and cannot be used.')
예제 #8
0
def domain_category_keywords(categories):
    dns_proxy = load_configuration('dns_proxy')

    domain_cats = dns_proxy['categories']['default']
    for cat in categories:
        if (cat not in domain_cats):
            raise ValidationError(INVALID_FORM)

        if (not domain_cats[cat]['enabled']):
            raise ValidationError(INVALID_FORM)
예제 #9
0
def main_services(services_form):
    valid_services = [
        'dnx-dns-proxy', 'dnx-fw-proxy', 'dnx-dhcp-server', 'dnx-updates'
    ]
    service = services_form['service']
    ruleset = services_form['ruleset']

    if (service not in valid_services):
        raise ValidationError(INVALID_FORM)

    if (service in ['dnx-dns-proxy', 'dnx-ip-proxy'] and ruleset is None):
        raise ValidationError(INVALID_FORM)
예제 #10
0
def portscan_settings(portscan_settings):
    ips = load_configuration('ips')

    current_prevention = ips['port_scan']['enabled']
    for item in portscan_settings:
        if (item not in ['enabled', 'reject']):
            raise ValidationError(INVALID_FORM)

    if ('reject' in portscan_settings and 'drop' not in portscan_settings
            and not current_prevention):
        raise ValidationError(
            'Prevention must be enabled to configure portscan reject.')
예제 #11
0
def dns_over_tls(dns_tls_settings):
    dns_server = load_configuration('dns_server')

    current_tls = dns_server['tls']['enabled']
    for item in dns_tls_settings['enabled']:
        if (item not in ['dns_over_tls', 'udp_fallback']):
            raise ValidationError(INVALID_FORM)

    # NOTE: current_tls shouldnt matter since tls will be in form if enabled regardless
    if (not current_tls and 'udp_fallback' in dns_tls_settings['enabled']
            and 'dns_over_tls' not in dns_tls_settings['enabled']):
        raise ValidationError(
            'DNS over TLS must be enabled to configure UDP fallback.')
예제 #12
0
def geolocation(region, rtype='country'):
    region['cfg_dir'] = convert_int(region['cfg_dir'])
    if (region['cfg_dir'] not in range(4)):
        raise ValidationError(INVALID_FORM)

    if (rtype == 'country'):
        valid_regions = load_configuration('ip_proxy')['geolocation']

    elif (rtype == 'continent'):
        valid_regions = load_configuration('geolocation',
                                           filepath='dnx_webui/data')

    if region[rtype] not in valid_regions:
        raise ValidationError(INVALID_FORM)
예제 #13
0
def proto_port(port_str):

    try:
        proto, port = port_str.split('/')
    except:
        raise ValidationError(
            'Invalid protocol/port definition. ex tcp/80 or udp/500-550')

    proto_int = _proto_map.get(proto, None)
    if (proto_int is None):
        raise ValidationError('Invalid protocol. Use [any, tcp, udp, icmp].')

    # ensuring icmp definitions conform to required format.
    if (proto_int == PROTO.ICMP and convert_int(port) != 0):
        raise ValidationError('ICMP does not support ports. Use icmp/0.')

    # splitting str after the "/" on "-" which is port range operator. this will make range or singular definition
    # handling the same.
    ports = [convert_int(p) for p in port.split('-', 1)]

    if (len(ports) == 2):
        if (ports[0] > ports[1]):
            raise ValidationError(
                'Invalid port range. The start value must be less than the end. ex. 9001-9002'
            )

        error = f'TCP/UDP port range must be between within range 1-65535 or 0 for any. ex tcp/500-550, udp/0'

    else:
        # this puts single port in range syntax
        ports.append(ports[0])

        error = f'TCP/UDP port must be between 1-65535. ex udp/9001'

    # converting 0 port values to cover full range (0 is an alias for any). ICMP will not be converted to ensure
    # compatibility between icmp service definition vs any service. Any protocol will not be converted for same reason.
    if (proto_int not in [PROTO.ICMP, PROTO.ANY]):
        ports[0] = ports[0] if ports[0] != 0 else 1

    # expanding the range out for any. this does not cause issues with icmp since it does not use ports so the second
    # value in a port range is is N/A for icmp, but in this case just letting it do what the others do.
    ports[1] = ports[1] if ports[1] != 0 else 65535

    for port in ports:

        # port 0 is used by icmp. if 0 is used outside of icmp it gets converted to a range.
        if (port not in range(65536)):
            raise ValidationError(error)

    return proto_int, ports
예제 #14
0
def time_offset(offset_settings):
    dir_offset = offset_settings['direction']
    if (dir_offset not in [' ', '-', '+']):
        raise ValidationError('Invalid time offset sign.')

    time_offset = offset_settings['time']
    if (time_offset not in range(0, 15)):
        raise ValidationError('Invalid time offset value.')

    if (dir_offset == ' ' and time_offset != 0):
        raise ValidationError(
            'Direction cannot be empty if amount is not zero.')

    elif (dir_offset == '-' and time_offset in [13, 14]):
        raise ValidationError('Invalid timezone/ time offset.')
예제 #15
0
def ip_proxy_settings(ip_hosts_settings, *, ruleset='reputation'):
    ip_proxy = load_configuration('ip_proxy')

    valid_categories = ip_proxy[ruleset]
    for category in ip_hosts_settings:
        try:
            category, direction = category[:-2], category[-1]
        except:
            raise ValidationError(INVALID_FORM)

        if (category not in valid_categories):
            raise ValidationError(INVALID_FORM)

        direction = convert_int(direction)
        if (direction not in range(4)):
            raise ValidationError(INVALID_FORM)
예제 #16
0
def dhcp_general_settings(server_settings):
    if (server_settings['interface'] not in ['lan', 'dmz']):
        raise ValidationError('Invalid interface referenced.')

    lease_range = server_settings['lease_range']

    # clamping range into lan/dmz class C's. this will have to change later if more control over interface
    # configurations is implemented.
    for field in lease_range.values():

        if (field not in range(2, 255)):
            raise ValidationError('DHCP ranges must be between 2 and 254.')

    if (lease_range['start'] >= lease_range['end']):
        raise ValidationError(
            'DHCP pool start value must be less than the end value.')
예제 #17
0
def password(password):
    # calculating the length
    if (len(password) < 8):
        raise ValidationError(
            'Password does not meet length requirement of 8 characters.')

    criteria = (
        re.search(r'\d', password),
        re.search(r'[A-Z]', password),  # searching for digits & uppercase
        re.search(r'[a-z]', password),
        re.search(r'\W', password)  # searching for lowercase & symbols
    )

    if not all(criteria):
        raise ValidationError(
            'Password does not meet complexity requirements.')
예제 #18
0
def time_restriction(tr_settings):
    tr_hour = convert_int(tr_settings['hour'])
    tr_min = convert_int(tr_settings['minutes'])

    if (tr_hour not in range(1, 13) or tr_min not in [00, 15, 30, 45]):
        raise ValidationError('Restriction settings are not valid.')

    tr_hour_len = convert_int(tr_settings['length_hour'])
    tr_min_len = convert_int(tr_settings['length_minutes'])

    if (tr_hour_len not in range(1, 13)
            and tr_min_len not in [00, 15, 30, 45]):
        raise ValidationError('Restriction settings are not valid.')

    if (tr_settings['suffix'] not in ['AM', 'PM']):
        raise ValidationError('Restriction settings are not valid.')
예제 #19
0
 def _remove_configuration(name):
     try:
         os.remove(f'{HOME_DIR}/dnx_system/config_backups/{name}.tar')
     except FileNotFoundError:
         raise ValidationError(
             f'{name} is not a valid file. reload page to see current backups.'
         )
예제 #20
0
    def _restore_configuration(name):

        # restoring system default consists of deleting all usr config files
        if (name == 'system_default'):
            usr_cfg_file_path = f'{HOME_DIR}/dnx_system/data/usr'

            for file in os.listdir(usr_cfg_file_path):

                # NOTE: IMPORTANT. ensuring database does not get removed
                if (file.endswith('.sqlite3') or file == 'temp'): continue

                # exception to protect process just in case it is being removed by another user at the same time.
                try:
                    os.remove(f'{usr_cfg_file_path}/{file}')
                except FileNotFoundError:
                    pass

            Log.simple_write(LOG_NAME, 'notice',
                             f'configuration restored to system defaults')

        else:  #
            try:
                with tarfile.open(
                        f'{HOME_DIR}/dnx_system/config_backups/{name}.tar',
                        'r') as tar:
                    tar.extractall(path=f'{HOME_DIR}/dnx_system/data/usr/')
            except:
                raise ValidationError(
                    'Error while loading configuration. has the file been removed?'
                )

            Log.simple_write(LOG_NAME, 'notice',
                             f'configuration restored from file [{name}]')
예제 #21
0
def domain_categories(categories, ruleset):
    if (ruleset == 'default' and
            not all(['malicious' in categories, 'cryptominer' in categories])):
        raise ValidationError(
            'Malicious and cryptominer categories cannot be disabled.')

    dns_proxy = load_configuration('dns_proxy')
    if (ruleset in ['default', 'user_defined']):
        cat_list = dns_proxy['categories'][ruleset]

    elif (ruleset in ['tlds']):
        cat_list = dns_proxy['tlds']

    for category in categories:
        if category not in cat_list:
            raise ValidationError(INVALID_FORM)
예제 #22
0
def standard(user_input, *, override=[]):
    for char in user_input:
        if (not char.isalnum() and char not in override):
            override = ', '.join(override)

            # TODO: F**K ENGLISH. MAKE THIS MAKE SENSE PLEASE GOD. F**K.
            raise ValidationError(
                f'Standard fields can only contain alpha numeric {override}.')
예제 #23
0
def ip_network(ip_netw):
    '''take ip network string, validates, then returns ip network string. the return string will always be the network
    id of the subnet.'''
    try:
        ip_netw = IPv4Network(ip_netw)
    except:
        raise ValidationError('IP network is not valid.')

    return int(ip_netw.network_address), ip_netw.prefixlen
예제 #24
0
def add_ip_whitelist(whitelist_settings):
    # handling alphanum check. will raise exception if invalid.
    standard(whitelist_settings['user'])

    if (whitelist_settings['type'] not in ['global', 'tor']):
        raise ValidationError(INVALID_FORM)

    # if ip is valid this will return, otherwise a ValidationError will be raised.
    _ip_address(whitelist_settings['user'])
예제 #25
0
def del_firewall_rule(fw_rule):
    output = run(f'sudo iptables -nL {fw_rule.zone} --line-number',
                 shell=True,
                 capture_output=True).stdout.splitlines()

    rule_count = len(output) + 2
    if (convert_int(fw_rule.position) not in range(1, rule_count)):
        raise ValidationError(
            'Selected rule is not valid and cannot be removed.')
예제 #26
0
def add_snat_rule(nat_rule):
    # ensuring all necessary fields are present in the namespace before continuing.
    valid_fields = [
        'src_zone',
        'orig_src_ip',
        'new_src_ip',
    ]
    if not all([hasattr(nat_rule, x) for x in valid_fields]):
        raise ValidationError('Invalid form.')
예제 #27
0
def remove_dhcp_lease(ip_addr):
    with ConfigurationManager('dhcp_server') as dnx:
        dhcp_leases = dnx.load_configuration()

        leases = dhcp_leases['leases']

        if not leases.pop(ip_addr, None):
            raise ValidationError(INVALID_FORM)

        dnx.write_configuration(dhcp_leases)
예제 #28
0
def ip_address(ip_addr=None, *, ip_iter=None):
    ip_iter = [] if not ip_iter else ip_iter
    if (not isinstance(ip_iter, list)):
        return ValidationError('Data format must be a list.')

    if (ip_addr):
        ip_iter.append(ip_addr)

    for ip in ip_iter:
        _ip_address(ip)
예제 #29
0
def update_custom_category(category, *, action):
    with ConfigurationManager('dns_proxy') as dnx:
        custom_category_lists = dnx.load_configuration()

        ud_cats = custom_category_lists['categories']['user_defined']
        if (action is CFG.DEL and category != 'enabled'):
            ud_cats.pop(category, None)

        elif (action is CFG.ADD):
            if (len(ud_cats) >= 6):
                raise ValidationError(
                    'Only support for maximum of 6 custom categories.')

            elif (category in ud_cats):
                raise ValidationError('Custom category already exists.')

            ud_cats[category] = {'enabled': False}

        dnx.write_configuration(custom_category_lists)
예제 #30
0
def management_access(fields):
    SERVICE_TO_PORT = {
        'webui': (80, 443),
        'cli': (0, ),
        'ssh': (22, ),
        'ping': 1
    }

    if (fields.zone not in ['lan', 'dmz']
            or fields.service not in ['webui', 'cli', 'ssh', 'ping']):
        raise ValidationError(INVALID_FORM)

    # convert_int will return -1  if issues with form data and ValueError will cover
    # invalid CFG action key/vals
    try:
        action = CFG(convert_int(fields.action))
    except ValueError:
        raise ValidationError(INVALID_FORM)

    fields.action = action
    fields.service_ports = SERVICE_TO_PORT[fields.service]