feat: make the complete ttyrec file path configurable and via-aware

pull/592/head
Stéphane Lesimple 7 days ago
parent c85013555c
commit 543c4d69b3
No known key found for this signature in database
GPG Key ID: 4B4A3289E9D35658

@ -236,10 +236,22 @@
"enableAccountSqlLog": true,
#
# ttyrecFilenameFormat (string)
# DESC: Sets the filename format of the output files of ttyrec for a given session. Magic tokens are: ``&bastionname``, ``&uniqid``, ``&account``, ``&ip``, ``&port``, ``&user`` (they'll be replaced by the corresponding values of the current session). Then, this string (automatically prepended with the correct folder) will be passed to ttyrec's ``-F`` parameter, which uses ``strftime()`` to expand it, so the usual character conversions will be done (``%Y`` for the year, ``%H`` for the hour, etc., see ``man strftime``). Note that in a addition to the usual ``strftime()`` conversion specifications, ttyrec also supports ``#usec#``, to be replaced by the current microsecond value of the time.
# DESC: Sets the filename format of the output files of ttyrec for a given session. Magic tokens are: ``&bastionname``, ``&uniqid``, ``&account``, ``&ip``, ``&port``, ``&user``, ``&home`` (the connecting account's home directory) and ``&remoteaccount`` (the remote account name for realm accounts, empty otherwise); they'll be replaced by the corresponding values of the current session. Then, this string (automatically prepended with the correct folder) will be passed to ttyrec's ``-F`` parameter, which uses ``strftime()`` to expand it, so the usual character conversions will be done (``%Y`` for the year, ``%H`` for the hour, etc., see ``man strftime``). Note that in a addition to the usual ``strftime()`` conversion specifications, ttyrec also supports ``#usec#``, to be replaced by the current microsecond value of the time. NOTE: this option sets the *filename* only, which is placed within a standard folder layout. To control the full path (directories included), define ``ttyrecDirectPathFormat`` and/or ``ttyrecViaPathFormat`` instead, which take precedence over this option when set.
# DEFAULT: "%Y-%m-%d.%H-%M-%S.#usec#.&uniqid.&account.&user.&ip.&port.ttyrec"
"ttyrecFilenameFormat": "%Y-%m-%d.%H-%M-%S.#usec#.&uniqid.&account.&user.&ip.&port.ttyrec",
#
# ttyrecDirectPathFormat (string)
# DESC: Full absolute path template (directories AND filename) of the ttyrec recording for a *direct* egress connection (i.e. not going through a jumphost). The supported magic tokens are the same as ``ttyrecFilenameFormat``. ``/`` separates directory components, the last one being the filename, which is passed to ttyrec's ``-F`` (hence ``strftime()``-expanded as for ``ttyrecFilenameFormat``). The directory tree is created as needed. When left empty, the standard layout ``&home/ttyrec/&ip/<ttyrecFilenameFormat>`` is used (with an extra ``&remoteaccount`` subfolder for realm accounts).
# EXAMPLE: "&home/ttyrec/&ip/%Y-%m-%d.%H-%M-%S.#usec#.&uniqid.&account.&user.&ip.&port.ttyrec"
# DEFAULT: ""
"ttyrecDirectPathFormat": "",
#
# ttyrecViaPathFormat (string)
# DESC: Same as ``ttyrecDirectPathFormat``, but used for egress connections going *through* a jumphost (proxy-jump). In addition to the tokens above, the proxy-related tokens ``&proxyip``, ``&proxyport`` and ``&proxyuser`` are available. When left empty, the standard layout ``&home/ttyrec/via-&proxyip-&ip/<ttyrecFilenameFormat>`` is used; note that with the default ``ttyrecFilenameFormat`` the proxy information then only appears in the folder name, so set this option explicitly if you want it in the filename too.
# EXAMPLE: "&home/ttyrec/via-&proxyip-&ip/%Y-%m-%d.%H-%M-%S.#usec#.&uniqid.&account.&user.&ip.&port.via.&proxyuser.&proxyip.&proxyport.ttyrec"
# DEFAULT: ""
"ttyrecViaPathFormat": "",
#
# ttyrecAdditionalParameters (array of strings)
# DESC: Additional parameters you want to pass to ``ttyrec`` invocation. Useful, for example, to enable on-the-fly compression, disable cheatcodes, or set/unset any other ``ttyrec`` option. This is an ARRAY, not a string.
# EXAMPLE: ["-s", "This is a message with spaces", "--zstd"]

@ -1197,53 +1197,82 @@ sub build_ttyrec_cmdline_part1of2 {
$params{'proxyIp'} = 'v6[' . $params{'proxyIp'} . ']';
}
# build ttyrec filename format
my $bastionName = OVH::Bastion::config('bastionName')->value;
my $ttyrecFilenameFormat = OVH::Bastion::config('ttyrecFilenameFormat')->value;
$ttyrecFilenameFormat =~ s/&bastionname/$bastionName/g;
$ttyrecFilenameFormat =~ s/&uniqid/$params{'uniqid'}/g if $params{'uniqid'};
$ttyrecFilenameFormat =~ s/&ip/$params{'ip'}/g if $params{'ip'};
$ttyrecFilenameFormat =~ s/&port/$params{'port'}/g if defined $params{'port'};
$ttyrecFilenameFormat =~ s/&user/$params{'user'}/g if defined $params{'user'};
$ttyrecFilenameFormat =~ s/&account/$params{'account'}/g if $params{'account'};
$ttyrecFilenameFormat =~ s/&proxyip/$params{'proxyIp'}/g if defined $params{'proxyIp'};
$ttyrecFilenameFormat =~ s/&proxyport/$params{'proxyPort'}/g if defined $params{'proxyPort'};
$ttyrecFilenameFormat =~ s/&proxyuser/$params{'proxyUser'}/g if defined $params{'proxyUser'};
if ($ttyrecFilenameFormat =~ /&(bastionname|uniqid|ip|port|user|account)/) {
# if we still have a placeholder here, then we were missing parameters
return R('ERR_MISSING_PARAMETER',
msg => "Missing bastionname, uniqid, ip, port, user or account in ttyrec cmdline building");
# Resolve the ttyrec recording path. Two dedicated full-path templates (directories + filename)
# are used depending on whether the egress connection goes through a jumphost ('via') or not
# ('direct'). When the relevant option isn't set, the path is built from the standard layout
# using ttyrecFilenameFormat (which sets the filename only).
my $bastionName = OVH::Bastion::config('bastionName')->value;
my $isVia = defined $params{'proxyIp'};
my $pathFormat =
$isVia
? OVH::Bastion::config('ttyrecViaPathFormat')->value
: OVH::Bastion::config('ttyrecDirectPathFormat')->value;
if (!$pathFormat) {
my $filenameFormat = OVH::Bastion::config('ttyrecFilenameFormat')->value;
# standard layout: <home>/ttyrec[/<remoteaccount>]/<perhost>/<filename>, where <perhost>
# is 'via-<proxyip>-<ip>' for proxy-jump connections (so it sorts by proxy first) or '<ip>'
my $perHost = $isVia ? 'via-&proxyip-&ip' : '&ip';
$pathFormat = "&home/ttyrec/&remoteaccount/$perHost/$filenameFormat";
}
# Substitute placeholders. Required tokens are left untouched when their value is missing, so
# the check just below catches the error; optional tokens (proxy*, remoteaccount) become the
# empty string when absent, and the resulting empty path components are collapsed afterwards.
my %requiredToken = (
'&home' => $params{'home'},
'&bastionname' => $bastionName,
'&uniqid' => $params{'uniqid'},
'&ip' => $params{'ip'},
'&port' => $params{'port'},
'&user' => $params{'user'},
'&account' => $params{'account'},
);
my %optionalToken = (
'&remoteaccount' => (($params{'realm'} && $params{'remoteaccount'}) ? $params{'remoteaccount'} : ''),
'&proxyip' => $params{'proxyIp'},
'&proxyport' => $params{'proxyPort'},
'&proxyuser' => $params{'proxyUser'},
);
foreach my $tok (keys %requiredToken) {
my $val = $requiredToken{$tok};
next if !defined $val;
$val =~ tr{/}{_} if $tok ne '&home'; # slash guard, except &home which is legitimately a path
$pathFormat =~ s/\Q$tok\E/$val/g;
}
foreach my $tok (keys %optionalToken) {
my $val = $optionalToken{$tok} // '';
$val =~ tr{/}{_};
$pathFormat =~ s/\Q$tok\E/$val/g;
}
# if there is no proxyIp, remove the via part and all proxy placeholders
if (!defined $params{'proxyIp'}) {
$ttyrecFilenameFormat =~ s/\.via//g;
$ttyrecFilenameFormat =~ s/\.&proxyuser//g;
$ttyrecFilenameFormat =~ s/\.&proxyip//g;
$ttyrecFilenameFormat =~ s/\.&proxyport//g;
# a still-present required placeholder means we were called with a missing parameter
if ($pathFormat =~ /&(home|bastionname|uniqid|ip|port|user|account)/) {
return R('ERR_MISSING_PARAMETER',
msg => "Missing home, bastionname, uniqid, ip, port, user or account in ttyrec cmdline building");
}
# ensure there are no '/'
$ttyrecFilenameFormat =~ tr{/}{_};
# collapse empty path components (e.g. an empty &remoteaccount for non-realm accounts)
$pathFormat =~ s{/+}{/}g;
# prepend (and create) directory
my $saveDir = $params{'home'} . "/ttyrec";
mkdir($saveDir);
if ($params{'realm'} && $params{'remoteaccount'}) {
$saveDir .= "/" . $params{'remoteaccount'};
mkdir($saveDir);
# safety: the resolved path must be absolute and must not climb out of the tree with '..'
if ($pathFormat !~ m{^/} || $pathFormat =~ m{(?:^|/)\.\.(?:/|$)}) {
return R('ERR_SECURITY_VIOLATION', msg => "Refusing to use unsafe ttyrec path '$pathFormat'");
}
# format it like via-$proxyIp-$ip so that it's sorted by proxyIp first, then by ip if you list the directory
if ($params{'proxyIp'}) {
$saveDir .= "/via-" . $params{'proxyIp'} . "-" . $params{'ip'};
# split into directory + filename, and create the directory tree (best-effort, default perms)
my ($saveDir, $ttyrecFilenameFormat) = $pathFormat =~ m{^(.*)/([^/]+)$};
if (!$saveDir || !$ttyrecFilenameFormat) {
return R('ERR_INVALID_PARAMETER', msg => "Invalid ttyrec path '$pathFormat'");
}
else {
$saveDir .= "/" . $params{'ip'};
my $mkpath = '';
foreach my $component (split m{/}, $saveDir) {
next if !$component;
$mkpath .= "/$component";
mkdir($mkpath); # ignore errors: parent directories likely already exist
}
mkdir($saveDir);
my $saveFileFormat = "$saveDir/$ttyrecFilenameFormat";

@ -156,11 +156,19 @@ sub load_configuration {
{name => 'syslogDescription', default => 'bastion', validre => qr/^([a-zA-Z0-9_.-]+)$/},
{
name => 'ttyrecFilenameFormat',
default =>
'%Y-%m-%d.%H-%M-%S.#usec#.&uniqid.&account.&user.&ip.&port.via.&proxyuser.&proxyip.&proxyport.ttyrec',
default => '%Y-%m-%d.%H-%M-%S.#usec#.&uniqid.&account.&user.&ip.&port.ttyrec',
validre => qr/^([a-zA-Z0-9%&#_.-]+)$/
},
{name => 'accountExpiredMessage', default => '', validre => qr/^(.*)$/, emptyok => 1},
# ttyrecDirectPathFormat/ttyrecViaPathFormat: full absolute path templates (directories +
# filename) for the session recording, used respectively for direct egress and for
# proxy-jump ('via') egress. When left empty (the default), the path is built from the
# standard layout using ttyrecFilenameFormat. '/' separates directory components; the last
# component is the filename. The supported placeholders are the same as ttyrecFilenameFormat;
# see bastion.conf.dist for the full list.
{name => 'ttyrecDirectPathFormat', default => '', validre => qr{^([a-zA-Z0-9%&#_./-]*)$}, emptyok => 1},
{name => 'ttyrecViaPathFormat', default => '', validre => qr{^([a-zA-Z0-9%&#_./-]*)$}, emptyok => 1},
{name => 'accountExpiredMessage', default => '', validre => qr/^(.*)$/, emptyok => 1},
{name => 'fanciness', default => 'full', validre => qr/^((none|boomer)|(basic|millenial)|(full|genz))$/},
{name => 'accountExternalValidationProgram', default => '', validre => qr'^([a-zA-Z0-9/$_.-]*)$', emptyok => 1},
{name => 'ttyrecStealthStdoutPattern', default => '', validre => qr'^(.{0,4096})$', emptyok => 1},

@ -174,7 +174,7 @@ $fnret = OVH::Bastion::build_ttyrec_cmdline_part1of2(
cmp_deeply(
$fnret->value->{'saveFile'},
re(
qr{^\Q/home/randomuser/ttyrec/via-10.0.0.1-192.168.1.100/20\E\d\d-\d\d-\d\d.\d\d\-\d\d\-\d\d\.\d{6}\Q.cafed00dcafe.bastionuser.targetuser.192.168.1.100.22.via.jumpi.10.0.0.1.2222.ttyrec\E$}
qr{^\Q/home/randomuser/ttyrec/via-10.0.0.1-192.168.1.100/20\E\d\d-\d\d-\d\d.\d\d\-\d\d\-\d\d\.\d{6}\Q.cafed00dcafe.bastionuser.targetuser.192.168.1.100.22.ttyrec\E$}
),
"build_ttyrec_cmdline_part1of2 with proxy saveFile"
);
@ -185,7 +185,7 @@ cmp_deeply(
'-f',
$fnret->value->{'saveFile'},
'-F',
'/home/randomuser/ttyrec/via-10.0.0.1-192.168.1.100/%Y-%m-%d.%H-%M-%S.#usec#.cafed00dcafe.bastionuser.targetuser.192.168.1.100.22.via.jumpi.10.0.0.1.2222.ttyrec'
'/home/randomuser/ttyrec/via-10.0.0.1-192.168.1.100/%Y-%m-%d.%H-%M-%S.#usec#.cafed00dcafe.bastionuser.targetuser.192.168.1.100.22.ttyrec'
],
"build_ttyrec_cmdline_part1of2 with proxy cmd"
);
@ -205,7 +205,7 @@ $fnret = OVH::Bastion::build_ttyrec_cmdline_part1of2(
cmp_deeply(
$fnret->value->{'saveFile'},
re(
qr{^\Q/home/randomuser/ttyrec/via-v6[2001.db8..1]-192.168.1.200/20\E\d\d-\d\d-\d\d.\d\d\-\d\d\-\d\d\.\d{6}\Q.cafed00dcafe.bastionuser.targetuser.192.168.1.200.22.via.jumpi.v6[2001.db8..1].22.ttyrec\E$}
qr{^\Q/home/randomuser/ttyrec/via-v6[2001.db8..1]-192.168.1.200/20\E\d\d-\d\d-\d\d.\d\d\-\d\d\-\d\d\.\d{6}\Q.cafed00dcafe.bastionuser.targetuser.192.168.1.200.22.ttyrec\E$}
),
"build_ttyrec_cmdline_part1of2 with IPv6 proxy saveFile"
);
@ -216,7 +216,7 @@ cmp_deeply(
'-f',
$fnret->value->{'saveFile'},
'-F',
'/home/randomuser/ttyrec/via-v6[2001.db8..1]-192.168.1.200/%Y-%m-%d.%H-%M-%S.#usec#.cafed00dcafe.bastionuser.targetuser.192.168.1.200.22.via.jumpi.v6[2001.db8..1].22.ttyrec'
'/home/randomuser/ttyrec/via-v6[2001.db8..1]-192.168.1.200/%Y-%m-%d.%H-%M-%S.#usec#.cafed00dcafe.bastionuser.targetuser.192.168.1.200.22.ttyrec'
],
"build_ttyrec_cmdline_part1of2 with IPv6 proxy cmd"
);
@ -486,4 +486,187 @@ is(
"build_re_from_wildcards() 2"
);
# ttyrecDirectPathFormat / ttyrecViaPathFormat
# a custom direct path format: full path with &home, &account, &ip, &port and a filename
OVH::Bastion::load_configuration(
mock_data => {
bastionName => 'mock',
ttyrecDirectPathFormat => '&home/rec/&account/&ip-&port/%Y.#usec#.&uniqid.&user.ttyrec',
}
);
$fnret = OVH::Bastion::build_ttyrec_cmdline_part1of2(
ip => "203.0.113.5",
port => 2222,
user => "alice",
account => "bob",
uniqid => 'deadbeef',
home => "/home/bob",
);
cmp_deeply(
$fnret->value->{'saveFile'},
re(qr{^\Q/home/bob/rec/bob/203.0.113.5-2222/20\E\d\d\Q.\E\d{6}\Q.deadbeef.alice.ttyrec\E$}),
"ttyrecDirectPathFormat: custom path saveFile"
);
is(
$fnret->value->{'cmd'}[4],
'/home/bob/rec/bob/203.0.113.5-2222/%Y.#usec#.deadbeef.alice.ttyrec',
"ttyrecDirectPathFormat: custom path -F format"
);
# a custom via path format: proxy tokens are available, and only the via format is consulted for proxy connections
OVH::Bastion::load_configuration(
mock_data => {
bastionName => 'mock',
ttyrecViaPathFormat => '&home/rec/via-&proxyuser-at-&proxyip-&proxyport/&ip/%Y.#usec#.&uniqid.ttyrec',
}
);
$fnret = OVH::Bastion::build_ttyrec_cmdline_part1of2(
ip => "203.0.113.5",
port => 22,
user => "alice",
account => "bob",
uniqid => 'deadbeef',
home => "/home/bob",
proxyIp => "198.51.100.9",
proxyPort => 2222,
proxyUser => "jump",
);
is(
$fnret->value->{'cmd'}[4],
'/home/bob/rec/via-jump-at-198.51.100.9-2222/203.0.113.5/%Y.#usec#.deadbeef.ttyrec',
"ttyrecViaPathFormat: custom path with proxy tokens"
);
# realm account, fallback layout: the &remoteaccount subfolder is inserted
OVH::Bastion::load_configuration(mock_data => {bastionName => 'mock'});
$fnret = OVH::Bastion::build_ttyrec_cmdline_part1of2(
ip => "203.0.113.5",
port => 22,
user => "alice",
account => "realm_foo",
uniqid => 'deadbeef',
home => "/home/realm_foo",
realm => "foo",
remoteaccount => "bob",
);
cmp_deeply(
$fnret->value->{'saveFile'},
re(qr{^\Q/home/realm_foo/ttyrec/bob/203.0.113.5/20\E\d\d.*\Q.deadbeef.realm_foo.alice.203.0.113.5.22.ttyrec\E$}),
"fallback realm account: &remoteaccount subfolder is present"
);
# a path format that tries to climb out of the tree must be refused
OVH::Bastion::load_configuration(
mock_data => {bastionName => 'mock', ttyrecDirectPathFormat => '&home/../../etc/&ip/x'});
$fnret = OVH::Bastion::build_ttyrec_cmdline_part1of2(
ip => "1.2.3.4",
port => 22,
user => "u",
account => "a",
uniqid => 'x',
home => "/home/u",
);
is($fnret->err, 'ERR_SECURITY_VIOLATION', "ttyrec path with '..' is refused");
# full matrix: {only Direct, only Via, both, neither} x {direct conn, via conn}
# each of ttyrecDirectPathFormat and ttyrecViaPathFormat falls back independently to the legacy
# layout (built from ttyrecFilenameFormat) when its own value is empty. We verify every cell.
# returns the ttyrec '-F' format (i.e. the resolved path template, before strftime expansion)
sub _ttyrec_F {
my %extra = @_;
my $r = OVH::Bastion::build_ttyrec_cmdline_part1of2(
ip => "203.0.113.5",
port => 22,
user => "alice",
account => "bob",
uniqid => 'deadbeef',
home => "/home/bob",
%extra,
);
return $r ? $r->value->{'cmd'}[4] : "ERR:" . $r->err;
}
my $DIRECT_FMT = '&home/D/&account/&ip-&port/%Y.#usec#.&uniqid.&user.ttyrec';
my $VIA_FMT = '&home/V/&proxyuser-&proxyip-&proxyport/&ip/%Y.#usec#.&uniqid.ttyrec';
my %VIA_CONN = (proxyIp => "198.51.100.9", proxyPort => 2222, proxyUser => "jump");
my $DIRECT_CUSTOM = '/home/bob/D/bob/203.0.113.5-22/%Y.#usec#.deadbeef.alice.ttyrec';
my $VIA_CUSTOM = '/home/bob/V/jump-198.51.100.9-2222/203.0.113.5/%Y.#usec#.deadbeef.ttyrec';
my $DIRECT_FALLBACK = '/home/bob/ttyrec/203.0.113.5/%Y-%m-%d.%H-%M-%S.#usec#.deadbeef.bob.alice.203.0.113.5.22.ttyrec';
my $VIA_FALLBACK =
'/home/bob/ttyrec/via-198.51.100.9-203.0.113.5/%Y-%m-%d.%H-%M-%S.#usec#.deadbeef.bob.alice.203.0.113.5.22.ttyrec';
# only ttyrecDirectPathFormat defined
OVH::Bastion::load_configuration(mock_data => {bastionName => 'mock', ttyrecDirectPathFormat => $DIRECT_FMT});
is(_ttyrec_F(), $DIRECT_CUSTOM, "only Direct defined: direct conn uses ttyrecDirectPathFormat");
is(_ttyrec_F(%VIA_CONN), $VIA_FALLBACK, "only Direct defined: via conn falls back (ttyrecViaPathFormat empty)");
# only ttyrecViaPathFormat defined
OVH::Bastion::load_configuration(mock_data => {bastionName => 'mock', ttyrecViaPathFormat => $VIA_FMT});
is(_ttyrec_F(), $DIRECT_FALLBACK, "only Via defined: direct conn falls back (ttyrecDirectPathFormat empty)");
is(_ttyrec_F(%VIA_CONN), $VIA_CUSTOM, "only Via defined: via conn uses ttyrecViaPathFormat");
# both defined
OVH::Bastion::load_configuration(
mock_data => {bastionName => 'mock', ttyrecDirectPathFormat => $DIRECT_FMT, ttyrecViaPathFormat => $VIA_FMT});
is(_ttyrec_F(), $DIRECT_CUSTOM, "both defined: direct conn uses ttyrecDirectPathFormat");
is(_ttyrec_F(%VIA_CONN), $VIA_CUSTOM, "both defined: via conn uses ttyrecViaPathFormat");
# neither defined (fallback for both)
OVH::Bastion::load_configuration(mock_data => {bastionName => 'mock'});
is(_ttyrec_F(), $DIRECT_FALLBACK, "neither defined: direct conn uses fallback layout");
is(_ttyrec_F(%VIA_CONN), $VIA_FALLBACK, "neither defined: via conn uses fallback layout");
# &home and &remoteaccount are usable in ttyrecFilenameFormat too: the substitution is applied to
# the whole resolved path, so these tokens are not restricted to the new path-format options.
OVH::Bastion::load_configuration(
mock_data => {bastionName => 'mock', ttyrecFilenameFormat => '&uniqid.&remoteaccount.&user.&ip.&port.ttyrec'});
is(
_ttyrec_F(account => "realm_foo", home => "/home/realm_foo", realm => "foo", remoteaccount => "bob"),
'/home/realm_foo/ttyrec/bob/203.0.113.5/deadbeef.bob.alice.203.0.113.5.22.ttyrec',
"&remoteaccount is usable inside ttyrecFilenameFormat"
);
OVH::Bastion::load_configuration(mock_data => {bastionName => 'mock', ttyrecFilenameFormat => '&home.&uniqid.ttyrec'});
is(_ttyrec_F(), '/home/bob/ttyrec/203.0.113.5/home/bob.deadbeef.ttyrec', "&home is usable inside ttyrecFilenameFormat");
# pre-proxyjump directory-layout regression: the &remoteaccount subfolder must be inserted IFF both 'realm'
# and 'remoteaccount' are passed (matching the pre-proxyjump 'if ($realm && $remoteaccount)' behavior).
OVH::Bastion::load_configuration(mock_data => {bastionName => 'mock'});
my $WITH_REMACCT =
'/home/bob/ttyrec/remacct/203.0.113.5/%Y-%m-%d.%H-%M-%S.#usec#.deadbeef.bob.alice.203.0.113.5.22.ttyrec';
is(_ttyrec_F(), $DIRECT_FALLBACK, "pre-proxyjump layout: no realm + no remoteaccount => no subfolder");
is(_ttyrec_F(realm => "myrealm", remoteaccount => "remacct"),
$WITH_REMACCT, "pre-proxyjump layout: realm + remoteaccount => remoteaccount subfolder");
is(_ttyrec_F(realm => "myrealm"), $DIRECT_FALLBACK,
"pre-proxyjump layout: realm without remoteaccount => no subfolder");
is(_ttyrec_F(remoteaccount => "remacct"),
$DIRECT_FALLBACK, "pre-proxyjump layout: remoteaccount without realm => no subfolder");
# Default vanilla install: ttyrecFilenameFormat at its shipped default, and both path-format options
# unset (''). This is the most common configuration, so its behavior must be identical before and
# after this PR. First pin those default values (mock-defaulting == a freshly copied bastion.conf):
OVH::Bastion::load_configuration(mock_data => {bastionName => 'mock'});
my $DEFAULT_FNAME = '%Y-%m-%d.%H-%M-%S.#usec#.&uniqid.&account.&user.&ip.&port.ttyrec';
is(OVH::Bastion::config('ttyrecFilenameFormat')->value, $DEFAULT_FNAME, "vanilla: ttyrecFilenameFormat default value");
is(OVH::Bastion::config('ttyrecDirectPathFormat')->value, '', "vanilla: ttyrecDirectPathFormat defaults to ''");
is(OVH::Bastion::config('ttyrecViaPathFormat')->value, '', "vanilla: ttyrecViaPathFormat defaults to ''");
# Same, but with all three options set EXPLICITLY to their vanilla values (as a copied
# bastion.conf.dist would have them): the resolved paths must match the pre-proxyjump layout, i.e. be
# identical to omitting the path-format options entirely.
OVH::Bastion::load_configuration(
mock_data => {
bastionName => 'mock',
ttyrecFilenameFormat => $DEFAULT_FNAME,
ttyrecDirectPathFormat => '',
ttyrecViaPathFormat => '',
}
);
is(_ttyrec_F(), $DIRECT_FALLBACK, "vanilla (explicit defaults): direct conn => pre-proxyjump layout");
is(_ttyrec_F(%VIA_CONN), $VIA_FALLBACK, "vanilla (explicit defaults): via conn => pre-proxyjump layout");
is(_ttyrec_F(realm => "myrealm", remoteaccount => "remacct"),
$WITH_REMACCT, "vanilla (explicit defaults): realm+remoteaccount => pre-proxyjump layout");
done_testing();

Loading…
Cancel
Save