VNC-257 Implement env var config override

This commit is contained in:
Dmitry Maksyoma 2025-10-08 18:41:34 +00:00 committed by Matthew McClaskey
parent 71bb0013eb
commit 6e3d2072f2
13 changed files with 483 additions and 129 deletions

3
.gitignore vendored
View file

@ -21,6 +21,9 @@ builder/build/
builder/www/
spec/tmp
Benchmark.xml
SelfBench.xml
# Deb building artefacts
debian/.debhelper/
debian/files

View file

@ -1,4 +1,5 @@
import os
import re
import sys
import shutil
import subprocess
@ -15,6 +16,16 @@ config_filename = os.path.join(config_dir, "config.yaml")
if os.getenv('KASMVNC_SPEC_DEBUG_OUTPUT'):
debug_output = True
def run_vncserver_to_print_xvnc_cli_options(env=os.environ):
return run_cmd(f'vncserver -dry-run -config {config_filename}', env=env)
def pick_cli_option(cli_option, xvnc_cmd):
cli_option_regex = re.compile(f'\'?-{cli_option}\'?(?:\s+[^-][^\s]*|$)')
results = cli_option_regex.findall(xvnc_cmd)
if len(results) == 0:
return None
return ' '.join(results)
def write_config(config_text):
os.makedirs(config_dir, exist_ok=True)

View file

@ -0,0 +1,133 @@
import os
from mamba import description, context, fcontext, it, fit, _it, before, after
from expects import expect, equal, contain, match
from helper.spec_helper import start_xvnc, kill_xvnc, run_cmd, clean_env, \
add_kasmvnc_user_docker, clean_kasm_users, start_xvnc_pexpect, \
write_config, config_filename, pick_cli_option, \
run_vncserver_to_print_xvnc_cli_options
with description('Env var config override') as self:
with context("env var override is turned off"):
with it("doesn't override, when setting is defined in config"):
write_config('''
desktop:
allow_resize: true
server:
allow_environment_variables_to_override_config_settings: false
''')
env = os.environ.copy()
env["KVNC_DESKTOP_ALLOW_RESIZE"] = "false"
completed_process = run_vncserver_to_print_xvnc_cli_options(env=env)
cli_option = pick_cli_option('AcceptSetDesktopSize',
completed_process.stdout)
expect(cli_option).to(equal("-AcceptSetDesktopSize '1'"))
with it("doesn't override, when setting is not defined in config"):
write_config('''
desktop:
allow_resize: true
''')
env = os.environ.copy()
env["KVNC_DESKTOP_ALLOW_RESIZE"] = "false"
completed_process = run_vncserver_to_print_xvnc_cli_options(env=env)
cli_option = pick_cli_option('AcceptSetDesktopSize',
completed_process.stdout)
expect(cli_option).to(equal("-AcceptSetDesktopSize '1'"))
with context("env var override is turned on"):
with it("converts env var to CLI option"):
write_config('''
desktop:
allow_resize: true
server:
allow_environment_variables_to_override_config_settings: true
''')
env = os.environ.copy()
env["KVNC_DESKTOP_ALLOW_RESIZE"] = "false"
completed_process = run_vncserver_to_print_xvnc_cli_options(env=env)
cli_option = pick_cli_option('AcceptSetDesktopSize',
completed_process.stdout)
expect(cli_option).to(equal("-AcceptSetDesktopSize '0'"))
with it("produces error message if env var has invalid value"):
write_config('''
desktop:
allow_resize: true
server:
allow_environment_variables_to_override_config_settings: true
''')
env = os.environ.copy()
env["KVNC_DESKTOP_ALLOW_RESIZE"] = "none"
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}', print_stderr=False, env=env)
expect(completed_process.stderr).to(contain("desktop.allow_resize 'none': must be true or false"))
with it("produces error message and exits if env var name is unsupported"):
write_config('''
foo: true
desktop:
allow_resize: true
server:
allow_environment_variables_to_override_config_settings: true
''')
env = os.environ.copy()
env["KVNC_FOO"] = "none"
completed_process = run_cmd(f'vncserver -test-output-topic validation -config {config_filename}', print_stderr=False, env=env)
expect(completed_process.stderr).to(contain("Unsupported config env vars found:\nKVNC_FOO"))
with context("config setting server.allow_environment_variables_to_override_config_settings"):
with it("produces error message if config has invalid value"):
write_config('''
server:
allow_environment_variables_to_override_config_settings: none
''')
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}', print_stderr=False)
expect(completed_process.stderr).to(contain("server.allow_environment_variables_to_override_config_settings 'none': must be true or false"))
with it("doesn't interpolate env vars into value"):
write_config('''
server:
allow_environment_variables_to_override_config_settings: ${ALLOW_OVERRIDE}
''')
env = os.environ.copy()
env["ALLOW_OVERRIDE"] = "true"
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}',\
print_stderr=False, env=env)
expect(completed_process.stderr).\
to(contain("server.allow_environment_variables_to_override_config_settings '${ALLOW_OVERRIDE}': must be true or false"))
with it("doesn't allow to override it with the corresponding env var if set to false"):
write_config('''
server:
allow_environment_variables_to_override_config_settings: false
''')
env = os.environ.copy()
env["KVNC_SERVER_ALLOW_ENVIRONMENT_VARIABLES_TO_OVERRIDE_CONFIG_SETTINGS"] = "${ALLOW_OVERRIDE}"
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}',\
print_stderr=False, env=env)
expect(completed_process.stderr).\
not_to(contain("server.allow_environment_variables_to_override_config_settings '${ALLOW_OVERRIDE}': must be true or false"))
with it("doesn't allow to override it with the env var if set to true"):
write_config('''
server:
allow_environment_variables_to_override_config_settings: true
''')
env = os.environ.copy()
env["KVNC_SERVER_ALLOW_ENVIRONMENT_VARIABLES_TO_OVERRIDE_CONFIG_SETTINGS"] = "${ALLOW_OVERRIDE}"
completed_process = run_cmd(f'vncserver -dry-run -test-output-topic validation -config {config_filename}',\
print_stderr=False, env=env)
expect(completed_process.stderr).\
not_to(contain("server.allow_environment_variables_to_override_config_settings '${ALLOW_OVERRIDE}': must be true or false"))

View file

@ -1,48 +1,19 @@
import os
import re
import shutil
from os.path import expanduser
from mamba import description, context, fcontext, it, fit, _it, before, after
from expects import expect, equal, contain, match
from helper.spec_helper import start_xvnc, kill_xvnc, run_cmd, clean_env, \
add_kasmvnc_user_docker, clean_kasm_users, start_xvnc_pexpect, \
write_config, config_filename
home_dir = expanduser("~")
vnc_dir = f'{home_dir}/.vnc'
user_config = f'{vnc_dir}/kasmvnc.yaml'
def run_vncserver():
return run_cmd(f'vncserver -dry-run -config {config_filename}')
def pick_cli_option(cli_option, xvnc_cmd):
cli_option_regex = re.compile(f'\'?-{cli_option}\'?(?:\s+[^-][^\s]*|$)')
results = cli_option_regex.findall(xvnc_cmd)
if len(results) == 0:
return None
return ' '.join(results)
def prepare_env():
os.makedirs(vnc_dir, exist_ok=True)
shutil.copyfile('spec/kasmvnc.yaml', user_config)
write_config, config_filename, pick_cli_option, \
run_vncserver_to_print_xvnc_cli_options
with description('YAML to CLI') as self:
with before.all:
prepare_env()
with context("convert a boolean key"):
with it("convert true to 1"):
write_config('''
desktop:
allow_resize: true
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('AcceptSetDesktopSize',
completed_process.stdout)
expect(cli_option).to(equal("-AcceptSetDesktopSize '1'"))
@ -52,7 +23,7 @@ with description('YAML to CLI') as self:
desktop:
allow_resize: false
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('AcceptSetDesktopSize',
completed_process.stdout)
expect(cli_option).to(equal("-AcceptSetDesktopSize '0'"))
@ -63,7 +34,7 @@ with description('YAML to CLI') as self:
brute_force_protection:
blacklist_threshold: 2
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('BlacklistThreshold',
completed_process.stdout)
expect(cli_option).to(equal("-BlacklistThreshold '2'"))
@ -74,7 +45,7 @@ with description('YAML to CLI') as self:
ssl:
pem_certificate: /etc/ssl/certs/ssl-cert-snakeoil.pem
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('cert',
completed_process.stdout)
expect(cli_option).to(
@ -87,7 +58,7 @@ with description('YAML to CLI') as self:
- 0x22->0x40
- 0x24->0x40
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('RemapKeys',
completed_process.stdout)
expect(cli_option).to(
@ -100,7 +71,7 @@ with description('YAML to CLI') as self:
server_to_client:
size: 20
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('DLP_ClipSendMax',
completed_process.stdout)
expect(cli_option).to(equal("-DLP_ClipSendMax '20'"))
@ -111,7 +82,7 @@ with description('YAML to CLI') as self:
network:
websocket_port: auto
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('websocketPort',
completed_process.stdout)
expect(["-websocketPort '8444'", "-websocketPort '8445'"]). \
@ -122,7 +93,7 @@ with description('YAML to CLI') as self:
network:
websocket_port: 8555
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('websocketPort',
completed_process.stdout)
expect(cli_option).to(equal("-websocketPort '8555'"))
@ -130,7 +101,7 @@ with description('YAML to CLI') as self:
with it("no key - no CLI option"):
write_config('''
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('websocketPort',
completed_process.stdout)
expect(cli_option).to(equal(None))
@ -141,7 +112,7 @@ with description('YAML to CLI') as self:
network:
protocol: http
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('noWebsocket',
completed_process.stdout)
expect(cli_option).to(equal(None))
@ -151,7 +122,7 @@ with description('YAML to CLI') as self:
network:
protocol: vnc
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('noWebsocket',
completed_process.stdout)
expect(cli_option).to(equal("-noWebsocket '1'"))
@ -162,7 +133,7 @@ with description('YAML to CLI') as self:
advanced:
kasm_password_file: ${HOME}/.kasmpasswd
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('KasmPasswordFile',
completed_process.stdout)
expect(cli_option).to(equal("-KasmPasswordFile '/home/docker/.kasmpasswd'"))
@ -174,7 +145,7 @@ with description('YAML to CLI') as self:
log_dest: logfile
level: 40
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('Log',
completed_process.stdout)
expect(cli_option).to(equal("-Log '*:stdout:40'"))
@ -188,7 +159,7 @@ with description('YAML to CLI') as self:
right: 40%
bottom: 40
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('DLP_Region',
completed_process.stdout)
expect(cli_option).to(equal("-DLP_Region '10,-10,40%,40'"))
@ -200,7 +171,7 @@ with description('YAML to CLI') as self:
advanced:
x_font_path: auto
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('fp',
completed_process.stdout)
expect(cli_option).to(match(r'/usr/share/fonts'))
@ -208,7 +179,7 @@ with description('YAML to CLI') as self:
with it("none specified"):
write_config('''
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('fp',
completed_process.stdout)
expect(cli_option).to(match(r'/usr/share/fonts'))
@ -219,7 +190,7 @@ with description('YAML to CLI') as self:
advanced:
x_font_path: /src
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('fp', completed_process.stdout)
expect(cli_option).to(equal("-fp '/src'"))
@ -239,7 +210,7 @@ with description('YAML to CLI') as self:
network:
interface: 0.0.0.0
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('interface',
completed_process.stdout)
expect(cli_option).to(equal("-interface '0.0.0.0'"))
@ -263,7 +234,7 @@ with description('YAML to CLI') as self:
width: 1024
height: 768
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('geometry',
completed_process.stdout)
expect(cli_option).to(equal("-geometry '1024x768'"))
@ -275,7 +246,7 @@ with description('YAML to CLI') as self:
text:
template: "星街すいせい"
''')
completed_process = run_vncserver()
completed_process = run_vncserver_to_print_xvnc_cli_options()
cli_option = pick_cli_option('DLP_WatermarkText',
completed_process.stdout)
expect(cli_option).to(equal("-DLP_WatermarkText '星街すいせい'"))

View file

@ -10,10 +10,13 @@ use Data::Dumper;
use KasmVNC::DataClumpValidator;
use KasmVNC::Utils;
use KasmVNC::SettingValidation;
our $fetchValueSub;
$KasmVNC::CliOption::dataClumpValidator = KasmVNC::DataClumpValidator->new();
@KasmVNC::CliOption::isActiveCallbacks = ();
our @ISA = ('KasmVNC::SettingValidation');
our $ConfigValue;
our $dataClumpValidator = KasmVNC::DataClumpValidator->new();
our @isActiveCallbacks = ();
sub new {
my ($class, $args) = @_;
@ -56,20 +59,20 @@ sub activate {
sub beforeIsActive {
my $callback = shift;
push @KasmVNC::CliOption::isActiveCallbacks, $callback;
push @isActiveCallbacks, $callback;
}
sub isActiveByCallbacks {
my $self = shift;
all { $_->($self) } @KasmVNC::CliOption::isActiveCallbacks;
all { $_->($self) } @isActiveCallbacks;
}
sub makeKeysWithValuesAccessible {
my $self = shift;
foreach my $name (@{ $self->configKeyNames() }) {
my $value = $self->fetchValue($name);
my $value = $ConfigValue->($name);
$self->{$name} = $value if defined($value);
}
}
@ -100,39 +103,11 @@ sub deriveValue {
my $self = shift;
my $value = $self->{deriveValueSub}->($self);
$self->interpolateEnvVars($value);
}
sub interpolateEnvVars {
my $self = shift;
my $value = shift;
return $value unless defined($value);
while ($value =~ /\$\{(\w+)\}/) {
my $envValue = $ENV{$1};
$value =~ s/\Q$&\E/$envValue/;
}
$value;
}
sub errorMessages {
my $self = shift;
join "\n", @{ $self->{errors} };
interpolateEnvVars($value);
}
# private
sub isValid {
my $self = shift;
$self->validate() unless $self->{validated};
$self->isNoErrorsPresent();
}
sub validate {
my $self = shift;
@ -142,22 +117,16 @@ sub validate {
$self->{validated} = 1;
}
sub isNoErrorsPresent {
my $self = shift;
scalar @{ $self->{errors} } == 0;
}
sub validateDataClump {
my $self = shift;
$KasmVNC::CliOption::dataClumpValidator->validate($self);
$dataClumpValidator->validate($self);
}
sub configValues {
my $self = shift;
map { $self->fetchValue($_->{name}) } @{ $self->{configKeys} };
map { $ConfigValue->($_->{name}) } @{ $self->{configKeys} };
}
sub configValue {
@ -183,22 +152,10 @@ sub hasKey {
first { $_ eq $configKey } @{ $self->configKeyNames() };
}
sub addErrorMessage {
my ($self, $errorMessage) = @_;
push @{ $self->{errors} }, $errorMessage;
}
sub validateConfigValues {
my $self = shift;
map { $_->validate($self) } @{ $self->{configKeys} };
}
sub fetchValue {
my $self = shift;
&$fetchValueSub(shift);
}
1;

View file

@ -53,6 +53,14 @@ sub get {
$value;
}
sub set {
my ($self, $absoluteKey, $value) = @_;
my $path = absoluteKeyToHashPath($absoluteKey);
my $config = $self->{data};
eval "\$config$path = \$value";
}
sub exists {
my ($self, $absoluteKey) = @_;
my $path = absoluteKeyToHashPath($absoluteKey);

View file

@ -0,0 +1,119 @@
package KasmVNC::ConfigEnvVars;
use strict;
use warnings;
use v5.10;
use Data::Dumper;
use Exporter;
@KasmVNC::ConfigEnvVars::ISA = qw(Exporter);
our @EXPORT = (
'OverrideConfigWithConfigEnvVars',
'CheckForUnsupportedConfigEnvVars'
);
use constant ENV_VAR_OVERRIDE_SETTING => "server.allow_environment_variables_to_override_config_settings";
our @configKeyOverrideDenylist = (
ENV_VAR_OVERRIDE_SETTING
);
our $logger;
our %prefixedEnvVars;
our %envVarAllowlist;
our $ConfigValue;
our $SetConfigValue;
our $ShouldPrintTopic;
our $SupportedAbsoluteKeys;
sub IsAllowEnvVarOverride {
my $allowOverride = $ConfigValue->(ENV_VAR_OVERRIDE_SETTING) // "false";
$allowOverride eq "true";
}
sub OverrideConfigWithConfigEnvVars {
return unless IsAllowEnvVarOverride();
%prefixedEnvVars = FetchPrefixedEnvVarsFromEnvironment();
PrepareEnvVarAllowlist();
for my $envVarName (sort keys %prefixedEnvVars) {
my $configKey = $envVarAllowlist{$envVarName};
next unless defined($configKey);
my $envVarValue = GetEnvVarValue($envVarName);
$logger->debug("Overriding $configKey with $envVarName=$envVarValue");
$SetConfigValue->($configKey, $envVarValue);
}
}
sub GetEnvVarValue {
my $envVarName = shift;
$prefixedEnvVars{$envVarName};
}
sub PrepareEnvVarAllowlist {
%envVarAllowlist = ();
my %configKeyOverrideAllowlist = %{ ConfigKeyOverrideAllowlist() };
for my $configKey (keys %configKeyOverrideAllowlist) {
my $allowedEnvVarName = ConvertConfigKeyToEnvVarName($configKey);
$envVarAllowlist{$allowedEnvVarName} = $configKey;
}
}
sub ConfigKeyOverrideAllowlist {
my %configKeyOverrideAllowlist = %{ $SupportedAbsoluteKeys->() };
delete @configKeyOverrideAllowlist{@configKeyOverrideDenylist};
\%configKeyOverrideAllowlist;
}
sub FetchPrefixedEnvVarsFromEnvironment {
my %prefixedEnvVars = map { $_ => $ENV{$_} } grep { /^KVNC_/ } keys %ENV;
PrintPrefixedEnvVars();
%prefixedEnvVars;
}
sub ConvertConfigKeyToEnvVarName {
my $configKey = shift;
my $envVarName = $configKey;
$envVarName =~ s/\./_/g;
$envVarName = "KVNC_$envVarName";
$envVarName = uc $envVarName;
$logger->debug("$configKey -> $envVarName");
$envVarName;
}
sub PrintPrefixedEnvVars {
$logger->debug("Found KVNC_ env vars:");
for my $envVarName (sort keys %prefixedEnvVars) {
$logger->debug("$envVarName=$prefixedEnvVars{$envVarName}");
}
}
sub CheckForUnsupportedConfigEnvVars {
return unless IsAllowEnvVarOverride();
my @unsupportedEnvVars =
grep(!defined($envVarAllowlist{$_}), keys %prefixedEnvVars);
return if (scalar @unsupportedEnvVars == 0);
if ($ShouldPrintTopic->("validation")) {
$logger->warn("Unsupported config env vars found:");
$logger->warn(join("\n", @unsupportedEnvVars));
$logger->warn();
}
exit 1;
}
1;

View file

@ -8,7 +8,7 @@ use Data::Dumper;
use KasmVNC::Utils;
our $fetchValueSub;
our $ConfigValue;
use constant {
INT => 0,
@ -86,18 +86,12 @@ sub isValueBlank {
!defined($value) || $value eq "";
}
sub fetchValue {
my $self = shift;
&$fetchValueSub(shift);
}
sub constructErrorMessage {
my $self = shift;
my $staticErrorMessage = shift;
my $name = $self->{name};
my $value = join ", ", @{ listify($self->fetchValue($name)) };
my $value = join ", ", @{ listify($ConfigValue->($name)) };
"$name '$value': $staticErrorMessage";
}
@ -117,7 +111,7 @@ sub isValidBoolean {
sub value {
my $self = shift;
$self->fetchValue($self->{name});
$ConfigValue->($self->{name});
}
our @EXPORT_OK = ('INT', 'STRING', 'BOOLEAN');

View file

@ -0,0 +1,57 @@
package KasmVNC::ConfigSetting;
use strict;
use warnings;
use v5.10;
use KasmVNC::SettingValidation;
our @ISA = ('KasmVNC::SettingValidation');
our $ConfigValue;
sub new {
my ($class, $args) = @_;
my $self = bless {
configKey => $args->{configKey},
errors => []
}, $class;
}
sub toValue {
my $self = shift;
$self->deriveValue();
}
sub deriveValue {
my $self = shift;
my $configKeyName = $self->{configKey}->{name};
my $value = $ConfigValue->($configKeyName);
interpolateEnvVars($value);
}
sub configKeyNames {
my $self = shift;
[$self->{configKey}->{name}];
}
# private
sub validate {
my $self = shift;
$self->validateConfigValue();
$self->{validated} = 1;
}
sub validateConfigValue {
my $self = shift;
$self->{configKey}->validate($self);
}
1;

View file

@ -0,0 +1,33 @@
package KasmVNC::SettingValidation;
use strict;
use warnings;
use v5.10;
sub isValid {
my $self = shift;
$self->validate() unless $self->{validated};
$self->isNoErrorsPresent();
}
sub errorMessages {
my $self = shift;
join "\n", @{ $self->{errors} };
}
sub isNoErrorsPresent {
my $self = shift;
scalar @{ $self->{errors} } == 0;
}
sub addErrorMessage {
my ($self, $errorMessage) = @_;
push @{ $self->{errors} }, $errorMessage;
}
1;

View file

@ -11,7 +11,7 @@ use Exporter;
@KasmVNC::Utils::ISA = qw(Exporter);
our @EXPORT = ('listify', 'flatten', 'isBlank', 'isPresent', 'deriveBoolean',
'printStackTrace');
'printStackTrace', 'interpolateEnvVars');
sub listify {
# Implementation based on Hyper::Functions
@ -73,4 +73,17 @@ sub containsWideSymbols {
$string =~ /[^\x00-\xFF]/;
}
sub interpolateEnvVars {
my $value = shift;
return $value unless defined($value);
while ($value =~ /\$\{(\w+)\}/) {
my $envValue = $ENV{$1};
$value =~ s/\Q$&\E/$envValue/;
}
$value;
}
1;

View file

@ -153,6 +153,7 @@ server:
no_user_session_timeout: never
active_user_session_timeout: never
inactive_user_session_timeout: never
allow_environment_variables_to_override_config_settings: false
command_line:
prompt: true

View file

@ -43,6 +43,7 @@ use DateTime;
use DateTime::TimeZone;
use KasmVNC::CliOption;
use KasmVNC::ConfigSetting;
use KasmVNC::ConfigKey;
use KasmVNC::PatternValidator;
use KasmVNC::EnumValidator;
@ -54,6 +55,8 @@ use KasmVNC::TextUI;
use KasmVNC::Utils;
use KasmVNC::Logger;
use KasmVNC::ConfigEnvVars;
use constant {
NO_ARG_VALUE => 0,
REQUIRED_ARG_VALUE => 1,
@ -71,9 +74,18 @@ ParseAndProcessCliOptions();
PrepareLoggingAndXvncKillingFramework();
CreateUserConfigIfNeeded();
DefineConfigToCLIConversion();
DefinePossibleConfigSettings();
LoadConfigs();
OverrideConfigWithConfigEnvVars();
InterpolateEnvVarsIntoConfigValues();
ValidateConfigValues();
CheckForUnsupportedConfigEnvVars();
CheckForUnsupportedConfigKeys();
ActivateConfigToCLIConversion();
SetAppSettingsFromConfigAndCli();
DisableLegacyVncAuth();
AllowXProgramsToConnectToXvnc();
@ -1178,6 +1190,11 @@ sub DefineFilePathsAndStuff {
$KasmVNC::Users::vncPasswdBin = $exedir . "kasmvncpasswd";
$KasmVNC::Users::logger = $logger;
$KasmVNC::Config::logger = $logger;
$KasmVNC::ConfigEnvVars::logger = $logger;
$KasmVNC::ConfigEnvVars::SupportedAbsoluteKeys = \&SupportedAbsoluteKeys;
$KasmVNC::ConfigEnvVars::ConfigValue = \&ConfigValue;
$KasmVNC::ConfigEnvVars::SetConfigValue = \&SetConfigValue;
$KasmVNC::ConfigEnvVars::ShouldPrintTopic = \&ShouldPrintTopic;
$vncSystemConfigDir = "/etc/kasmvnc";
if ($ENV{KASMVNC_DEVELOPMENT}) {
@ -1227,8 +1244,9 @@ sub limitVncModeOptions {
}
sub DefineConfigToCLIConversion {
$KasmVNC::CliOption::fetchValueSub = \&ConfigValue;
$KasmVNC::ConfigKey::fetchValueSub = \&ConfigValue;
$KasmVNC::CliOption::ConfigValue = \&ConfigValue;
$KasmVNC::ConfigSetting::ConfigValue = \&ConfigValue;
$KasmVNC::ConfigKey::ConfigValue = \&ConfigValue;
my $regionValidator = KasmVNC::PatternValidator->new({
pattern => qr/^(-)?\d+(%)?$/,
@ -2700,7 +2718,7 @@ sub ShouldPrintTopic {
sub SupportedAbsoluteKeys {
my @supportedAbsoluteKeys =
map { $_->configKeyNames() } @allCliOptions;
map { $_->configKeyNames() } (@allCliOptions, @configSettings);
@supportedAbsoluteKeys = flatten(@supportedAbsoluteKeys);
my %result = map { $_ => 1 } @supportedAbsoluteKeys;
@ -2724,7 +2742,6 @@ sub SupportedSectionsFromAbsoluteKey {
sub StartXvncOrExit {
$cmd = ConstructXvncCmd();
CheckForUnsupportedConfigKeys();
CheckSslCertReadable();
say $cmd if ($debug || IsDryRun()) && ShouldPrintTopic("xvnc-cmd");
@ -2877,6 +2894,13 @@ sub ConfigValue {
return $configRef->get($absoluteKey);
}
sub SetConfigValue {
my ($absoluteKey, $value, $configRef) = @_;
$configRef ||= $mergedConfig;
$configRef->set($absoluteKey, $value);
}
sub DerivedValue {
my $absoluteKey = shift;
@ -2951,7 +2975,6 @@ sub ConstructOptFromConfig{
}
sub ConfigToCmd {
ValidateConfig();
%optFromConfig = %{ ConstructOptFromConfig() };
my @cmd = map { $cliArgMap{$_}->toString() } (keys %optFromConfig);
@ -2960,20 +2983,20 @@ sub ConfigToCmd {
return $cmdStr;
}
sub ValidateConfig {
foreach my $cliOption (@allCliOptions) {
ValidateCliOption($cliOption);
sub ValidateConfigValues {
foreach my $setting (@allCliOptions, @configSettings) {
ValidateSetting($setting);
}
}
sub ValidateCliOption {
my $cliOption = $_[0];
sub ValidateSetting {
my $setting = $_[0];
return if ($cliOption->isValid());
return if ($setting->isValid());
if (ShouldPrintTopic("validation")) {
$logger->warn("config errors:");
$logger->warn($cliOption->errorMessages());
$logger->warn($setting->errorMessages());
$logger->warn();
}
@ -3033,3 +3056,34 @@ sub InitLogger {
sub UseUtfStdio {
use open qw( :std :encoding(UTF-8) );
}
sub DefinePossibleConfigSettings {
DefineConfigToCLIConversion();
DefineConfigSettings();
}
sub DefineConfigSettings {
@configSettings = (
KasmVNC::ConfigSetting->new({
configKey => KasmVNC::ConfigKey->new({
name => "server.allow_environment_variables_to_override_config_settings",
type => KasmVNC::ConfigKey::BOOLEAN
})
}),
);
}
sub InterpolateEnvVarsIntoConfigValues {
my @interpolationDenylist = (
"server.allow_environment_variables_to_override_config_settings"
);
my %supportedAbsoluteKeys = %{ SupportedAbsoluteKeys() };
delete @supportedAbsoluteKeys{@interpolationDenylist};
for my $absoluteKey (keys %supportedAbsoluteKeys) {
my $value = ConfigValue($absoluteKey);
my $interpolatedValue = interpolateEnvVars($value);
SetConfigValue($absoluteKey, $interpolatedValue);
}
}