From ed89f965bf75c9e0080db6138ee2861ccfbdbd77 Mon Sep 17 00:00:00 2001 From: Vincent Li Date: Tue, 25 Jun 2024 17:20:58 +0000 Subject: [PATCH] keepalived UI: add keepalived UI BPFire red0 does not support multicast, need to have unicast peer configured, then the virtual ipaddress can be added to red0 interface. the UI requires /var/ipfire/keepalived/runsettings /var/ipfire/keepalived/settings to be created, so add them lfs/configroot Signed-off-by: Vincent Li --- config/cfgroot/general-functions.pl | 8 + config/menu/60-ipfire.menu | 6 + config/rootfiles/common/web-user-interface | 1 + html/cgi-bin/keepalived.cgi | 351 +++++++++++++++++++++ langs/en/cgi-bin/en.pl | 12 + langs/zh/cgi-bin/zh.pl | 12 + lfs/configroot | 2 +- 7 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 html/cgi-bin/keepalived.cgi diff --git a/config/cfgroot/general-functions.pl b/config/cfgroot/general-functions.pl index 52dd5b584..875ff5103 100644 --- a/config/cfgroot/general-functions.pl +++ b/config/cfgroot/general-functions.pl @@ -366,6 +366,14 @@ sub validip } } +sub validnum { + my $num = shift; + if ($num =~ /^\d+$/) { + return 1; + } + return 0; +} + sub validmask { my $mask = shift; diff --git a/config/menu/60-ipfire.menu b/config/menu/60-ipfire.menu index a686fe1f0..89dd6968a 100644 --- a/config/menu/60-ipfire.menu +++ b/config/menu/60-ipfire.menu @@ -21,6 +21,12 @@ 'title' => "$Lang::tr{'loxilb fw'}", 'enabled' => 1, }; + $subipfire->{'50.keepalived'} = { + 'caption' => $Lang::tr{'keepalived'}, + 'uri' => '/cgi-bin/keepalived.cgi', + 'title' => "$Lang::tr{'keepalived'}", + 'enabled' => 1, + }; $subipfire->{'80.pakfire'} = {'caption' => 'Pakfire', 'uri' => '/cgi-bin/pakfire.cgi', 'title' => "Pakfire", diff --git a/config/rootfiles/common/web-user-interface b/config/rootfiles/common/web-user-interface index c943cc3f9..47a8a9d1e 100644 --- a/config/rootfiles/common/web-user-interface +++ b/config/rootfiles/common/web-user-interface @@ -94,6 +94,7 @@ srv/web/ipfire/cgi-bin/ddos.cgi srv/web/ipfire/cgi-bin/loxilb.cgi srv/web/ipfire/cgi-bin/loxilbconfig.cgi srv/web/ipfire/cgi-bin/loxilbfw.cgi +srv/web/ipfire/cgi-bin/keepalived.cgi #srv/web/ipfire/html srv/web/ipfire/html/blob.gif #srv/web/ipfire/html/captive diff --git a/html/cgi-bin/keepalived.cgi b/html/cgi-bin/keepalived.cgi new file mode 100644 index 000000000..434e2b6a6 --- /dev/null +++ b/html/cgi-bin/keepalived.cgi @@ -0,0 +1,351 @@ +#!/usr/bin/perl +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Team # +# Copyright (C) 2024 BPFire # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +use strict; +use experimental 'smartmatch'; + +# enable only the following on debugging purpose +#use warnings; +#use CGI::Carp 'fatalsToBrowser'; + +require '/var/ipfire/general-functions.pl'; +require "${General::swroot}/lang.pl"; +require "${General::swroot}/header.pl"; +#workaround to suppress a warning when a variable is used only once +my @dummy = ( ${Header::colouryellow} ); +undef (@dummy); + +our %hasettings=(); +our %netsettings=(); +my %mainsettings=(); +my %timesettings=(); +my $setting = "${General::swroot}/keepalived/settings"; +my $runsetting = "${General::swroot}/keepalived/runsettings"; +my $loxilbipfile = "${General::swroot}/loxilb/ipconfig"; + # because we need commas in the some data +my $errormessage = ''; +my @nosaved=(); +my %color = (); + +$hasettings{'ENABLE_HA'} = 'off'; + +# Load multiline data +our @current = (); +if (open(FILE, "$loxilbipfile")) { + @current = ; + close (FILE); +} + +&Header::showhttpheaders(); +our @ITFs=('RED', 'GREEN'); +my @STATE= ('MASTER', 'BACKUP'); + +#Settings1 for the first screen box +foreach my $itf (@ITFs) { + $hasettings{"ENABLE_${itf}"} = 'off'; + $hasettings{"state_${itf}"} = ''; + $hasettings{"garp_master_delay_${itf}"} = ''; + $hasettings{"virtual_router_id_${itf}"} = ''; + $hasettings{"priority_${itf}"} = ''; + $hasettings{"advert_int_${itf}"} = ''; + $hasettings{"auth_pass_${itf}"} = ''; + $hasettings{"unicast_peer_${itf}"} = ''; + $hasettings{"virtual_ipaddress_${itf}"} = ''; +} + +# Read Ipcop settings +&General::readhash("${General::swroot}/ethernet/settings", \%netsettings); +&General::readhash("${General::swroot}/main/settings", \%mainsettings); +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color); + +#Get GUI values +&Header::getcgihash(\%hasettings); + +if ($hasettings{'ACTION'} eq $Lang::tr{'enable'}) +{ + &General::writehash("$runsetting", \%hasettings); + if ($hasettings{'ENABLE_HA'} eq 'on') { + &General::system('/usr/bin/touch', "${General::swroot}/keepalived/enable_ha"); + &General::system('/usr/local/bin/keepalivedctrl', 'start'); + } else { + &General::system('/usr/local/bin/keepalivedctrl', 'stop'); + unlink "${General::swroot}/keepalived/enable_ha"; + } +} + +# Check Settings1 first because they are needed by &buildconf +if ($hasettings{'ACTION'} eq $Lang::tr{'save'}) { + foreach my $itf (@ITFs) { + if ($hasettings{"ENABLE_${itf}"} eq 'on' ) { + + if (!(&General::validnum($hasettings{"virtual_router_id_${itf}"})) || ($hasettings{"virtual_router_id_${itf}"} eq '')) { + $errormessage = "virtual_router_id" . " is $Lang::tr{'required field'}" . " or not valid num"; + goto ERROR; + } + if (!(&General::validnum($hasettings{"priority_${itf}"})) || ($hasettings{"priority_${itf}"} eq '')) { + $errormessage = "priority" . " is $Lang::tr{'required field'}" . " or not valid num"; + goto ERROR; + } + if (!(&General::validnum($hasettings{"advert_int_${itf}"})) || ($hasettings{"advert_int_${itf}"} eq '')) { + $errormessage = "advert_int" . " is $Lang::tr{'required field'}" . " or not valid num"; + goto ERROR; + } + if (!(&General::validnum($hasettings{"garp_master_delay_${itf}"})) || ($hasettings{"garp_master_delay_${itf}"} eq '')) { + $errormessage = "garp master delay" . " is $Lang::tr{'required field'}" . " or not valid num"; + goto ERROR; + } + } + + } + + map (delete ($hasettings{$_}) ,@nosaved,'ACTION','KEY1','KEY2','q'); # Must not be saved + &General::writehash($setting, \%hasettings); # Save good settings + $hasettings{'ACTION'} = $Lang::tr{'save'}; # create an 'ACTION' + map ($hasettings{$_} = '',@nosaved,'KEY1','KEY2'); # and reinit vars to empty + &buildconf; + ERROR: +} + +if ($hasettings{'ACTION'} eq '' ) { # First launch from GUI + + # Set default DHCP values only if blank and disabled + foreach my $itf (@ITFs) { + if ($hasettings{"ENABLE_${itf}"} ne 'on' ) { + $hasettings{"virtual_router_id_${itf}"} = '50'; + $hasettings{"priority_${itf}"} = '100'; + $hasettings{"advert_int_${itf}"} = '1'; + $hasettings{"auth_pass_${itf}"} = ''; + $hasettings{"unicast_peer_${itf}"} = ''; + $hasettings{"garp_master_delay_${itf}"} = '10'; + } + } +} + +### START PAGE ### +&Header::openpage($Lang::tr{'keepalived configuration'}, 1, $Header::extraHead); +&Header::openbigbox('100%', 'left', '', $errormessage); + +if ($errormessage) { + &Header::openbox('100%', 'left', $Lang::tr{'error messages'}); + print "$errormessage \n"; + &Header::closebox(); +} + +# Read configuration file. +&General::readhash("$runsetting", \%hasettings); + +# Checkbox pre-selection. +my $checked; +if ($hasettings{'ENABLE_HA'} eq "on") { + $checked = "checked='checked'"; +} + +my $sactive = "
$Lang::tr{'stopped'}
"; + +my @status = &General::system_output('/usr/local/bin/keepalivedctrl', 'status'); + +if (grep(/is running/, @status)){ + $sactive = "
$Lang::tr{'running'}
"; +} + +&Header::openbox('100%', 'center', $Lang::tr{'keepalived status'}); + +print < +
+   +   +   + $Lang::tr{'keepalived status'} + $sactive + + + $Lang::tr{'enable'} + + + +END + +print "
\n"; + +&Header::closebox(); +# + + +&General::readhash($setting, \%hasettings); # Get saved settings and reset to good if needed + +&Header::openbox('100%', 'left', $Lang::tr{'keepalived config'}); +print "
"; + +foreach my $itf (@ITFs) { + my %checked = (); + my @vips; + my $lc_itf = lc($itf); + my $current_state = $hasettings{"state_${itf}"}; + my @current_vips = split(/\|/, $hasettings{"virtual_ipaddress_${itf}"}); #multi selected value is separated by pipe | + + foreach my $line (@current) { + chomp($line); + my @temp = split(/\,/, $line); + if ($temp[1] eq $netsettings{"${itf}_DEV"}) { + push(@vips, $temp[0]); + } + } + $checked{'ENABLE'}{'on'} = ($hasettings{"ENABLE_${itf}"} ne 'on') ? '' : "checked='checked'"; + + print < + + $Lang::tr{"$lc_itf interface"} + $Lang::tr{'enabled'} + + + + $Lang::tr{'keepalived state'}:  + + + + + $Lang::tr{'keepalived priority'} * + + + + $Lang::tr{'keepalived advert int'} * + + + + $Lang::tr{'keepalived garp master delay'}  + + + + $Lang::tr{'keepalived auth pass'} * + + + + $Lang::tr{'keepalived unicast peer'} * + + + + $Lang::tr{'keepalived virtual address'}: * + + + + + +END + +&Header::closebox(); + +&Header::closebigbox(); +&Header::closepage(); + +# Build the configuration file mixing settings, fixed leases and advanced options +sub buildconf { + open(FILE, ">/${General::swroot}/keepalived/keepalived.conf") or die "Unable to write keepalived.conf file"; + flock(FILE, 2); + + # Global settings + print FILE "global_defs {\n"; + print FILE "\trouter_id BPFire_DEVEL\n"; + print FILE "}\n"; + + print FILE "\n"; + + #Subnet range definition + foreach my $itf (@ITFs) { + my $lc_itf=lc($itf); + if ($hasettings{"ENABLE_${itf}"} eq 'on' ){ + print FILE "vrrp_instance VI_$lc_itf {" . "\n"; + print FILE "\tstate " . $hasettings{"state_${itf}"} . "\n"; + print FILE "\tinterface " . $netsettings{"${itf}_DEV"} . "\n"; + print FILE "\tvirtual_router_id " . $hasettings{"virtual_router_id_${itf}"} . "\n"; + print FILE "\tpriority " . $hasettings{"priority_${itf}"} . "\n"; + print FILE "\tadvert_int " . $hasettings{"advert_int_${itf}"} . "\n"; + print FILE "\tgarp_master_delay " . $hasettings{"garp_master_delay_${itf}"} . "\n"; + #unicast peer, red0 does not support multicast + print FILE "\tunicast_peer {" . "\n"; + print FILE "\t\t" . $hasettings{"unicast_peer_${itf}"} . "\n"; + print FILE "\t}" . "\n"; + # authentication + print FILE "\tauthentication {" . "\n"; + print FILE "\t\tauth_type PASS" . "\n"; + print FILE "\t\tauth_pass " . $hasettings{"auth_pass_${itf}"} . "\n"; + print FILE "\t}" . "\n"; + # virtual ipaddress + print FILE "\tvirtual_ipaddress {" . "\n"; + my @vips = split(/\|/, $hasettings{"virtual_ipaddress_${itf}"}); + foreach my $ip (@vips) { + print FILE "\t\t$ip" . "\n"; + } + print FILE "\t}" . "\n"; + + print FILE "} #$itf\n\n"; + + &General::system('/usr/bin/touch', "${General::swroot}/keepalived/enable_${lc_itf}"); + } else { + unlink "${General::swroot}/keepalived/enable_${lc_itf}"; + } + } + + close FILE; + + &General::system_background('/usr/local/bin/keepalivedctrl', 'restart'); +} diff --git a/langs/en/cgi-bin/en.pl b/langs/en/cgi-bin/en.pl index 05214bef1..62882c0b9 100644 --- a/langs/en/cgi-bin/en.pl +++ b/langs/en/cgi-bin/en.pl @@ -1386,6 +1386,7 @@ 'graph per' => 'per', 'green' => 'GREEN', 'green interface' => 'Green Interface', +'red interface' => 'Red Interface', 'grouptype' => 'Grouptype:', 'guaranteed bandwidth' => 'Guaranteed bandwidth', 'guardian' => 'Guardian', @@ -2492,6 +2493,17 @@ 'loxilb ip virtualIP' => 'Virtual IP', 'loxilb ip interface' => 'Interface', 'loxilb ip add' => 'Add Virtual IP', +'keepalived' => 'High Availability', +'keepalived config' => 'Keepalived Configuration', +'keepalived status' => 'Keepalived Status', +'keepalived state' => 'State', +'keepalived virtual router id' => 'Virtual Router ID', +'keepalived priority' => 'Priority', +'keepalived advert int' => 'Advert Interval', +'keepalived garp master delay' => 'Garp Master Delay', +'keepalived auth pass' => 'Auth Pass', +'keepalived unicast peer' => 'Unicast Peer', +'keepalived virtual address' => 'Virtual Address', 'status' => 'Status', 'status information' => 'Status information', 'status ovpn' => 'OpenVPN', diff --git a/langs/zh/cgi-bin/zh.pl b/langs/zh/cgi-bin/zh.pl index 86875f9e7..6b81cf5f3 100644 --- a/langs/zh/cgi-bin/zh.pl +++ b/langs/zh/cgi-bin/zh.pl @@ -1379,6 +1379,7 @@ 'graph per' => 'per', 'green' => '绿色', 'green interface' => '绿色接口', +'red interface' => '红色接口', 'grouptype' => '组类型:', 'guaranteed bandwith' => '保证带宽', 'guardian' => '监护人', @@ -2459,6 +2460,17 @@ 'loxilb ip virtualIP' => '虚拟 IP', 'loxilb ip interface' => '网络接口', 'loxilb ip add' => '添加虚拟 IP', +'keepalived' => '高可用性', +'keepalived config' => 'Keepalived 配置', +'keepalived status' => 'Keepalived 运行状态', +'keepalived state' => '主备状态', +'keepalived virtual router id' => '虚拟路由 ID', +'keepalived priority' => '优先级', +'keepalived advert int' => '广告间隔', +'keepalived garp master delay' => 'Garp 主设备延迟', +'keepalived auth pass' => '认证密码', +'keepalived unicast peer' => '单播同伴设备', +'keepalived virtual address' => '虚拟 IP地址', 'status' => '状态', 'status information' => '状态信息', 'status ovpn' => 'OpenVPN', diff --git a/lfs/configroot b/lfs/configroot index a7197580f..afda8910d 100644 --- a/lfs/configroot +++ b/lfs/configroot @@ -68,7 +68,7 @@ $(TARGET) : fwhosts/customnetworks fwhosts/customhosts fwhosts/customgroups fwhosts/customservicegrp fwhosts/customlocationgrp fwlogs/ipsettings fwlogs/portsettings ipblocklist/modified \ ipblocklist/settings mac/settings main/hosts main/routing main/security main/settings optionsfw/settings \ ovpn/ccd.conf ovpn/ccdroute ovpn/ccdroute2 pakfire/settings portfw/config ppp/settings-1 ppp/settings-2 ppp/settings-3 ppp/settings-4 \ - ppp/settings-5 ppp/settings proxy/settings proxy/squid.conf proxy/advanced/settings proxy/advanced/cre/enable remote/settings ddos/settings ddos/tcp_ports ddos/udp-ddos-settings ddos/udp_ports ddos/dns-ddos-settings loxilb/settings keepalived/keepalived.conf qos/settings qos/classes qos/subclasses qos/level7config qos/portconfig \ + ppp/settings-5 ppp/settings proxy/settings proxy/squid.conf proxy/advanced/settings proxy/advanced/cre/enable remote/settings ddos/settings ddos/tcp_ports ddos/udp-ddos-settings ddos/udp_ports ddos/dns-ddos-settings loxilb/settings keepalived/keepalived.conf keepalived/runsettings keepalived/settings qos/settings qos/classes qos/subclasses qos/level7config qos/portconfig \ qos/tosconfig suricata/settings vpn/config vpn/settings vpn/ipsec.conf \ vpn/ipsec.secrets vpn/caconfig wakeonlan/clients.conf wireless/config wireless/settings; do \ touch $(CONFIG_ROOT)/$$i; \