Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add dnsSupportLevel for systems with broken DNS (fixes #397) #454

Merged
merged 1 commit into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions doc/sphinx/administration/configuration/bastion_conf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Global network policies options

Those options can set a few global network policies to be applied bastion-wide.

- `dnsSupportLevel`_
- `allowedNetworks`_
- `forbiddenNetworks`_
- `ingressToEgressRules`_
Expand Down Expand Up @@ -345,6 +346,19 @@ Additional parameters that will be passed as-is to mosh-server. See ``man mosh-s
Global network policies
-----------------------

.. _dnsSupportLevel:

dnsSupportLevel
***************

:Type: ``integer between 0 and 2``

:Default: ``2``

If set to 0, The Bastion will never attempt to do DNS or reverse-DNS resolutions, and return an error if you request connection to a hostname instead of an IP. Use this if you know there's no working DNS in your environment and only use IPs everywhere.
If set to 1, The Bastion will not attempt to do DNS or reverse-DNS resolutions unless you force it to (i.e. by requesting connection to a hostname instead of an IP). You may use this if for example you have well-known hostnames in /etc/hosts, but don't have a working DNS (which would imply that reverse-DNS resolutions will always fail).
If set to 2, The Bastion will make the assumption that you have a working DNS setup, and will do DNS and reverse-DNS resolutions normally.

.. _allowedNetworks:

allowedNetworks
Expand Down
7 changes: 7 additions & 0 deletions etc/bastion/bastion.conf.dist
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@
# > Global network policies
# >> Those options can set a few global network policies to be applied bastion-wide.
#
# dnsSupportLevel (integer between 0 and 2)
# DESC: If set to 0, The Bastion will never attempt to do DNS or reverse-DNS resolutions, and return an error if you request connection to a hostname instead of an IP. Use this if you know there's no working DNS in your environment and only use IPs everywhere.
# If set to 1, The Bastion will not attempt to do DNS or reverse-DNS resolutions unless you force it to (i.e. by requesting connection to a hostname instead of an IP). You may use this if for example you have well-known hostnames in /etc/hosts, but don't have a working DNS (which would imply that reverse-DNS resolutions will always fail).
# If set to 2, The Bastion will make the assumption that you have a working DNS setup, and will do DNS and reverse-DNS resolutions normally.
# DEFAULT: 2
"dnsSupportLevel": 2,
#
# allowedNetworks (array of strings (IPs and/or prefixes))
# DESC: Restricts egress connection attempts to those listed networks only. This is enforced at all times and can NOT be overridden by users. If you are lucky enough to have you own IP blocks, it's probably a good idea to list them here. An empty array means no restriction is applied.
# DEFAULT: []
Expand Down
8 changes: 8 additions & 0 deletions lib/perl/OVH/Bastion/allowdeny.inc
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ sub get_ip {
return R('ERR_INVALID_IP', msg => "IP $host version is not allowed");
}

if (OVH::Bastion::config('dnsSupportLevel')->value < 1) {
return R('ERR_DNS_DISABLED', msg => "DNS resolving is disabled on this bastion");
}

osh_debug("Trying to resolve '$host' because is_valid_ip() says it's not an IP");
my ($err, @res);
eval {
Expand Down Expand Up @@ -334,6 +338,10 @@ sub ip2host {
my $ip = shift;
my ($err, @sockaddr, $host);

if (OVH::Bastion::config('dnsSupportLevel')->value < 2) {
return R('ERR_DNS_DISABLED');
}

eval {
# ip => packedip. AI_PASSIVE: don't use dns, just build sockaddr
# can croak
Expand Down
39 changes: 18 additions & 21 deletions lib/perl/OVH/Bastion/configuration.inc
Original file line number Diff line number Diff line change
Expand Up @@ -211,47 +211,44 @@ sub load_configuration {
{name => 'accountMaxInactiveDays', min => 0, max => 365 * 5, default => 0},
{name => 'interactiveModeTimeout', min => 0, max => 86400 * 365, default => 15},
{name => 'interactiveModeProactiveMFAexpiration', min => 0, max => 86400, default => 900},
{name => 'dnsSupportLevel', min => 0, max => 2, default => 2},
)
{
if (not defined $C->{$o->{'name'}}) {
$C->{$o->{'name'}} = $o->{'default'};
push @errors, "Configuration error: missing option '" . $o->{'name'} . "', defaulting to " . $o->{'default'}
push @errors,
sprintf("Configuration error: missing option '%s', defaulting to %s", $o->{'name'}, $o->{'default'})
if $test;
}
if ($C->{$o->{'name'}} =~ /^(-?\d+)$/) {

# untaint
$C->{$o->{'name'}} = $1;
}
else {
push @errors,
"Configuration error: value of option '"
. $o->{'name'} . "' ('"
. $C->{$o->{'name'}}
. "') is not a number, defaulting to "
. $o->{'default'};
sprintf(
"Configuration error: value of option '%s' ('%s') is not a number, defaulting to %s",
$o->{'name'}, $C->{$o->{'name'}},
$o->{'default'}
);
$C->{$o->{'name'}} = $o->{'default'};
}
if ($C->{$o->{'name'}} > $o->{'max'}) {
push @errors,
"Configuration error: value of option '"
. $o->{'name'} . "' ("
. $C->{$o->{'name'}}
. ") is higher than allowed value ("
. $o->{'max'}
. "), defaulting to "
. $o->{'default'};
sprintf(
"Configuration error: value of option '%s' (%s) is higher than allowed value (%s), defaulting to %s",
$o->{'name'}, $C->{$o->{'name'}},
$o->{'max'}, $o->{'default'}
);
$C->{$o->{'name'}} = $o->{'default'};
}
elsif ($C->{$o->{'name'}} < $o->{'min'}) {
push @errors,
"Configuration error: value of option '"
. $o->{'name'} . "' ("
. $C->{$o->{'name'}}
. ") is lower than allowed value ("
. $o->{'min'}
. "), defaulting to "
. $o->{'default'};
sprintf(
"Configuration error: value of option '%s' (%s) is lower than allowed value (%s), defaulting to %s",
$o->{'name'}, $C->{$o->{'name'}},
$o->{'min'}, $o->{'default'}
);
$C->{$o->{'name'}} = $o->{'default'};
}
delete $unknownkeys{$o->{'name'}};
Expand Down
19 changes: 19 additions & 0 deletions tests/functional/tests.d/380-config-options.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# vim: set filetype=sh ts=4 sw=4 sts=4 et:
# shellcheck shell=bash
# shellcheck disable=SC2086,SC2016,SC2046
# below: convoluted way that forces shellcheck to source our caller
# shellcheck source=tests/functional/launch_tests_on_instance.sh
. "$(dirname "${BASH_SOURCE[0]}")"/dummy

testsuite_config_options()
{
configchg 's=^\\\\x22dnsSupportLevel\\\\x22.+=\\\\x22dnsSupportLevel\\\\x22:0,='

run a1_connect_nodns $a0 localhost
retvalmustbe 102
json .error_code KO_HOST_NOT_FOUND
contain 'DNS resolving is disabled'
}

testsuite_config_options
unset -f testsuite_config_options