--- loncom/interface/domainprefs.pm 2021/03/06 13:39:54 1.380 +++ loncom/interface/domainprefs.pm 2021/04/18 02:08:46 1.381 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.380 2021/03/06 13:39:54 raeburn Exp $ +# $Id: domainprefs.pm,v 1.381 2021/04/18 02:08:46 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -311,16 +311,16 @@ sub handler { print => \&print_defaults, modify => \&modify_defaults, }, - 'wafproxy' => - { text => 'Web Application Firewall/Reverse Proxy', + 'wafproxy' => + { text => 'Web Application Firewall/Reverse Proxy', help => 'Domain_Configuration_WAF_Proxy', - header => [{col1 => 'Domain server', - col2 => 'Alias for WAF/Reverse Proxy', + header => [{col1 => 'Domain(s)', + col2 => 'Servers and WAF/Reverse Proxy alias(es)', }, - {col1 => 'Setting', - col2 => 'Value',}], + {col1 => 'Domain(s)', + col2 => 'WAF Configuration',}], print => \&print_wafproxy, - modify => \&modify_wafproxy, + modify => \&modify_wafproxy, }, 'passwords' => { text => 'Passwords (Internal authentication)', @@ -855,6 +855,8 @@ sub print_config_box { $output .= <i_javascript($settings); } elsif ($action eq 'proctoring') { $output .= &proctoring_javascript($settings); + } elsif ($action eq 'wafproxy') { + $output .= &wafproxy_javascript($dom); } $output .= ' @@ -2844,6 +2846,121 @@ function toggleLTITools(form,setting,ite ENDSCRIPT } +sub wafproxy_javascript { + my ($dom) = @_; + return <<"ENDSCRIPT"; + + +ENDSCRIPT +} + sub proctoring_javascript { my ($settings) = @_; my (%ordered,$total,%jstext); @@ -7178,32 +7295,43 @@ sub print_wafproxy { my $itemcount = 0; my $datatable; my %servers = &Apache::lonnet::internet_dom_servers($dom); - my (%othercontrol,%otherdoms,%aliases,%values,$setdom); + my (%othercontrol,%otherdoms,%aliases,%values,$setdom,$showdom); my %lt = &wafproxy_titles(); foreach my $server (sort(keys(%servers))) { my $serverhome = &Apache::lonnet::get_server_homeID($servers{$server}); + next if ($serverhome eq ''); my $serverdom; if ($serverhome ne $server) { $serverdom = &Apache::lonnet::host_domain($serverhome); - $othercontrol{$server} = $serverdom; + if (($serverdom ne '') && (&Apache::lonnet::domain($serverdom) ne '')) { + $othercontrol{$server} = $serverdom; + } } else { $serverdom = &Apache::lonnet::host_domain($server); + next if (($serverdom eq '') || (&Apache::lonnet::domain($serverdom) eq '')); if ($serverdom ne $dom) { $othercontrol{$server} = $serverdom; } else { $setdom = 1; if (ref($settings) eq 'HASH') { - %{$values{$dom}} = (); if (ref($settings->{'alias'}) eq 'HASH') { $aliases{$dom} = $settings->{'alias'}; - } - foreach my $item ('ipheader','trusted','vpnint','vpnext') { - $values{$dom}{$item} = $settings->{$item}; + if ($aliases{$dom} ne '') { + $showdom = 1; + } } } } } } + if ($setdom) { + %{$values{$dom}} = (); + if (ref($settings) eq 'HASH') { + foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') { + $values{$dom}{$item} = $settings->{$item}; + } + } + } if (keys(%othercontrol)) { %otherdoms = reverse(%othercontrol); foreach my $domain (keys(%otherdoms)) { @@ -7212,7 +7340,7 @@ sub print_wafproxy { if (ref($config{$domain}) eq 'HASH') { if (ref($config{$domain}{'wafproxy'}) eq 'HASH') { $aliases{$domain} = $config{$domain}{'wafproxy'}{'alias'}; - foreach my $item ('ipheader','trusted','vpnint','vpnext') { + foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') { $values{$domain}{$item} = $config{$domain}{'wafproxy'}{$item}; } } @@ -7221,59 +7349,152 @@ sub print_wafproxy { } if ($position eq 'top') { my %servers = &Apache::lonnet::internet_dom_servers($dom); + my %aliasinfo; foreach my $server (sort(keys(%servers))) { - $itemcount ++; - $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; - $datatable .= ''. - ''. - ''. + ''; if ($othercontrol{$server}) { + $dom_in_effect = $othercontrol{$server}; my $current; if (ref($aliases{$othercontrol{$server}}) eq 'HASH') { $current = $aliases{$othercontrol{$server}{$server}}; } if ($current) { - $datatable .= $current; + $aliasrows .= $current; } else { - $datatable .= &mt('None in effect'); + $aliasrows .= &mt('None in effect'); } - $datatable .= '
('. + $aliasrows .= ''; } else { + $dom_in_effect = $dom; my $current; if (ref($aliases{$dom}) eq 'HASH') { if ($aliases{$dom}{$server}) { $current = $aliases{$dom}{$server}; } } - $datatable .= ''; + $aliasrows .= ''; + } + $aliasrows .= ''; + $aliasinfo{$dom_in_effect} .= $aliasrows; + } + if ($aliasinfo{$dom}) { + my ($onclick,$wafon,$wafoff,$showtable); + $onclick = ' onclick="javascript:toggleWAF();"'; + $wafoff = ' checked="checked"'; + $showtable = ' style="display:none";'; + if ($showdom) { + $wafon = $wafoff; + $wafoff = ''; + $showtable = ' style="display:inline;"'; + } + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable = ''. + ''. + ''; + $itemcount++; + } + if (keys(%othercontrol)) { + foreach my $key (sort(keys(%othercontrol))) { + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable = ''. + ''. + ''; + $itemcount++; } - $datatable .= ''; } } else { if ($setdom) { $itemcount ++; $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; - $datatable .= ''. + my ($nowafstyle,$wafstyle,$curr_remotip,$currwafdisplay,$vpndircheck,$vpnaliascheck, + $currwafvpn,$wafrangestyle); + $wafstyle = ' style="display:none;"'; + $nowafstyle = ' style="display:table-row;"'; + $currwafdisplay = ' style="display: none"'; + $wafrangestyle = ' style="display: none"'; + $curr_remotip = 'n'; + if ($showdom) { + $wafstyle = ' style="display:table-row;"'; + $nowafstyle = ' style="display:none;"'; + if (keys(%{$values{$dom}})) { + if ($values{$dom}{remoteip} =~ /^[nmh]$/) { + $curr_remotip = $values{$dom}{remoteip}; + } + if ($curr_remotip eq 'h') { + $currwafdisplay = ' style="display:table-row"'; + $wafrangestyle = ' style="display:inline-block;"'; + } + } + if (($values{$dom}{'vpnint'} ne '') || ($values{$dom}{'vpnext'} ne '')) { + $vpndircheck = ' checked="checked"'; + $currwafvpn = ' style="display:table-row;"'; + $wafrangestyle = ' style="display:inline-block;"'; + } else { + $vpnaliascheck = ' checked="checked"'; + $currwafvpn = ' style="display:none;"'; + } + } + $datatable .= ''. + ''. + ''. + ''. + ''. ''. - ''; } } } @@ -7302,10 +7523,25 @@ sub print_wafproxy { sub wafproxy_titles { return &Apache::lonlocal::texthash( - vpnint => 'Internal IP Range(s) for VPN sessions', - vpnext => 'IP Range for backend WAF connections', - trusted => 'Trusted IP range(s)', - ipheader => 'Custom request header', + remoteip => "Method for determining user's IP", + ipheader => 'Request header containing remote IP', + trusted => 'Trusted IP range(s)', + vpnaccess => 'Access from institutional VPN', + vpndirect => 'via regular hostname (no WAF)', + vpnaliased => 'via aliased hostname (WAF)', + vpnint => 'Internal IP Range(s) for VPN sessions', + vpnext => 'IP Range(s) for backend WAF connections', + ssloptions => 'Forwarding http/https', + alltossl => 'WAF forwards both http and https requests to https', + ssltossl => 'WAF forwards http requests to http and https to https', + ); +} + +sub remoteip_methods { + return &Apache::lonlocal::texthash( + m => 'Use Apache mod_remoteip', + h => 'Use headers parsed by LON-CAPA', + n => 'Not in use', ); } @@ -19582,26 +19818,33 @@ sub modify_wafproxy { my $serverdom = &Apache::lonnet::host_domain($server); if ($serverdom eq $dom) { $canset{$server} = 1; - if (ref($domconfig{'wafproxy'}) eq 'HASH') { - %{$values{$dom}} = (); - if (ref($domconfig{'wafproxy'}{'alias'}) eq 'HASH') { - %curralias = %{$domconfig{'wafproxy'}{'alias'}}; - } - foreach my $item ('ipheader','trusted','exempt') { - $currvalue{$item} = $domconfig{'wafproxy'}{$item}; - } - } } } } + if (ref($domconfig{'wafproxy'}) eq 'HASH') { + %{$values{$dom}} = (); + if (ref($domconfig{'wafproxy'}{'alias'}) eq 'HASH') { + %curralias = %{$domconfig{'wafproxy'}{'alias'}}; + } + foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') { + $currvalue{$item} = $domconfig{'wafproxy'}{$item}; + } + } my $output; if (keys(%canset)) { %{$wafproxy{'alias'}} = (); foreach my $key (sort(keys(%canset))) { - $wafproxy{'alias'}{$key} = $env{'form.wafproxy_alias_'.$key}; - $wafproxy{'alias'}{$key} =~ s/^\s+|\s+$//g; - if ($wafproxy{'alias'}{$key} ne $curralias{$key}) { - $changes{'alias'} = 1; + if ($env{'form.wafproxy_'.$dom}) { + $wafproxy{'alias'}{$key} = $env{'form.wafproxy_alias_'.$key}; + $wafproxy{'alias'}{$key} =~ s/^\s+|\s+$//g; + if ($wafproxy{'alias'}{$key} ne $curralias{$key}) { + $changes{'alias'} = 1; + } + } else { + $wafproxy{'alias'}{$key} = ''; + if ($curralias{$key}) { + $changes{'alias'} = 1; + } } if ($wafproxy{'alias'}{$key} eq '') { if ($curralias{$key}) { @@ -19616,21 +19859,39 @@ sub modify_wafproxy { # Localization for values in %warn occus in &mt() calls separately. my %warn = ( trusted => 'trusted IP range(s)', - exempt => 'exempt IP range(s)', + vpnint => 'internal IP range(s) for VPN sessions(s)', + vpnext => 'IP range(s) for backend WAF connections', ); - foreach my $item ('ipheader','trusted','exempt') { + foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') { my $possible = $env{'form.wafproxy_'.$item}; $possible =~ s/^\s+|\s+$//g; if ($possible ne '') { - if ($item eq 'ipheader') { - $wafproxy{$item} = $possible; + if ($item eq 'remoteip') { + if ($possible =~ /^[mhn]$/) { + $wafproxy{$item} = $possible; + } + } elsif ($item eq 'ipheader') { + if ($wafproxy{'remoteip'} eq 'h') { + $wafproxy{$item} = $possible; + } } else { my (@ok,$count); - $possible =~ s/[\r\n]+/\s/g; - $possible =~ s/\s*-\s*/-/g; - $possible =~ s/\s+/,/g; + if (($item eq 'vpnint') || ($item eq 'vpnext')) { + unless ($env{'form.wafproxy_vpnaccess'}) { + $possible = ''; + } + } elsif ($item eq 'trusted') { + unless ($wafproxy{'remoteip'} eq 'h') { + $possible = ''; + } + } + unless ($possible eq '') { + $possible =~ s/[\r\n]+/\s/g; + $possible =~ s/\s*-\s*/-/g; + $possible =~ s/\s+/,/g; + } $count = 0; - if ($possible) { + if ($possible ne '') { foreach my $poss (split(/\,/,$possible)) { $count ++; if (&validate_ip_pattern($poss)) { @@ -19647,15 +19908,22 @@ sub modify_wafproxy { $diff,$warn{$item}). ''); } - if ($wafproxy{$item} ne $currvalue{$item}) { - $changes{$item} = 1; - } } } - } else { - if ($currvalue{$item}) { + if ($wafproxy{$item} ne $currvalue{$item}) { $changes{$item} = 1; } + } elsif ($currvalue{$item}) { + $changes{$item} = 1; + } + } + } else { + if (keys(%curralias)) { + $changes{'alias'} = 1; + } + if (keys(%currvalue)) { + foreach my $key (keys(%currvalue)) { + $changes{$key} = 1; } } } @@ -19668,7 +19936,7 @@ sub modify_wafproxy { if ($putresult eq 'ok') { my $cachetime = 24*60*60; my (%domdefaults,$updatedomdefs); - foreach my $item ('ipheader','trusted','exempt') { + foreach my $item ('ipheader','trusted','vpnint','vpnext') { if ($changes{$item}) { unless ($updatedomdefs) { %domdefaults = &Apache::lonnet::get_domain_defaults($dom); @@ -19705,7 +19973,7 @@ sub modify_wafproxy { } } $output = &mt('Changes were made to Web Application Firewall/Reverse Proxy').'
'.&mt('Hostname').': '. - &Apache::lonnet::hostname($server).''; + $itemcount ++; + my $dom_in_effect; + my $aliasrows = '
'.&mt('Hostname').': '. + &Apache::lonnet::hostname($server).'('. &mt('WAF/Reverse Proxy controlled by domain: [_1]', - ''.$othercontrol{$server}.'').''; + ''.$othercontrol{$server}.'').''.&mt('WAF/Reverse Proxy Alias').': '. + '
'.&mt('Domain: [_1]',''.$dom.'').'
'. + ''.&mt('WAF in use?').' '.(' 'x2).'
'. + ''.$aliasinfo{$dom}. + '
'.&mt('Domain: [_1]',''.$key.'').''.$aliasinfo{$key}. + '
'.&mt('Domain: [_1]',''.$dom.'').''.&mt('WAF not in use, nothing to set').'
'.&mt('Domain: [_1]',''.$dom.'').'

'. - &mt('Format for comma separated IP blocks').':
'. - &mt('A.B.C.D/N or A.B.C.D - E.F.G.H').'
'; - foreach my $item ('ipheader','trusted','vpnint','vpnext') { - $datatable .= ''. - ''; + '
'.&mt('Format for comma separated IP ranges').':
'. + &mt('A.B.C.D/N or A.B.C.D-E.F.G.H').'
'. + ''; } @@ -7284,15 +7505,15 @@ sub print_wafproxy { $datatable .= ''. ''. ''; + $datatable .= '
'.$lt{$item}.': '; - if ($item eq 'ipheader') { - $datatable .= ''; - - } else { - $datatable .= ''; - } - $datatable .= '
'. + ''. + ''."\n". + ''."\n". + ''."\n". + ''."\n". + ''. + ''; + foreach my $item ('vpnint','vpnext') { + $datatable .= ''. + ''."\n"; } $datatable .= '
'.$lt{'remoteip'}.': '. + '
'. + $lt{'ipheader'}.': '. + ''. + '
'. + $lt{'trusted'}.': '. + ''. + '

'.$lt{'vpnaccess'}.':
'. + ''.(' 'x2). + '
'.$lt{$item}.': '. + ''. + '
'.&mt('Domain: [_1]',$domain).''; - foreach my $item ('ipheader','trusted','vpnint','vpnext') { + foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') { my $showval = &mt('None'); if ($values{$domain}{$item}) { - $showval = $values{$domain}{$item}; + $showval = $values{$domain}{$item}; } $datatable .= ''. ''; } - $datatable .= '
'.$lt{$item}.': '.$showval.'