Beispiel #1
0
def test_scan_time(tags_to_apply,
                   get_configuration, configure_environment,
                   restart_syscheckd, wait_for_initial_scan):
    """ Check if there is a scan at a certain time

    scan_time option makes sure there is only one scan every 24 hours, at a certain time.

    * This test is intended to be used with valid configurations files. Each execution of this test will configure
    the environment properly, restart the service and wait for the initial scan.
    """
    check_apply_test(tags_to_apply, get_configuration['tags'])

    # Reformat given time to a readable format since it can be writen in several ways in ossec.conf
    scan_time = reformat_time(get_configuration['metadata']['scan_time'])
    current_time = datetime.now()

    # Calculate how much time we need to travel in time to make sure there hasn't been any scan until it is the given
    # time
    time_difference = (scan_time - current_time) if (scan_time - current_time).days == 0 else \
        ((scan_time - current_time) + timedelta(days=2))
    TimeMachine.travel_to_future(time_difference + timedelta(minutes=-30))
    with pytest.raises(TimeoutError):
        wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT, callback=callback_detect_end_scan)
    TimeMachine.travel_to_future(timedelta(minutes=31))
    wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT, callback=callback_detect_end_scan)
def test_create_file_scheduled(folder, name, filetype, content, checkers, tags_to_apply, get_configuration,
                               configure_environment, restart_syscheckd, wait_for_initial_scan):
    """ Checks if a special or regular file creation is detected by syscheck using scheduled monitoring

        Regular files must be monitored. Special files must not.

        :param folder: Name of the monitored folder
        :param name: Name of the file
        :param filetype: Type of the file
        :param content: Content of the file
        :param checkers: Checks that will compared to the ones from the event

        * This test is intended to be used with valid configurations files. Each execution of this test will configure
          the environment properly, restart the service and wait for the initial scan.
    """
    check_apply_test(tags_to_apply, get_configuration['tags'])

    # Create files
    create_file(filetype, folder, name, content=content)

    # Go ahead in time to let syscheck perform a new scan
    TimeMachine.travel_to_future(timedelta(hours=13))

    if filetype == REGULAR:
        # Wait until event is detected
        event = wazuh_log_monitor.start(
            timeout=DEFAULT_TIMEOUT, callback=callback_detect_event).result()
        validate_event(event, checkers)
    else:
        with pytest.raises(TimeoutError):
            wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT, callback=callback_detect_event)
Beispiel #3
0
def check_time_travel(time_travel):
    """Changes date and time of the system.

    :param time_travel boolean True if we need to update time, False otherwise
    """
    if time_travel:
        TimeMachine.travel_to_future(timedelta(hours=13))
def wait_for_event(fim_mode):
    """ Wait for the event to be scanned

    :param fim_mode: FIM mode (scheduled, realtime, whodata)
    """
    if fim_mode == 'scheduled':
        TimeMachine.travel_to_future(timedelta(hours=13))

    # Wait until event is detected
    wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT,
                            callback=callback_detect_event)
def check_restrict(directory, trigger, check_list, file_list, timeout,
                   scheduled):
    """ Standard restrict attribute test """

    create_file(REGULAR, directory, file_list[0], content='')
    if scheduled:
        TimeMachine.travel_to_future(timedelta(hours=13))
    while True:
        ignored_file = wazuh_log_monitor.start(
            timeout=timeout, callback=callback_restricted).result()
        if ignored_file == os.path.join(directory, file_list[0]):
            break
def test_scan_day_and_time(tags_to_apply, get_configuration,
                           configure_environment, restart_syscheckd,
                           wait_for_initial_scan):
    """ Check if there is a scan in a certain day and time

    This test must check both scan params.

    * This test is intended to be used with valid configurations files. Each execution of this test will configure
    the environment properly, restart the service and wait for the initial scan.
    """
    check_apply_test(tags_to_apply, get_configuration['tags'])

    day_of_week = {
        'monday': 0,
        'tuesday': 1,
        'wednesday': 2,
        'thursday': 3,
        'friday': 4,
        'saturday': 5,
        'sunday': 6
    }
    current_day = datetime.now()
    scan_day = day_of_week[get_configuration['metadata']['scan_day']]
    scan_time = reformat_time(get_configuration['metadata']['scan_time'])
    day_diff = scan_day - current_day.weekday()
    scan_today = False

    if day_diff < 0:
        day_diff %= 7
    elif day_diff == 0:
        scan_today = True

    scan_time = replace_date(scan_time, day_diff)

    if scan_today:
        if (scan_time - current_day).days == 0:
            TimeMachine.travel_to_future(scan_time - current_day +
                                         timedelta(minutes=1))
            wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT,
                                    callback=callback_detect_end_scan)
            return
        else:
            day_diff = 7

    if day_diff > 1:
        TimeMachine.travel_to_future(timedelta(days=day_diff - 1))
        current_day = datetime.now()
        with pytest.raises(TimeoutError):
            wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT,
                                    callback=callback_detect_end_scan)

    TimeMachine.travel_to_future(scan_time - current_day -
                                 timedelta(minutes=5))
    with pytest.raises(TimeoutError):
        wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT,
                                callback=callback_detect_end_scan)
    TimeMachine.travel_to_future(timedelta(minutes=6))
    wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT,
                            callback=callback_detect_end_scan)
Beispiel #7
0
def test_scan_day(tags_to_apply, get_configuration, configure_environment,
                  restart_syscheckd, wait_for_initial_scan):
    """ Check if there is a scan at a certain day of the week

    It will only scan once a week, on the given day.

    * This test is intended to be used with valid configurations files. Each execution of this test will configure
    the environment properly, restart the service and wait for the initial scan.
    """
    check_apply_test(tags_to_apply, get_configuration['tags'])

    day_of_week = {
        'monday': 0,
        'tuesday': 1,
        'wednesday': 2,
        'thursday': 3,
        'friday': 4,
        'saturday': 5,
        'sunday': 6
    }
    current_day = datetime.now().weekday()
    scan_day = day_of_week[get_configuration['metadata']['scan_day']]
    day_diff = scan_day - current_day

    if day_diff < 0:
        day_diff %= 7
    elif day_diff == 0:
        with pytest.raises(TimeoutError):
            wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT,
                                    callback=callback_detect_end_scan)
        return

    if day_diff > 1:
        TimeMachine.travel_to_future(timedelta(days=day_diff - 1))
        with pytest.raises(TimeoutError):
            wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT,
                                    callback=callback_detect_end_scan)
    TimeMachine.travel_to_future(timedelta(days=1))
    wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT,
                            callback=callback_detect_end_scan)
Beispiel #8
0
def test_sync_interval(get_configuration, configure_environment,
                       restart_syscheckd):
    """Verify that synchronization checks take place at the expected time given SYNC_INTERVAL variable.

    This test is intended to be used with valid configurations files. Each execution of this test will configure the
    environment properly and restart the service.
    """
    def truncate_log():
        truncate_file(LOG_FILE_PATH)
        return FileMonitor(LOG_FILE_PATH)

    # Check if the test should be skipped
    check_apply_test({'sync_interval'}, get_configuration['tags'])

    wazuh_log_monitor = truncate_log()
    detect_initial_scan(wazuh_log_monitor)
    wazuh_log_monitor.start(timeout=5,
                            callback=callback_detect_synchronization)

    wazuh_log_monitor = truncate_log()
    TimeMachine.travel_to_future(
        time_to_timedelta(get_configuration['metadata']['sync_interval']))
    wazuh_log_monitor.start(timeout=5,
                            callback=callback_detect_synchronization)

    # This should fail as we are only advancing half the time needed for synchronization to occur
    wazuh_log_monitor = truncate_log()
    TimeMachine.travel_to_future(
        time_to_timedelta(get_configuration['metadata']['sync_interval']) / 2)
    try:
        result = wazuh_log_monitor.start(
            timeout=1,
            callback=callback_detect_synchronization,
            accum_results=1).result()
        if result is not None:
            pytest.fail("Synchronization shouldn't happen at this point")
    except TimeoutError:
        return
Beispiel #9
0
def test_ignore_subdirectory(folder, filename, content, triggers_event,
                             tags_to_apply, get_configuration,
                             configure_environment, restart_syscheckd,
                             wait_for_initial_scan):
    """Checks files are ignored in subdirectory according to configuration. It also ensures that events for files that
    are not being ignored are still detected when using the ignore option.

    This test is intended to be used with valid configurations files. Each execution of this test will configure the
    environment properly, restart the service and wait for the initial scan.

    :param folder: Directory where the file is being created
    :param filename: Name of the file to be created
    :param content: Content to fill the new file
    :param triggers_event: True if an event must be generated, False otherwise
    :param tags_to_apply: Run test if matches with a configuration identifier, skip otherwise
    """
    check_apply_test(tags_to_apply, get_configuration['tags'])

    # Create text files
    create_file(REGULAR, folder, filename, content=content)

    if get_configuration['metadata']['fim_mode'] == 'scheduled':
        # Go ahead in time to let syscheck perform a new scan
        TimeMachine.travel_to_future(timedelta(hours=13))

    if triggers_event:
        event = wazuh_log_monitor.start(
            timeout=10, callback=callback_detect_event).result()
        assert event['data']['type'] == 'added', f'Event type not equal'
        assert event['data']['path'] == os.path.join(
            folder, filename), f'Event path not equal'
    else:
        while True:
            ignored_file = wazuh_log_monitor.start(
                timeout=10, callback=callback_ignore).result()
            if ignored_file == os.path.join(folder, filename):
                break
Beispiel #10
0
def test_frequency(folder, tags_to_apply, get_configuration,
                   configure_environment, restart_syscheckd,
                   wait_for_initial_scan):
    """ Checks if a non existing directory is monitored in realtime after the frequency time has passed

    Even with realtime monitoring, if we monitor a non existing directory and then we create it after restarting
    the service, syscheck won't detect anything from it until the scan restarts (using its frequency interval).

    :param folder: Directory that is being monitored

    * This test is intended to be used with valid configurations files. Each execution of this test will configure
    the environment properly, restart the service and wait for the initial scan.
    """
    check_apply_test(tags_to_apply, get_configuration['tags'])
    try:
        frequency = get_configuration['metadata']['frequency'] if 'frequency' in get_configuration['metadata'] \
            else 43200

        # Dont expect any event
        regular_file_cud(folder,
                         wazuh_log_monitor,
                         file_list=['regular'],
                         min_timeout=5,
                         triggers_event=False)

        # Travel in time as many seconds as frequency is set to
        TimeMachine.travel_to_future(timedelta(seconds=int(frequency)))

        # Expect events now
        regular_file_cud(folder,
                         wazuh_log_monitor,
                         file_list=['regular'],
                         min_timeout=DEFAULT_TIMEOUT,
                         triggers_event=True)
    finally:
        # Remove directory since it is not included in fixture
        shutil.rmtree(directory_str, ignore_errors=True)
def test_response_timeout(num_files, sync_interval, get_configuration,
                          configure_environment, restart_syscheckd):
    """Verify that synchronization checks take place at the expected time given SYNC_INTERVAL and RESPONSE_TIMEOUT
    parameters. To accomplish this a connection with a Wazuh Agent (Linux based) must be established via SSH using
    Paramiko. All operations will take place on the Agent side.

    This test is intended to be used with valid configurations files and requires a properly configured agent.

    :param num_files String Number of files to create within the test
    :param sync_interval String The value to the SYNC_INTERVAL variable. Must be a number with one of the following 
    units 's', 'm', 'h', 'd' or 'w'. If no unit is specified the default 's' will be used.
    """
    def overwrite_agent_conf_file():
        sync_cmd = "sudo sed -i 's|<sync_interval>.*|<sync_interval>" + str(
            sync_interval) + "</sync_interval>|g' /var/ossec/etc/ossec.conf"
        ssh.exec_command(sync_cmd)

        response_cmd = "sudo sed -i 's|<response_timeout>.*|<response_timeout>" + response_timeout + \
                       "</response_timeout>|g' /var/ossec/etc/ossec.conf"
        ssh.exec_command(response_cmd)

    def wait_agent_initial_scan(time_out=60):
        truncate_agent_log()
        start_time = datetime.now()
        while datetime.now() < start_time + timedelta(seconds=time_out):
            ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(
                "sudo cat /var/ossec/logs/ossec.log")
            for line in ssh_stdout.read().decode('ascii').splitlines():
                if callback_detect_end_scan(line):
                    return
        pytest.fail(
            "No 'File integrity monitoring scan ended.' was found on ossec.log."
        )

    def create_files_in_agent():
        ssh.exec_command("sudo systemctl stop wazuh-agent")
        ssh.exec_command("sudo mkdir " + DIR_NAME)
        ssh.exec_command("sudo touch " + DIR_NAME + "/testfile{0.." +
                         str(num_files) + "}")

        purge_manager_db()

        ssh.exec_command("sudo systemctl start wazuh-agent")

    def purge_manager_db():
        for proc in psutil.process_iter(attrs=['name']):
            if proc.name() == "wazuh-db":
                proc.kill()

        os.system("rm -f /var/ossec/queue/db/00{1..9}.db*")
        os.system("/var/ossec/bin/wazuh-db")
        time.sleep(5)

    def detect_synchronization_start(time_out=1):
        start_time = datetime.now()
        while datetime.now() < start_time + timedelta(seconds=time_out):
            ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(
                "sudo cat /var/ossec/logs/ossec.log")
            for line in ssh_stdout.read().decode('ascii').splitlines():
                if callback_detect_synchronization(line):
                    return extract_datetime(str(line))
        return None

    def wait_agent_dbsync_finish():
        previous_time = datetime.now()
        while datetime.now() - previous_time < timedelta(seconds=3):
            ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(
                "sudo cat /var/ossec/logs/ossec.log")
            for line in ssh_stdout.read().decode('ascii').splitlines():
                if "syscheck dbsync" in line:
                    previous_time = datetime.now()
            truncate_agent_log()
        return datetime.now()

    def wait_agent_integrity_control():
        previous_time = datetime.now()
        while datetime.now() - previous_time < timedelta(seconds=1):
            ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(
                "sudo cat /var/ossec/logs/ossec.log")
            for line in ssh_stdout.read().decode('ascii').splitlines():
                if "Sending integrity control message" in line:
                    previous_time = datetime.now()
                elif callback_detect_synchronization(line):
                    pytest.fail(
                        "No new synchronization process should start until `integrity control message` ends."
                    )
            truncate_agent_log()

    def truncate_agent_log():
        ssh.exec_command("sudo truncate -s 0 " + LOG_PATH)

    def extract_datetime(line):
        return datetime.strptime(line[0:19], '%Y/%m/%d %H:%M:%S')

    def update_agent_datetime():
        now = datetime.now()
        year = str(now.year)
        month = str(now.month)
        day = str(now.day)
        hour = str(now.hour)
        minute = str(now.minute)
        second = str(now.second)
        timedatectl_cmd = "sudo timedatectl set-time '" + year + "-" + month + "-" + day + " " + hour + ":" + minute + \
                          ":" + second + "'"
        ssh.exec_command(timedatectl_cmd)

    # Check if the test should be skipped
    check_apply_test({'response_timeout'}, get_configuration['tags'])

    # Variables
    LOG_PATH = "/var/ossec/logs/ossec.log"
    DIR_NAME = "/testdir1"
    AGENT_IP = "172.19.0.201"
    USERNAME = "******"
    PASSWORD = "******"
    response_timeout = get_configuration['metadata']['response_timeout']

    # Connect to the agent
    ssh = paramiko.SSHClient()
    ssh.load_system_host_keys()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname=AGENT_IP, username=USERNAME, password=PASSWORD)

    # Setup agent
    overwrite_agent_conf_file()
    update_agent_datetime()
    create_files_in_agent()
    wait_agent_initial_scan()

    # Check if first synchronization has started
    time_first_synchronization = detect_synchronization_start()
    if time_first_synchronization is None:
        pytest.fail("No synchronization was detected.")

    # Wait until synchronization ends
    time_after_dbsync = wait_agent_dbsync_finish()
    wait_agent_integrity_control()
    truncate_agent_log()

    # Determines when the next synchronization should occur
    max_next_synchronization = max(
        time_first_synchronization + time_to_timedelta(sync_interval),
        time_after_dbsync + time_to_timedelta(response_timeout))

    # Calculate a datetime when the next synchronization should NOT happen
    min_next_synchronization = min(
        time_first_synchronization + time_to_timedelta(sync_interval),
        time_after_dbsync + time_to_timedelta(response_timeout))

    # Travels in time to a datetime when synchronization should NOT happen, if needed
    if min_next_synchronization > datetime.now(
    ) and sync_interval != response_timeout:
        TimeMachine.travel_to_future(min_next_synchronization - datetime.now())
        update_agent_datetime()

    # Ensure there is no synchronization at this time.
    if detect_synchronization_start() is not None:
        pytest.fail("No synchronization should happen at this time.")

    # Travels in time to a datetime when synchronization MUST ocurr
    TimeMachine.travel_to_future(max_next_synchronization - datetime.now())
    update_agent_datetime()

    # Wait until next synchronization is detected
    if detect_synchronization_start(time_out=30) is None:
        pytest.fail("No synchronization was detected.")
Beispiel #12
0
def test_rename(folder, tags_to_apply, get_configuration, clean_directories,
                configure_environment, restart_syscheckd,
                wait_for_initial_scan):
    """ Checks if syscheckd detects events when renaming directories or files

        If we rename a directory or file, we expect 'deleted' and 'added' events.

        :param folder: Directory where the files will be created

        * This test is intended to be used with valid configurations files. Each execution of this test will configure
          the environment properly, restart the service and wait for the initial scan.
    """
    def expect_events(path):
        event = wazuh_log_monitor.start(
            timeout=DEFAULT_TIMEOUT, callback=callback_detect_event).result()
        try:
            assert 'added' in event['data']['type'] and path in event['data']['path'], \
                f'Deleted event not detected'
        except AssertionError:
            if 'deleted' not in event['data'][
                    'type'] and new_name not in event['data']['path']:
                raise AssertionError(
                    f'Wrong event when renaming a non empty directory')

    check_apply_test(tags_to_apply, get_configuration['tags'])

    scheduled = get_configuration['metadata']['fim_mode'] == 'scheduled'
    create_file(REGULAR, folder, old_name, content='')
    check_time_travel(scheduled)
    wazuh_log_monitor.start(timeout=DEFAULT_TIMEOUT,
                            callback=callback_detect_event)

    # testdir1 will have renamed files within. testdir2 will be renamed with files within
    if folder == testdir1:
        # Change the file name
        os.rename(os.path.join(folder, old_name),
                  os.path.join(folder, new_name))
        check_time_travel(scheduled)
        # Expect deleted and created events
        deleted = wazuh_log_monitor.start(
            timeout=DEFAULT_TIMEOUT, callback=callback_detect_event).result()
        try:
            assert 'deleted' in deleted['data']['type'] and os.path.join(
                folder, old_name) in deleted['data']['path']
        except AssertionError:
            if 'added' not in deleted['data']['type'] and os.path.join(
                    folder, old_name) not in deleted['data']['path']:
                raise AssertionError(f'Wrong event when renaming a file')

        added = wazuh_log_monitor.start(
            timeout=DEFAULT_TIMEOUT, callback=callback_detect_event).result()
        try:
            assert 'added' in added['data']['type'] and os.path.join(
                folder, new_name) in added['data']['path']
        except AssertionError:
            if 'deleted' not in added['data']['type'] and os.path.join(
                    folder, new_name) not in added['data']['path']:
                raise AssertionError(f'Wrong event when renaming a file')
    else:
        os.rename(folder, os.path.join(os.path.dirname(folder), new_name))
        check_time_travel(scheduled)
        expect_events(new_name)
        # Travel in time to force delete event in realtime/whodata
        if get_configuration['metadata']['fim_mode'] != 'scheduled':
            TimeMachine.travel_to_future(timedelta(hours=13))
        expect_events(folder)