From b26ef272e358650a507334899172d496eec7193c Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Wed, 10 Apr 2024 15:43:32 +0200 Subject: [PATCH] ovpnmain.cgi: Refactor the connection listing Signed-off-by: Michael Tremer --- html/cgi-bin/ovpnmain.cgi | 554 +++++++++--------- html/html/themes/ipfire/include/css/style.css | 18 +- 2 files changed, 305 insertions(+), 267 deletions(-) diff --git a/html/cgi-bin/ovpnmain.cgi b/html/cgi-bin/ovpnmain.cgi index 12a3f0988..98e7512ca 100755 --- a/html/cgi-bin/ovpnmain.cgi +++ b/html/cgi-bin/ovpnmain.cgi @@ -937,6 +937,60 @@ sub writecollectdconf { &General::system("/usr/local/bin/collectdctrl", "restart"); } +sub openvpn_status($) { + my $port = shift; + + # Create a new Telnet session + my $telnet = new Net::Telnet( + Port => $port, + Timeout => 1, + Errmode => "return", + ); + + # Connect + $telnet->open("127.0.0.1"); + + # Send a command + my @output = $telnet->cmd( + String => "state", + Prompt => "/(END.*\n|ERROR:.*\n)/" + ); + + my ($time, $status) = split(/\,/, $output[1]); + + ### + #CONNECTING -- OpenVPN's initial state. + #WAIT -- (Client only) Waiting for initial response from server. + #AUTH -- (Client only) Authenticating with server. + #GET_CONFIG -- (Client only) Downloading configuration options from server. + #ASSIGN_IP -- Assigning IP address to virtual network interface. + #ADD_ROUTES -- Adding routes to system. + #CONNECTED -- Initialization Sequence Completed. + #RECONNECTING -- A restart has occurred. + #EXITING -- A graceful exit is in progress. + #### + + if ($status eq "CONNECTING") { + return "DISCONNECTED"; + } elsif ($status eq "WAIT") { + return "DISCONNECTED"; + } elsif ($status eq "AUTH") { + return "DISCONNECTED"; + } elsif ($status eq "GET_CONFIG") { + return "DISCONNECTED"; + } elsif ($status eq "ASSIGN_IP") { + return "DISCONNECTED"; + } elsif ($status eq "ADD_ROUTES") { + return "DISCONNECTED"; + } elsif ($status eq "RECONNECTING") { + return "CONNECTED"; + } elsif ($status eq "EXITING") { + return "DISCONNECTED"; + } + + return $status; +} + ### ### Save Advanced options ### @@ -5142,304 +5196,280 @@ END &Header::closebox(); - if ( -f "${General::swroot}/ovpn/ca/cacert.pem" ) { -### -#$Lang::tr{'remark'}
L2089 -### - &Header::openbox('100%', 'LEFT', $Lang::tr{'connection status and controlc' }); - ; - my $id = 0; - my $gif; - my $col1=""; - my $lastnet; - foreach my $key (sort { ncmp ($confighash{$a}[32],$confighash{$b}[32]) } sort { ncmp ($confighash{$a}[1],$confighash{$b}[1]) } keys %confighash) { - if ($confighash{$key}[32] eq "" && $confighash{$key}[3] eq 'net' ){$confighash{$key}[32]=$Lang::tr{'fwhost OpenVPN N-2-N'};} - if ($confighash{$key}[32] eq "dynamic"){$confighash{$key}[32]=$Lang::tr{'ccd dynrange'};} - if($id == 0){ - print"$confighash{$key}[32]"; - print < - - $Lang::tr{'name'} - $Lang::tr{'type'} - $Lang::tr{'remark'} - $Lang::tr{'status'} - $Lang::tr{'action'} - -END - } - if ($id > 0 && $lastnet ne $confighash{$key}[32]){ - print "
"; - print"$confighash{$key}[32]"; - print < - - $Lang::tr{'name'} - $Lang::tr{'type'} - $Lang::tr{'remark'} - $Lang::tr{'status'} - $Lang::tr{'action'} - + print < + + + $Lang::tr{'name'} + + + $Lang::tr{'type'} + + + $Lang::tr{'remark'} + + + $Lang::tr{'status'} + + + $Lang::tr{'action'} + + END - } - if ($confighash{$key}[0] eq 'on') { $gif = 'on.gif'; } else { $gif = 'off.gif'; } - # Create some simple booleans to check the status - my $hasExpired; - my $expiresSoon; + my $gif; - # Fetch information about the certificate for non-N2N connections only - if ($confighash{$key}[3] ne 'net') { - my @cavalid = &General::system_output("/usr/bin/openssl", "x509", "-text", - "-in", "${General::swroot}/ovpn/certs/$confighash{$key}[1]cert.pem"); + foreach my $key (sort { ncmp ($confighash{$a}[1],$confighash{$b}[1]) } keys %confighash) { + my $status = $confighash{$key}[0]; + my $name = $confighash{$key}[1]; + my $type = $confighash{$key}[3]; - my $expiryDate = 0; + # Create some simple booleans to check the status + my $hasExpired = 0; + my $expiresSoon = 0; - # Parse the certificate information - foreach my $line (@cavalid) { - if ($line =~ /Not After : (.*)[\n]/) { - $expiryDate = &Date::Parse::str2time($1); - last; + # Fetch information about the certificate for non-N2N connections only + if ($confighash{$key}[3] ne 'net') { + my @cavalid = &General::system_output("/usr/bin/openssl", "x509", "-text", + "-in", "${General::swroot}/ovpn/certs/$confighash{$key}[1]cert.pem"); + + my $expiryDate = 0; + + # Parse the certificate information + foreach my $line (@cavalid) { + if ($line =~ /Not After : (.*)[\n]/) { + $expiryDate = &Date::Parse::str2time($1); + last; + } } - } - # Calculate the remaining time - my $remainingTime = $expiryDate - time(); + # Calculate the remaining time + my $remainingTime = $expiryDate - time(); - # Determine whether the certificate has already expired, or will so soon - $hasExpired = ($remainingTime <= 0); - $expiresSoon = ($remainingTime <= 30 * 24 * 3600); + # Determine whether the certificate has already expired, or will so soon + $hasExpired = ($remainingTime <= 0); + $expiresSoon = ($remainingTime <= 30 * 24 * 3600); + } - } else { - # Populate booleans with dummy values for N2N connections (#13066) - $hasExpired = 0; - $expiresSoon = 0; - } + my @classes = (); - print ""; + # Highlight the row if the certificate has expired/will expire soon + if ($hasExpired || $expiresSoon) { + push(@classes, "is-warning"); + } - if ($hasExpired || $expiresSoon) { - $col="bgcolor='$Header::color{'color14'}'"; - } elsif ($id % 2) { - $col="bgcolor='$Header::color{'color20'}'"; - } else { - $col="bgcolor='$Header::color{'color22'}'"; - } - print "$confighash{$key}[1]"; - if ($hasExpired) { - print " ($Lang::tr{'openvpn cert has expired'})"; - } elsif ($expiresSoon) { - print " ($Lang::tr{'openvpn cert expires soon'})"; - } - print ""; - print "" . $Lang::tr{"$confighash{$key}[3]"} . " (" . $Lang::tr{"$confighash{$key}[4]"} . ")"; - print "$confighash{$key}[25]"; - $col1="bgcolor='${Header::colourred}'"; - my $active = "$Lang::tr{'capsclosed'}"; - - if ($confighash{$key}[0] eq 'off') { - $col1="bgcolor='${Header::colourblue}'"; - $active = "$Lang::tr{'capsclosed'}"; - } else { - if ($confighash{$key}[3] eq 'net') { - - if (-e "/var/run/$confighash{$key}[1]n2n.pid") { - my @output = ""; - my @tustate = ""; - my $tport = $confighash{$key}[22]; - my $tnet = new Net::Telnet ( Timeout=>5, Errmode=>'return', Port=>$tport); - if ($tport ne '') { - $tnet->open('127.0.0.1'); - @output = $tnet->cmd(String => 'state', Prompt => '/(END.*\n|ERROR:.*\n)/'); - @tustate = split(/\,/, $output[1]); -### -#CONNECTING -- OpenVPN's initial state. -#WAIT -- (Client only) Waiting for initial response from server. -#AUTH -- (Client only) Authenticating with server. -#GET_CONFIG -- (Client only) Downloading configuration options from server. -#ASSIGN_IP -- Assigning IP address to virtual network interface. -#ADD_ROUTES -- Adding routes to system. -#CONNECTED -- Initialization Sequence Completed. -#RECONNECTING -- A restart has occurred. -#EXITING -- A graceful exit is in progress. -#### - - if (($tustate[1] eq 'CONNECTED') || ($tustate[1] eq 'WAIT')) { - $col1="bgcolor='${Header::colourgreen}'"; - $active = "$Lang::tr{'capsopen'}"; - }else { - $col1="bgcolor='${Header::colourred}'"; - $active = "$tustate[1]"; + # Start a new row + print ""; + + # Show the name of the connection + print " $name"; + if ($hasExpired) { + print " ($Lang::tr{'openvpn cert has expired'})"; + } elsif ($expiresSoon) { + print " ($Lang::tr{'openvpn cert expires soon'})"; } - } - } - }else { - - my $cn; - my @match = (); - foreach my $line (@status) { - chomp($line); - if ( $line =~ /^(.+),(\d+\.\d+\.\d+\.\d+\:\d+),(\d+),(\d+),(.+)/) { - @match = split(m/^(.+),(\d+\.\d+\.\d+\.\d+\:\d+),(\d+),(\d+),(.+)/, $line); - if ($match[1] ne "Common Name") { - $cn = $match[1]; + print ""; + + # Show type + print "$Lang::tr{$type}"; + + # Show remarks + print "$confighash{$key}[25]"; + + my $connstatus = "DISCONNECTED"; + + # Disabled Connections + if ($status eq "off") { + $connstatus = "DISABLED"; + + # N2N Connections + } elsif ($type eq "net") { + if (-e "/var/run/${name}n2n.pid") { + my $port = $confighash{$key}[22]; + + if ($port ne "") { + $connstatus = &openvpn_status($confighash{$key}[22]); } - if ($cn eq "$confighash{$key}[2]") { - $col1="bgcolor='${Header::colourgreen}'"; - $active = "$Lang::tr{'capsopen'}"; + } + + # RW Connections + } elsif ($type eq "host") { + my $cn; + + foreach my $line (@status) { + chomp($line); + + if ($line =~ /^(.+),(\d+\.\d+\.\d+\.\d+\:\d+),(\d+),(\d+),(.+)/) { + my @match = split(m/^(.+),(\d+\.\d+\.\d+\.\d+\:\d+),(\d+),(\d+),(.+)/, $line); + + if ($match[1] ne "Common Name") { + $cn = $match[1]; + } + + if ($cn eq "$confighash{$key}[2]") { + $connstatus = "CONNECTED"; + } } } - } - } -} + } + if ($connstatus eq "DISABLED") { + print "$Lang::tr{'capsclosed'}"; + } elsif ($connstatus eq "CONNECTED") { + print "$Lang::tr{'capsopen'}"; + } elsif ($connstatus eq "DISCONNECTED") { + print "$Lang::tr{'capsclosed'}"; + } else { + print "$connstatus"; + } - if ($confighash{$key}[41] eq "pass") { - print <$active + # Download Configuration + if ($confighash{$key}[41] eq "pass") { + print < +
+ + + + +
+ +END -
- - - - -
+ } elsif ($confighash{$key}[41] eq "no-pass") { + print < +
+ + + + +
+ END - ; } elsif ($confighash{$key}[41] eq "no-pass") { - print <$active + } else { + print ""; + } -
- - - - -
+ # Show Certificate + if ($confighash{$key}[4] eq 'cert') { + print < +
+ + + +
+ END - ; } else { - print " "; - } - if ($confighash{$key}[4] eq 'cert') { - print < - - - - + } else { + print ""; + } + + # Show OTP QR code + if ($confighash{$key}[43] eq 'on') { + print < +
+ + + +
+ END - ; } else { - print " "; - } + } else { + print ""; + } - if ($confighash{$key}[43] eq 'on') { - print < - - - - + # Download Certificate + if ($confighash{$key}[4] eq 'cert' && -f "${General::swroot}/ovpn/certs/$confighash{$key}[1].p12") { + print < +
+ + + +
+ END -; } else { - print " "; - } - if ($confighash{$key}[4] eq 'cert' && -f "${General::swroot}/ovpn/certs/$confighash{$key}[1].p12") { - print < - - - - + } elsif ($confighash{$key}[4] eq 'cert') { + print < +
+ + + +
+ END - ; } elsif ($confighash{$key}[4] eq 'cert') { - print < - - - - + } else { + print ""; + } + + if ($status eq 'on') { + $gif = 'on.gif'; + } else { + $gif = 'off.gif'; + } + + print < +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + END - ; } else { - print " "; + } - print < - - - - - -
- - - -
-
- - - -
- -END - ; - $id++; - $lastnet = $confighash{$key}[32]; - } - print""; - ; + print""; - # If the config file contains entries, print Key to action icons - if ( $id ) { + # Show controls print < - -   $Lang::tr{'legend'}: -     ?RELOAD - $Lang::tr{'dl client arch insecure'} -     ?RELOAD - $Lang::tr{'dl client arch'} -     $Lang::tr{ - $Lang::tr{'show certificate'} -     $Lang::tr{ - $Lang::tr{'show otp qrcode'} - - -   -     ?FLOPPY - $Lang::tr{'download certificate'} -   ?OFF - $Lang::tr{'click to enable'} -   $Lang::tr{ - $Lang::tr{'click to disable'} - -     $Lang::tr{ - $Lang::tr{'edit'} -     $Lang::tr{ - $Lang::tr{'remove'} - -
+ + + + +
+
+ + +
+
END - ; - } - print < -
- - - - -
- -END - ; &Header::closebox(); - } # CA/key listing &Header::openbox('100%', 'LEFT', "$Lang::tr{'certificate authorities'}"); diff --git a/html/html/themes/ipfire/include/css/style.css b/html/html/themes/ipfire/include/css/style.css index 24ef00e4d..c5fc7dcef 100644 --- a/html/html/themes/ipfire/include/css/style.css +++ b/html/html/themes/ipfire/include/css/style.css @@ -5,6 +5,8 @@ --color-red-invert : #ffffff; --color-blue : #333399; --color-blue-invert : #ffffff; + --color-yellow : #ffd700; + --color-yellow-invert : #000000; --color-grey : #d6d6d6; --color-light-grey : #f0f0f0; @@ -359,6 +361,11 @@ table.form tr.action td { width: 100%; } +.tbl tr.is-warning td { + color: var(--color-yellow-invert) !important; + background-color: var(--color-yellow) !important; +} + .tbl th { color: #ffffff; border-top: 1px solid #363636; @@ -366,6 +373,9 @@ table.form tr.action td { background: #363636; padding-left: 0.5em; padding-right: 0.5em; + + text-align: center; + font-weight: bold; } .tbl th[scope=row] { @@ -433,16 +443,14 @@ table.form tr.action td { font-weight: bold; } -.tbl .status.is-running { +.tbl .status.is-running, .tbl .status.is-connected { background-color: var(--color-green); color: var(--color-green-invert); } -.tbl .status.is-stopped { +.tbl .status.is-stopped, .tbl .status.is-disconnected { background-color: var(--color-red); color: var(--color-red-invert); - - width: 33%; } .tbl .status.is-vulnerable { @@ -450,7 +458,7 @@ table.form tr.action td { color: var(--color-red-invert); } -.tbl .status.is-mitigation { +.tbl .status.is-mitigation, .tbl .status.is-disabled { background-color: var(--color-blue); color: var(--color-blue-invert); } -- 2.39.5