#! /usr/bin/env perl # vim: set filetype=perl ts=4 sw=4 sts=4 et: use common::sense; use Test::More; use Test::Deep; use File::Basename; use lib dirname(__FILE__) . '/../../lib/perl'; use OVH::Bastion; use OVH::Result; use JSON; OVH::Bastion::enable_mocking(); OVH::Bastion::set_mock_data( { "accounts" => { "me" => { "uid" => 99982, "gid" => 99982, "personal_accesses" => [qw{ me@1.2.3.4 }], "legacy_accesses" => [qw{ me@1.2.3.5 }], "guest_accesses" => { "group2" => [qw{ group1@9.9.9.9 }], } }, "wildcard" => { "uid" => 99981, "gid" => 99981, "personal_accesses" => [qw{ 0.0.0.0/0 }], }, }, "groups" => { "group1" => { "members" => [qw{ me }], "accesses" => [qw{ group1@0.0.0.0/0 }], }, "group2" => {} }, } ); OVH::Bastion::load_configuration( mock_data => { ingressToEgressRules => [ [["10.19.0.0/16", "10.15.15.0/24"], ["10.20.0.0/16"], "ALLOW-EXCLUSIVE"], [["192.168.42.0/24"], ["192.168.42.0/24"], "ALLOW"], [["192.168.0.0/16"], ["192.168.0.0/16"], "DENY"] ], bastionName => "mock", idleLockTimeout => 17, idleKillTimeout => 29, # all options below are bool, we'll test for their normalization enableSyslog => 1, enableGlobalAccessLog => JSON::true, enableAccountAccessLog => "yes", enableGlobalSqlLog => 0, enableAccountSqlLog => JSON::false, displayLastLogin => "", debug => JSON::null, passwordAllowed => "no", telnetAllowed => "false", } ); # TESTS my $fnret; $fnret = OVH::Bastion::build_ttyrec_cmdline( ip => "127.0.0.1", port => 7979, user => "randomuser", account => "bastionuser", uniqid => 'cafed00dcafe', home => "/home/randomuser", ); cmp_deeply( $fnret->value->{'saveFile'}, re( qr{^\Q/home/randomuser/ttyrec/127.0.0.1/20\E\d\d-\d\d-\d\d.\d\d\-\d\d\-\d\d\.\d{6}\Q.cafed00dcafe.bastionuser.randomuser.127.0.0.1.7979.ttyrec\E$} ), "build_ttyrec_cmdline saveFile" ); cmp_deeply( $fnret->value->{'cmd'}, [ 'ttyrec', '-f', $fnret->value->{'saveFile'}, '-F', '/home/randomuser/ttyrec/127.0.0.1/%Y-%m-%d.%H-%M-%S.#usec#.cafed00dcafe.bastionuser.randomuser.127.0.0.1.7979.ttyrec', '-t', 17, '-s', "To unlock, use '--osh unlock' from another console", '-k', 29, ], "build_ttyrec_cmdline cmd" ); $fnret = OVH::Bastion::build_ttyrec_cmdline_part1of2( ip => "127.0.0.1", port => 7979, user => "randomuser", account => "bastionuser", uniqid => 'cafed00dcafe', home => "/home/randomuser", ); cmp_deeply( $fnret->value->{'saveFile'}, re( qr{^\Q/home/randomuser/ttyrec/127.0.0.1/20\E\d\d-\d\d-\d\d.\d\d\-\d\d\-\d\d\.\d{6}\Q.cafed00dcafe.bastionuser.randomuser.127.0.0.1.7979.ttyrec\E$} ), "build_ttyrec_cmdline_part1of2 saveFile" ); cmp_deeply( $fnret->value->{'cmd'}, [ 'ttyrec', '-f', $fnret->value->{'saveFile'}, '-F', '/home/randomuser/ttyrec/127.0.0.1/%Y-%m-%d.%H-%M-%S.#usec#.cafed00dcafe.bastionuser.randomuser.127.0.0.1.7979.ttyrec' ], "build_ttyrec_cmdline_part1of2 cmd" ); $fnret = OVH::Bastion::build_ttyrec_cmdline_part2of2( input => $fnret->value, idleKillTimeout => 88, idleLockTimeout => 99, ); cmp_deeply( $fnret->value->{'saveFile'}, re( qr{^\Q/home/randomuser/ttyrec/127.0.0.1/20\E\d\d-\d\d-\d\d.\d\d\-\d\d\-\d\d\.\d{6}\Q.cafed00dcafe.bastionuser.randomuser.127.0.0.1.7979.ttyrec\E$} ), "build_ttyrec_cmdline_part2of2 saveFile" ); cmp_deeply( $fnret->value->{'cmd'}, [ 'ttyrec', '-f', $fnret->value->{'saveFile'}, '-F', '/home/randomuser/ttyrec/127.0.0.1/%Y-%m-%d.%H-%M-%S.#usec#.cafed00dcafe.bastionuser.randomuser.127.0.0.1.7979.ttyrec', '-t', 99, '-s', "To unlock, use '--osh unlock' from another console", '-k', 88, ], "build_ttyrec_cmdline_part2of2 cmd" ); is(OVH::Bastion::config("bastionName")->value, "mock", "bastion name is mocked"); ok(OVH::Bastion::is_account_valid(account => "azerty")->is_ok, "is_account_valid('azerty')"); is(OVH::Bastion::is_account_valid(account => "in valid")->err, "KO_FORBIDDEN_CHARS", "is_account_valid('in valid')"); for my $suffix (qw{ tty aclkeeper gatekeeper owner }) { is(OVH::Bastion::is_account_valid(account => "account-$suffix")->err, "KO_FORBIDDEN_SUFFIX", "is_account_valid('account-$suffix')"); } is(OVH::Bastion::is_account_valid(account => "root")->err, "KO_FORBIDDEN_NAME", "is_account_valid('root')"); ok(OVH::Bastion::is_bastion_account_valid_and_existing(account => "me")->is_ok, "is_bastion_account_valid_and_existing('me')"); is_deeply( OVH::Bastion::is_access_granted( account => "me", user => "remote", ipfrom => "1.2.3.4", ip => "5.6.7.8", port => "9876" ), R('KO_ACCESS_DENIED', msg => 'Access denied for me to remote@5.6.7.8:9876'), "is_access_granted(me) on denied machine" ); ok( OVH::Bastion::is_access_granted( account => "me", user => "me", ipfrom => "1.1.1.1", ip => "1.2.3.4", port => "9876" )->is_ok, "is_access_granted(me) on allowed machine" ); is( OVH::Bastion::is_access_granted( account => "wildcard", user => "root", ipfrom => "10.15.15.15", ip => "1.2.3.4", port => "9876" )->err, "KO_ACCESS_DENIED", "is_access_granted(wildcard) on disallowed machine due to ingressToEgressRules #1" ); is( OVH::Bastion::is_access_granted( account => "wildcard", user => "root", ipfrom => "10.19.1.2", ip => "1.2.3.4", port => "9876" )->err, "KO_ACCESS_DENIED", "is_access_granted(wildcard) on disallowed machine due to ingressToEgressRules #1" ); ok( OVH::Bastion::is_access_granted( account => "wildcard", user => "root", ipfrom => "10.19.1.2", ip => "10.20.1.2", port => "9876" )->is_ok, "is_access_granted(wildcard) on allowed machine due to ingressToEgressRules #1" ); ok( OVH::Bastion::is_access_granted( account => "wildcard", user => "root", ipfrom => "192.168.42.1", ip => "192.168.42.4", port => "9876" )->is_ok, "is_access_granted(wildcard) on allowed machine due to ingressToEgressRules #2" ); ok( OVH::Bastion::is_access_granted( account => "wildcard", user => "root", ipfrom => "192.168.42.1", ip => "5.6.7.8", port => "9876" )->is_ok, "is_access_granted(wildcard) on allowed machine due to ingressToEgressRules #2" ); is( OVH::Bastion::is_access_granted( account => "wildcard", user => "root", ipfrom => "192.168.43.1", ip => "192.168.42.4", port => "9876" )->err, "KO_ACCESS_DENIED", "is_access_granted(wildcard) on disallowed machine due to ingressToEgressRules #3" ); ok( OVH::Bastion::is_access_granted( account => "wildcard", user => "root", ipfrom => "192.168.43.1", ip => "5.6.7.8", port => "9876" )->is_ok, "is_access_granted(wildcard) on allowed machine due to ingressToEgressRules catch-all" ); # check that "bool" type options are correctly normalized is(OVH::Bastion::config("enableSyslog")->value, 1, "config bool(1)"); is(OVH::Bastion::config("enableGlobalAccessLog")->value, 1, "config bool(true)"); is(OVH::Bastion::config("enableAccountAccessLog")->value, 1, "config bool(\"yes\")"); is(OVH::Bastion::config("enableGlobalSqlLog")->value, 0, "config bool(0)"); is(OVH::Bastion::config("enableAccountSqlLog")->value, 0, "config bool(false)"); is(OVH::Bastion::config("displayLastLogin")->value, 0, "config bool(\"\")"); is(OVH::Bastion::config("interactiveModeByDefault")->value, 1, "config bool(missing, default true)"); is(OVH::Bastion::config("interactiveModeAllowed")->value, 0, "config bool(missing, default false)"); is(OVH::Bastion::config("debug")->value, 0, "config bool(null)"); is(OVH::Bastion::config("passwordAllowed")->value, 0, "config bool(\"no\")"); is(OVH::Bastion::config("telnetAllowed")->value, 0, "config bool(\"false\")"); is( OVH::Bastion::plugin_config( plugin => "help", key => "disabled", mock_data => {disabled => JSON::true} )->value ? 1 : 0, 1, "is_plugin_disabled(disabled=true)" ); is( OVH::Bastion::plugin_config( plugin => "help", key => "disabled", mock_data => {disabled => JSON::false} )->value ? 1 : 0, 0, "is_plugin_disabled(disabled=false)" ); is( OVH::Bastion::plugin_config( plugin => "help", key => "disabled", mock_data => {disabled => JSON::null} )->value ? 1 : 0, 0, "is_plugin_disabled(disabled=null)" ); is( OVH::Bastion::plugin_config( plugin => "help", key => "disabled", mock_data => {disabled => "yes"} )->value ? 1 : 0, 1, "is_plugin_disabled(disabled=\"yes\")" ); is( OVH::Bastion::plugin_config( plugin => "help", key => "disabled", mock_data => {disabled => "no"} )->value ? 1 : 0, 0, "is_plugin_disabled(disabled=\"no\")" ); is( OVH::Bastion::plugin_config( plugin => "help", key => "disabled", mock_data => {disabled => "true"} )->value ? 1 : 0, 1, "is_plugin_disabled(disabled=\"true\")" ); is( OVH::Bastion::plugin_config( plugin => "help", key => "disabled", mock_data => {disabled => "false"} )->value ? 1 : 0, 0, "is_plugin_disabled(disabled=\"false\")" ); is( OVH::Bastion::plugin_config( plugin => "help", key => "disabled", mock_data => {disabled => ""} )->value ? 1 : 0, 0, "is_plugin_disabled(disabled=\"\")" ); is( OVH::Bastion::plugin_config( plugin => "help", key => "disabled", mock_data => {disabled => "0"} )->value ? 1 : 0, 0, "is_plugin_disabled(disabled=\"0\")" ); is( OVH::Bastion::plugin_config( plugin => "help", key => "disabled", mock_data => {disabled => "1"} )->value ? 1 : 0, 1, "is_plugin_disabled(disabled=\"1\")" ); is( OVH::Bastion::plugin_config( plugin => "help", key => "disabled", mock_data => {disabled => 0} )->value ? 1 : 0, 0, "is_plugin_disabled(disabled=0)" ); is( OVH::Bastion::plugin_config( plugin => "help", key => "disabled", mock_data => {disabled => 1} )->value ? 1 : 0, 1, "is_plugin_disabled(disabled=1)" ); is( OVH::Bastion::plugin_config( plugin => "help", key => "disabled", mock_data => {} )->value ? 1 : 0, 0, "is_plugin_disabled()" ); is( OVH::Bastion::build_re_from_wildcards(wildcards => ["azerty", "st*ar", "que?stion", "c*ompl?i*cated*"])->value, qr/^azerty$|^st.*ar$|^que.stion$|^c.*ompl.i.*cated.*$/, "build_re_from_wildcards() 1" ); is( OVH::Bastion::build_re_from_wildcards( wildcards => ["azerty", "st*ar", "que?stion", "c*ompl?i*cated*"], implicit_contains => 1 )->value, qr/^.*azerty.*$|^st.*ar$|^que.stion$|^c.*ompl.i.*cated.*$/, "build_re_from_wildcards() 2" ); done_testing();