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