Files @ 2c24e973d44a
Branch filter:

Location: majic-ansible-roles/roles/backup_server/molecule/default/tests/test_parameters_optional.py

branko
MAR-192: Added support for Debian 12 Bookworm to backup_server role:

- Use test parametrisation instead of looping over a list when testing
correct key usage.
- Replace deprecated key algorithm (in Debian 12 Bookworm) for test
purposes (it just needs to be one of the RSA variants).
import os

import pytest
import testinfra.utils.ansible_runner


testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('parameters-optional')


def test_backup_client_users_and_groups(host):
    """
    Tests if the system groups and users for backup clients have been set-up
    correctly.
    """

    with host.sudo():

        client1_group = host.group('bak-client1_backup')
        assert client1_group.exists
        assert client1_group.gid < 1000

        client1_user = host.user('bak-client1_backup')
        assert client1_user.exists
        assert client1_user.group == 'bak-client1_backup'
        assert sorted(client1_user.groups) == sorted(['bak-client1_backup', 'backup'])
        assert client1_user.home == '/srv/backups/client1.backup'
        assert client1_user.uid < 1000
        assert client1_user.password == '!'

        client2_group = host.group('bak-client2-backup')
        assert client2_group.exists
        assert client2_group.gid == 5001

        client2_user = host.user('bak-client2-backup')
        assert client2_user.exists
        assert client2_user.group == 'bak-client2-backup'
        assert sorted(client2_user.groups) == sorted(['bak-client2-backup', 'backup'])
        assert client2_user.home == '/srv/backups/client2-backup'
        assert client2_user.uid == 5001
        assert client2_user.password == '!'


def test_backup_client_home_directories(host):
    """
    Tests if the home directory structure has been set-up correctly for the
    backup client system user.
    """

    with host.sudo():

        client1_user = host.user('bak-client1_backup')

        client1_user_home = host.file(client1_user.home)
        assert client1_user_home.is_directory
        assert client1_user_home.user == 'root'
        assert client1_user_home.group == 'bak-client1_backup'
        assert client1_user_home.mode == 0o750

        client1_user_duplicity = host.file(os.path.join(client1_user.home, 'duplicity'))
        assert client1_user_duplicity.is_directory
        assert client1_user_duplicity.user == 'bak-client1_backup'
        assert client1_user_duplicity.group == 'bak-client1_backup'
        assert client1_user_duplicity.mode == 0o770

        client1_user_ssh = host.file(os.path.join(client1_user.home, '.ssh'))
        assert client1_user_ssh.is_directory
        assert client1_user_ssh.user == 'root'
        assert client1_user_ssh.group == 'root'
        assert client1_user_ssh.mode == 0o751

        # This verifies /etc/skel was not used for setting-up home.
        assert not host.file(os.path.join(client1_user.home, '.bashrc')).exists

        client2_user = host.user('bak-client2-backup')

        client2_user_home = host.file(client2_user.home)
        assert client2_user_home.is_directory
        assert client2_user_home.user == 'root'
        assert client2_user_home.group == 'bak-client2-backup'
        assert client2_user_home.mode == 0o750

        client2_user_duplicity = host.file(os.path.join(client2_user.home, 'duplicity'))
        assert client2_user_duplicity.is_directory
        assert client2_user_duplicity.user == 'bak-client2-backup'
        assert client2_user_duplicity.group == 'bak-client2-backup'
        assert client2_user_duplicity.mode == 0o770

        client2_user_ssh = host.file(os.path.join(client2_user.home, '.ssh'))
        assert client2_user_ssh.is_directory
        assert client2_user_ssh.user == 'root'
        assert client2_user_ssh.group == 'root'
        assert client2_user_ssh.mode == 0o751

        # This verifies /etc/skel was not used for setting-up home.
        assert not host.file(os.path.join(client2_user.home, '.bashrc')).exists


def test_backup_client_authorized_keys(host):
    """
    Tests if the authorized keys for backup client system user have been set-up
    correctly.
    """

    with host.sudo():

        client1_user = host.user('bak-client1_backup')

        client1_user_authorized_keys = host.file(os.path.join(client1_user.home, '.ssh', 'authorized_keys'))
        assert client1_user_authorized_keys.is_file
        assert client1_user_authorized_keys.user == 'root'
        assert client1_user_authorized_keys.group == 'bak-client1_backup'
        assert client1_user_authorized_keys.mode == 0o640
        assert client1_user_authorized_keys.content_string == open('tests/data/ssh/client1.pub', 'r').read()

        client2_user = host.user('bak-client2-backup')

        client2_user_authorized_keys = host.file(os.path.join(client2_user.home, '.ssh', 'authorized_keys'))
        assert client2_user_authorized_keys.is_file
        assert client2_user_authorized_keys.user == 'root'
        assert client2_user_authorized_keys.group == 'bak-client2-backup'
        assert client2_user_authorized_keys.mode == 0o640
        assert client2_user_authorized_keys.content_string == open('tests/data/ssh/client2.pub', 'r').read()


def test_firewall_configuration(host):
    """
    Tests if the firewall configuration file has been deployed correctly.
    """

    with host.sudo():

        firewall_config = host.file('/etc/ferm/conf.d/40-backup.conf')

        assert firewall_config.is_file
        assert firewall_config.user == 'root'
        assert firewall_config.group == 'root'
        assert firewall_config.mode == 0o640
        assert 'saddr ( 192.168.56.1 192.168.56.3) @subchain "backup_in" {' in firewall_config.content_string


@pytest.mark.usefixtures("prepare_ssh_client_private_key_permissions")
def test_regular_ssh_server_inaccessible(host):
    """
    Tests if the default SSH server is inaccessible for the backup client system
    users.
    """

    # Extract first non-IPv6 IP. Crude test, but it should work.
    remote_ip = next(a for a in host.interface("eth1").addresses if ":" not in a)
    local = host.get_host("local://")

    # Test connectivity towards regular ssh (should fail).
    login_attempt = local.run("ssh "
                              "-o PasswordAuthentication=no "
                              "-o StrictHostKeyChecking=no "
                              "-o UserKnownHostsFile=/dev/null "
                              "-i tests/data/ssh/client1 "
                              "bak-client1_backup@%s "
                              "/bin/echo sshtest" % remote_ip)
    assert login_attempt.rc != 0
    assert "bad permissions" not in login_attempt.stderr  # Avoid passing test due to client private key having wrong permissions.
    assert "Permission denied (publickey)" in login_attempt.stderr

    login_attempt = local.run("ssh "
                              "-o PasswordAuthentication=no "
                              "-o StrictHostKeyChecking=no "
                              "-o UserKnownHostsFile=/dev/null "
                              "-i tests/data/ssh/client2 "
                              "bak-client2-backup@%s "
                              "/bin/echo sshtest" % remote_ip)
    assert login_attempt.rc != 0
    assert "bad permissions" not in login_attempt.stderr  # Avoid passing test due to client private key having wrong permissions.
    assert "Permission denied (publickey)" in login_attempt.stderr


@pytest.mark.usefixtures("prepare_ssh_client_private_key_permissions")
def test_backup_ssh_service_connectivity(host):
    """
    Tests if SFTP (only) is availavble to system users used by backup clients.
    """

    # Extract first non-IPv6 IP. Crude test, but it should work.
    remote_ip = next(a for a in host.interface("eth1").addresses if ":" not in a)

    local = host.get_host("local://")

    # Test connectivity towards dedicated ssh (should be allowed, but only for sftp).
    login_attempt = local.run("ssh -p 2222 "
                              "-o PasswordAuthentication=no "
                              "-o StrictHostKeyChecking=no "
                              "-o UserKnownHostsFile=/dev/null "
                              "-i tests/data/ssh/client1 "
                              "bak-client1_backup@%s /bin/echo sshtest" % remote_ip)
    assert login_attempt.rc == 1
    assert "This service allows sftp connections only." in login_attempt.stdout

    # Test connectivity towards dedicated ssh (should be allowed, but only for sftp).
    login_attempt = local.run("ssh -p 2222 "
                              "-o PasswordAuthentication=no "
                              "-o StrictHostKeyChecking=no "
                              "-o UserKnownHostsFile=/dev/null "
                              "-i tests/data/ssh/client2 "
                              "bak-client2-backup@%s /bin/echo sshtest" % remote_ip)
    assert login_attempt.rc == 1
    assert "This service allows sftp connections only." in login_attempt.stdout


@pytest.mark.usefixtures("prepare_ssh_client_private_key_permissions")
@pytest.mark.parametrize('key_algorithm', [
    'rsa-sha2-512',
    'ssh-ed25519',
    'ecdsa-sha2-nistp256',
])
def test_backup_ssh_service_key_fingerprints(host, key_algorithm):
    """
    Tests fingerprints of backup SSH server in order to ensure correct keys are
    in use.
    """

    # Extract first non-IPv6 IP. Crude test, but it should work.
    remote_ip = next(a for a in host.interface("eth1").addresses if ":" not in a)

    local = host.get_host("local://")

    login_attempt = local.run("ssh -p 2222 "
                              "-o PasswordAuthentication=no "
                              "-o StrictHostKeyChecking=yes "
                              "-o UserKnownHostsFile=tests/data/ssh/known_hosts "
                              "-i tests/data/ssh/client1 "
                              "-o HostKeyAlgorithms=%s "
                              "bak-client1_backup@%s /bin/echo sshtest" % (key_algorithm, remote_ip))
    assert login_attempt.rc == 1
    assert "This service allows sftp connections only." in login_attempt.stdout