initial commit of file from CVS for smeserver-openvpn-bridge on Sat Sep 7 19:57:25 AEST 2024

This commit is contained in:
Trevor Batley
2024-09-07 19:57:25 +10:00
parent d755aea606
commit 9dcb47db31
73 changed files with 14036 additions and 2 deletions

View File

@@ -0,0 +1,27 @@
[Unit]
Description=OpenVPN Server to Server
After=network.service
After=bridge.service
Requires=bridge.service
[Service]
Type=notify
PrivateTmp=true
WorkingDirectory=/etc/openvpn/bridge
ExecStart=/usr/sbin/openvpn --status /var/log/openvpn-bridge/status.log --status-version 2 --ncp-ciphers AES-256-GCM:AES-128-GCM:AES-256-CBC:AES-128-CBC:BF-CBC --config /etc/openvpn/bridge/openvpn.conf --cd /etc/openvpn/bridge
PrivateTmp=true
CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE
LimitNPROC=10
DeviceAllow=/dev/null rw
DeviceAllow=/dev/net/tun rw
ProtectSystem=true
ProtectHome=true
KillMode=process
RestartSec=5s
Restart=on-failure
[Install]
WantedBy=sme-server.target

View File

@@ -0,0 +1,990 @@
#!/usr/bin/perl -w
package esmith::FormMagick::Panel::openvpnbridge;
use strict;
use warnings;
use esmith::ConfigDB;
use esmith::FormMagick;
use esmith::cgi;
use esmith::util;
use Net::OpenVPN::Manage;
use Net::IP;
our @ISA = qw(esmith::FormMagick Exporter);
our @EXPORT = qw(
get_prop
get_status
print_link_status
get_auth_type
get_ip_pool
print_crt_not_ready
print_client_config
print_downloads_links
download_file
print_custom_button
print_section_bar
write_pem
read_pem
disconnect_client
get_cipher_status
get_hmac_status
get_current_hmac
get_current_cipher
get_digests_options
get_ciphers_options
);
our $config_db = esmith::ConfigDB->open || die "Couldn't open ConfigDB\n";
our $rules_db = esmith::ConfigDB->open('openvpn-bridge') || esmith::ConfigDB->create('openvpn-bridge');
our $base_url = "?page=0&page_stack=&Next=Next&wherenext=";
our $pubdir = '/etc/openvpn/bridge/pub';
our $privdir = '/etc/openvpn/bridge/priv';
*wherenext = \&CGI::FormMagick::wherenext;
sub new {
shift;
my $fm = esmith::FormMagick->new();
$fm->{calling_package} = (caller)[0];
bless $fm;
return $fm;
}
# Retourne le paramètre demandé
sub get_prop{
my ($fm, $prop, $default) = @_;
return $config_db->get_prop("openvpn-bridge", $prop) || $default;
}
# Retourne l'état du service
sub get_status{
my ($fm) = @_;
my $status = get_prop('','status','disabled');
if ($status eq 'enabled'){
return $fm->localise('ENABLED');
}
else{
return $fm->localise('DISABLED');
}
}
# Retourne le mode d'authentification
sub get_auth_type{
my ($fm) = @_;
my $auth_type = get_prop('','userAuth');
if ($auth_type eq 'CrtOnly'){
return $fm->localise('CRT_ONLY');
}
elsif ($auth_type eq 'CrtWithPass'){
return $fm->localise('CRT_WITH_PASS');
}
else{
return $fm->localise('BAD_VALUE');
}
}
# Retourne la plage d'adresses
sub get_ip_pool{
my ($fm) = @_;
my $start = get_prop('','startPool') || "x.x.x.x";
my $end = get_prop('','endPool') || "x.x.x.x";
return "$start - $end";
}
# Inscrit les valeurs de la configuration dans la db
sub apply_settings{
my $fm = shift;
my $q = $fm->{'cgi'};
$config_db->set_prop('openvpn-bridge', 'status', $q->param("status"));
$config_db->set_prop('openvpn-bridge', 'userAuth', $q->param("auth_type"));
$config_db->set_prop('openvpn-bridge', 'startPool', $q->param("start_pool"));
$config_db->set_prop('openvpn-bridge', 'endPool', $q->param("end_pool"));
if ($q->param("hmac") eq 'SHA1') {
my $tmpk = $config_db->get('openvpn-bridge');
$tmpk->delete_prop('HMAC');
}
else {
$config_db->set_prop('openvpn-bridge', 'HMAC', $q->param("hmac"));
}
if ($q->param("cipher") eq 'BF-CBC') {
my $tmpk = $config_db->get('openvpn-bridge');
$tmpk->delete_prop('Cipher');
}
else {
$config_db->set_prop('openvpn-bridge', 'Cipher', $q->param("cipher"));
}
unless ( system ("/sbin/e-smith/signal-event", "openvpn-bridge-update") == 0 ){
return $fm->error('ERROR_OCCURED', 'FIRST');;
}
return $fm->success('SUCCESS','FIRST');
}
#status global du lien
sub print_link_status{
my $fm = shift;
my $q = $fm->{cgi};
my $common_name = $fm->localise('COMMON_NAME');
my $real_ip = $fm->localise('REAL_IP');
my $virtual_ip = $fm->localise('VIRTUAL_IP');
my $sent = $fm->localise('SENT');
my $received = $fm->localise('RECEIVED');
my $connected_since = $fm->localise('CONNECTED_SINCE');
my $disconnect = $fm->localise('DISCONNECT');
# test status db
return get_status($fm) if get_status($fm) eq $fm->localise('DISABLED');
# test systemd
my $act = `/usr/bin/systemctl is-active openvpn-bridge.service`;
chomp $act;
return "<span style='color:red'>" . $fm->localise('SYSTEMD_RETURNED') . " $act </span>" unless $act eq "active";
# On récupère les paramètre et on les parse
my $param = get_prop('',"management");
my @param = split(/:/,$param);
my $host = $param[0];
my $port = $param[1];
my $pass = $param[2];
# On cré l'objet vpn
my $vpn = Net::OpenVPN::Manage->new({
host => $host,
port => $port,
password => $pass,
timeout => 3
});
# On se connecte ou on retourne le message d'erreur
unless($vpn->connect()){
print "<span style='color:red'>" . $fm->localise('ERROR_CONNECT_TO_MANAGER'). "</span>";
return "";
}
my $r = $vpn->status_ref();
return "<span style='color:green'>" . $fm->localise('UP') ."</span>" if $r->{TITLE};
return "<span style='color:red'>" . $fm->localise('ERROR') ."</span>";
}
# Affiche les connexions en cours
sub print_clients_table{
my $fm = shift;
my $q = $fm->{cgi};
my $common_name = $fm->localise('COMMON_NAME');
my $real_ip = $fm->localise('REAL_IP');
my $virtual_ip = $fm->localise('VIRTUAL_IP');
my $sent = $fm->localise('SENT');
my $received = $fm->localise('RECEIVED');
my $connected_since = $fm->localise('CONNECTED_SINCE');
my $disconnect = $fm->localise('DISCONNECT');
# On récupère les paramètre et on les parse
my $param = get_prop('',"management");
my @param = split(/:/,$param);
my $host = $param[0];
my $port = $param[1];
my $pass = $param[2];
# On cré l'objet vpn
my $vpn = Net::OpenVPN::Manage->new({
host => $host,
port => $port,
password => $pass,
timeout => 3
});
# On se connecte ou on retourne le message d'erreur
unless($vpn->connect()){
print $q->Tr($q->td($fm->localise('ERROR_CONNECT_TO_MANAGER')));
return "";
}
my $r = $vpn->status_ref();
my %virtIP;
my %realIP;
my %remotePort;
my %sentBytes;
my %receivedBytes;
my %connectedSince;
my @commonNames;
my $count = 0;
foreach( @{$r->{CLIENT_LIST}} ){
my $CN = $$_[0];
unshift (@commonNames,$CN);
$virtIP{$_} = $$_[1];
my @ipPort = split (/:/,$$_[1]);
$realIP{$CN} = $ipPort[0];
$remotePort{$CN} = $ipPort[1];
$virtIP{$CN} = $$_[2];
$receivedBytes{$CN} = $$_[3]/1048576;
$receivedBytes{$CN} = sprintf("%.2f", $receivedBytes{$CN});
$sentBytes{$CN} = $$_[4]/1048576;
$sentBytes{$CN} = sprintf("%.2f", $sentBytes{$CN});
$connectedSince{$CN} = $$_[5];
}
# Si @commonName est vide, il n'y a aucun client connecté
unless ( scalar @commonNames ){
print $q->Tr($q->td($fm->localise('NO_CLIENTS_CONNECTED')));
return "";
}
print $q->start_table({-CLASS => "sme-border"}),"\n";
print $q->Tr (
esmith::cgi::genSmallCell($q, $common_name,"header"),
esmith::cgi::genSmallCell($q, $real_ip,"header"),
esmith::cgi::genSmallCell($q, $virtual_ip,"header"),
esmith::cgi::genSmallCell($q, $sent,"header"),
esmith::cgi::genSmallCell($q, $received,"header"),
esmith::cgi::genSmallCell($q, $connected_since,"header"),
esmith::cgi::genSmallCell($q, $disconnect,"header", 3)),"\n";
foreach my $cn (@commonNames){
print $q->Tr (esmith::cgi::genSmallCell($q,"$cn"),
esmith::cgi::genSmallCell($q,"$realIP{$cn} ($remotePort{$cn})"),
esmith::cgi::genSmallCell ($q, "$virtIP{$cn}"),
esmith::cgi::genSmallCell ($q, "$sentBytes{$cn}".' MB'),
esmith::cgi::genSmallCell ($q, "$receivedBytes{$cn}".' MB'),
esmith::cgi::genSmallCell ($q, "$connectedSince{$cn}"),
esmith::cgi::genSmallCell ($q, $q->a ({href => $q->url (-absolute => 1).
$base_url."CLIENT_DISCONNECT_PAGE&common_name=".
$cn}, $disconnect)));
}
print $q->end_table,"\n";
return "";
}
# Retourne la liste des règles
sub print_rules{
my $fm = shift;
my $q = $fm->{cgi};
my @rules = $rules_db->get_all_by_prop(type => 'rule');
unless (@rules){
print $q->Tr($q->td($fm->localise('NO_RULE')));
return "";
}
print $q->start_table({-CLASS => "sme-border"}),"\n";
print $q->Tr (
esmith::cgi::genSmallCell(
$q, $fm->localise('COMMON_NAME'),"header"),
esmith::cgi::genSmallCell(
$q, $fm->localise('IP_ADDRESS'),"header"),
esmith::cgi::genSmallCell(
$q, $fm->localise('COMMENT'),"header"),
esmith::cgi::genSmallCell(
$q, $fm->localise('GATEWAY_REDIRECTION'),"header"),
esmith::cgi::genSmallCell(
$q, $fm->localise('ACCESS'),"header"),
esmith::cgi::genSmallCell(
$q, $fm->localise('MODIFY'),"header"),
esmith::cgi::genSmallCell(
$q, $fm->localise('REMOVE'),"header")
),"\n";
foreach (@rules){
my $rule = $_->key;
my $rec_rule = $rules_db->get("$rule");
my $ip = $rec_rule->prop("ip") || $fm->localise('DYNAMIC');
my $gw = $rec_rule->prop("redirectGW") || 'disabled';
my $access = $rec_rule->prop("access") || 'allowed';
$gw = ( $gw eq 'enabled')
? $fm->localise('ENABLED')
: $fm->localise('DISABLED');
$access = ( $access eq 'allowed')
? $fm->localise('ALLOWED')
: $fm->localise('DENIED');
print $q->Tr (esmith::cgi::genSmallCell($q,"$rule"),
esmith::cgi::genSmallCell($q,"$ip"),
esmith::cgi::genSmallCell($q,$rec_rule->prop("comment")),
esmith::cgi::genSmallCell($q,$gw),
esmith::cgi::genSmallCell($q,$access),
esmith::cgi::genSmallCell ($q,
$q->a ({href => $q->url (-absolute => 1).
$base_url."CREATE_OR_MODIFY_RULE_PAGE&action=modify&common_name=".$rule}, $fm->localise('MODIFY'))),
esmith::cgi::genSmallCell ($q,
$q->a ({href => $q->url (-absolute => 1).
$base_url."REMOVE_RULE_PAGE&common_name=".$rule}, $fm->localise('REMOVE'))));
}
print $q->end_table,"\n";
return "";
}
# Ajouter ou modifier une règle
sub create_or_modify_rule{
my ($fm) = @_;
my $q = $fm->{cgi};
my $rule = $q->param('common_name');
my $comment = $q->param('comment');
my $ip = $q->param('reserved_ip');
my $gw_redirection = $q->param('gw_redirection');
my $access = $q->param('access');
my $action = $q->param('action');
if ($action eq 'create'){
if ($rules_db->get($rule)){
$fm->error('CN_CONFLICT','RULES_PAGE');
return undef;
}
my $msg = $fm->validate_common_name($rule);
unless ($msg eq "OK"){
return $fm->error($msg,'RULES_PAGE');
}
else{
$rules_db->new_record(
$rule,{
comment => $comment,
ip => $ip,
redirectGW => $gw_redirection,
access => $access,
type => 'rule',
}
);
}
$fm->success('SUCCESS','RULES_PAGE');
}
elsif ($action eq 'modify'){
my $rec_rule = $rules_db->get($rule);
$rec_rule->set_prop('comment',$comment);
$rec_rule->set_prop('ip',$ip);
$rec_rule->set_prop('redirectGW',$gw_redirection);
$rec_rule->set_prop('access',$access);
}
unless ( system ("/sbin/e-smith/signal-event", "openvpn-bridge-reload-ccd") == 0 ){
$fm->error('ERROR_OCCURED','RULES_PAGE');
return undef;
}
$fm->success('SUCCESS','RULES_PAGE');
}
# Afficher le champ Nom Commun
sub print_common_name_field {
my $fm = shift;
my $q = $fm->{cgi};
my $rule = $fm->{cgi}->param('common_name') || '';
my $action = $fm->{cgi}->param('action') || '';
print qq(<tr><td colspan="2">) . $fm->localise('DESC_COMMON_NAME').qq(</td></tr>);
print qq(<tr><td class="sme-noborders-label">) .
$fm->localise('COMMON_NAME') . qq(</td>\n);
if ($action eq 'modify' and $rule) {
print qq(
<td class="sme-noborders-content">$rule
<input type="hidden" name="name" value="$rule">
<input type="hidden" name="action" value="modify">
</td>
);
my $rec_rule = $rules_db->get($rule);
if ($rec_rule){
$q->param(-name=>'comment',-value=>
$rec_rule->prop('comment'));
$q->param(-name=>'reserved_ip',-value=>
$rec_rule->prop('ip'));
$q->param(-name=>'gw_redirection',-value=>
$rec_rule->prop('redirectGW'));
$q->param(-name=>'access',-value=>
$rec_rule->prop('access'));
}
}
else {
print qq(
<td><input type="text" name="common_name" value="$rule">
<input type="hidden" name="action" value="create">
</td>
);
}
print qq(</tr>\n);
return undef;
}
sub print_rule_to_remove{
my ($fm) = @_;
my $q = $fm->{cgi};
my $rule = $q->param('common_name');
my $rec_rule = $rules_db->get($rule);
my $comment = $rec_rule->prop('comment');
print $q->Tr(
$q->td(
{ -class => 'sme-noborders-label' },
$fm->localise('COMMON_NAME')
),
$q->td( { -class => 'sme-noborders-content' }, $rule )
),
"\n";
print $q->Tr(
$q->td(
{ -class => 'sme-noborders-label' },
$fm->localise('COMMENT')
),
$q->td( { -class => 'sme-noborders-content' }, $comment )
),
"\n";
print $q->table(
{ -width => '100%' },
$q->Tr(
$q->th(
{ -class => 'sme-layout' },
$q->submit(
-name => 'cancel',
-value => $fm->localise('CANCEL')
),
' ',
$q->submit(
-name => 'remove',
-value => $fm->localise('REMOVE')
)
)
)
),
"\n";
# Clear these values to prevent collisions when the page reloads.
$q->delete("cancel");
$q->delete("remove");
return undef;
}
sub print_client_to_disconnect{
my ($fm) = @_;
my $q = $fm->{cgi};
my $cn = $q->param('common_name');
print $q->Tr(
$q->td(
{ -class => 'sme-noborders-label' },
$fm->localise('COMMON_NAME')
),
$q->td( { -class => 'sme-noborders-content' }, $cn )
),
"\n";
print $q->table(
{ -width => '100%' },
$q->Tr(
$q->th(
{ -class => 'sme-layout' },
$q->submit(
-name => 'cancel',
-value => $fm->localise('CANCEL')
),
' ',
$q->submit(
-name => 'disconnect',
-value => $fm->localise('DISCONNECT')
)
)
)
),
"\n";
# Clear these values to prevent collisions when the page reloads.
$q->delete("cancel");
$q->delete("disconnect");
return undef;
}
sub disconnect_client{
my ($fm) = @_;
my $q = $fm->{cgi};
my $cn = $q->param('common_name');
# On récupère les paramètre et on les parse
my $param = get_prop('',"management");
my @param = split(/:/,$param);
my $host = $param[0];
my $port = $param[1];
my $pass = $param[2];
my $vpn = Net::OpenVPN::Manage->new({
host => $host,
port => $port,
password => $pass,
timeout => 3
});
unless($q->param('cancel')){
unless($vpn->connect()){
$fm->error('ERROR_CONNECT_TO_MANAGER','SHOW_CLIENTS_PAGE');
return undef;
}
unless($vpn->kill($cn)){
$fm->error('ERROR_CONNECT_TO_MANAGER','SHOW_CLIENTS_PAGE');
return undef;
}
$fm->success('CLIENT_DISCONNECTED','SHOW_CLIENTS_PAGE');
return undef;
}
$fm->error('CANCELED','SHOW_CLIENTS_PAGE');
return undef;
}
sub print_crt_not_ready_warning{
my ($fm) = @_;
# First, check the service "bridge" is running
my $bridge = $config_db->get_prop('bridge', 'status') || 'disabled';
unless ($bridge eq 'enabled'){
return $fm->localise('BRIDGE_NOT_ENABLED');
}
# If any of the required files is missing or empty
# Warn the user
if (
(( -z "$pubdir/cacert.pem" ) || ( ! -e "$pubdir/cacert.pem" )) ||
(( -z "$pubdir/cert.pem") || ( ! -e "$pubdir/cert.pem" )) ||
(( -z "$privdir/key.pem") || ( ! -e "$privdir/key.pem" )) ||
(( -z "$pubdir/cacrl.pem") || ( ! -e "$pubdir/cacrl.pem" )) ||
(( -z "$pubdir/dh.pem") || ( ! -e "$pubdir/dh.pem" ))
){
return $fm->localise('CRT_CONFIG_ERROR');
}
return $fm->localise('CRT_CONFIG_OK');
}
sub print_client_config{
my ($fm) = @_;
my $q = $fm->{cgi};
my $proto = get_prop('','proto','udp');
$proto = 'tcp-client' if ($proto eq 'tcp');
my $port = ($proto eq 'udp' ? (get_prop('','UDPPort','1194')):(get_prop('','TCPPort','1194')));
my $mtutest = get_prop('','mtuTest','enabled');
my $fragment = get_prop('','fragment','');
my $tunmtu = get_prop('','tunMtu','');
my $cipher = get_prop('','Cipher','');
my $hmac = get_prop('','HMAC','');
if ($proto eq 'tcp'){
$mtutest = 'disabled';
$fragment = '';
}
my $fic = '';
$fic .= "rport $port\n";
$fic .= "proto $proto\n";
$fic .= "dev tap\n";
$fic .= "nobind\n";
$fic .= "# Uncomment the following line if your system\n# support passtos (not supported on Windows)\n";
$fic .= "# passtos\n";
$fic .= "remote ".$config_db->get('SystemName')->value.".".$config_db->get('DomainName')->value."\n\n";
$fic .= "tls-client\n";
$fic .= "tls-auth takey.pem 1\n"
if (( -e "$privdir/takey.pem")&&( !-z "$privdir/takey.pem"));
$fic .= "ns-cert-type server\n\n";
$fic .= "cipher $cipher\n" if (($cipher ne '') && ($cipher ne 'auto'));
$fic .= "auth $hmac\n" if (($hmac ne '') && ($hmac ne 'auto'));
$fic .= "\n";
$fic .= (get_prop('','userAuth','CrtWithPass') eq 'CrtWithPass' ? "auth-user-pass\n\n" : "\n");
$fic .= "\n";
if ($mtutest eq 'enabled'){
$fic .= "mtu-test\n";
}
elsif (($mtutest eq 'disabled')){
if ($tunmtu ne ''){
$fic .= "tun-mtu $tunmtu\n";
}
if (($proto eq 'udp') && ($fragment ne '')){
$fic .= "fragment $fragment\nmssfix\n";
}
}
$fic .= "comp-lzo\n";
$fic .= "pull\n";
$fic .= "\n";
$fic .= "# Uncomment and replace user.p12 \n# with the certificate bundle in PKCS12 format\n";
$fic .= "#pkcs12 user.p12\n\n";
$fic .= "# You can replace the pkcs12\n# directive with the old ones\n";
$fic .= "#ca cacert.pem\n#cert user.pem\n#key user-key.pem\n\n";
$fic .= "# Alternatively you can paste your cert and private key here:\n";
#infile file support
$fic .= "# client certificate - uncomment and paste between delimiters \n";
$fic .= "#<cert>\n";
$fic .= "#</cert>\n";
$fic .= "# client private key - uncomment and paste between delimiters\n";
$fic .= "#<key>\n";
$fic .= "#</key>\n";
$fic .= "\n";
$fic .= "# CA certificate\n";
$fic .= "<ca>\n";
$fic .= read_pem($fm,'cacert.pem')."\n";
$fic .= "</ca>\n";
if (( -e "$privdir/takey.pem")&&( !-z "$privdir/takey.pem")) {
$fic .= "\n# Shared TLS key\n";
$fic .= "<tls-auth>\n";
$fic .= read_pem($fm,'takey.pem')."\n";
$fic .= "</tls-auth>\n";
}
print(esmith::cgi::genTextRow($q,
$q->textarea (
-name => "config_file",
-override => 1,
-default => $fic,
-rows => 30,
-columns => 100)
)
);
return "";
}
sub remove_rule{
my ($fm) = @_;
my $q = $fm->{cgi};
my $rule = $q->param('common_name');
unless($q->param("cancel")){
unless ($rules_db->get($rule)->delete()){
$fm->error('ERROR_OCCURED','RULES_PAGE');
return undef;
}
unless (system ("/sbin/e-smith/signal-event", "openvpn-bridge-reload-ccd") == 0 ){
$fm->error('ERROR_OCCURED','RULES_PAGE');
return undef;
}
$fm->success('SUCCESS','RULES_PAGE');
return undef;
}
$fm->error('CANCELED','RULES_PAGE');
return undef;
}
sub print_custom_button{
my ($fm,$desc,$url) = @_;
my $q = $fm->{cgi};
$url="openvpnbridge?page=0&page_stack=&Next=Next&wherenext=".$url;
print " <tr>\n <td colspan='2'>\n";
print $q->p($q->a({href => $url, -class => "button-like"},
$fm->localise($desc)));
print qq(</tr>\n);
return undef;
}
sub print_section_bar{
my ($fm) = @_;
print " <tr>\n <td colspan='2'>\n";
print "<hr class=\"sectionbar\"/>\n";
return undef;
}
sub read_pem{
my ($fm,$pem) = @_;
my $q = $fm->{cgi};
my $dir = '';
my $ret;
if (($pem eq 'cacert.pem') || ($pem eq 'cert.pem') || ($pem eq 'dh.pem')){
$dir = $pubdir;
}
elsif (($pem eq 'key.pem') || ($pem eq 'takey.pem')){
$dir = $privdir;
}
if (! open (PEM, "<$dir/$pem")){
$fm->error('ERROR_OPEN_PEM','FIRST');
# Tell the user something bad has happened
return;
}
while (<PEM>){
$ret .= $_;
}
close PEM;
return $ret;
}
sub write_pem{
my ($fm) = @_;
my $q = $fm->{cgi};
my $ca = $q->param('ca_pem');
my $crt = $q->param('crt_pem');
my $key = $q->param('key_pem');
my $dh = $q->param('dhpar_pem');
my $ta = $q->param('ta_pem');
$config_db->set_prop('openvpn-bridge', 'CrlUrl', $q->param('crl_url'));
if (! open (CA, ">$pubdir/cacert.pem")){
$fm->error('ERROR_OPEN_CA','FIRST');
# Tell the user something bad has happened
return;
}
print CA $ca;
close CA;
if (! open (CRT, ">$pubdir/cert.pem")){
$fm->error('ERROR_OPEN_CRT','FIRST');
# Tell the user something bad has happened
return;
}
print CRT $crt;
close CRT;
if (! open (KEY, ">$privdir/key.pem")){
$fm->error('ERROR_OPEN_KEY','FIRST');
# Tell the user something bad has happened
return;
}
print KEY $key;
close KEY;
chmod(0600, "$privdir/key.pem" );
esmith::util::chownFile("root", "root","$privdir/key.pem" );
if (! open (DH, ">$pubdir/dh.pem")){
$fm->error('ERROR_OPEN_DH','FIRST');
# Tell the user something bad has happened
return;
}
print DH $dh;
close DH;
if (! open (TA, ">$privdir/takey.pem")){
$fm->error('ERROR_OPEN_TA','FIRST');
# Tell the user something bad has happened
return;
}
print TA $ta;
close TA;
chmod(0600, "$privdir/takey.pem" );
esmith::util::chownFile("root", "root","$privdir/takey.pem" );
# Restrict permissions on sensitive data
esmith::util::chownFile("root", "root","$privdir");
esmith::util::chownFile("root", "root","$pubdir");
chmod 0700, "$privdir";
chmod 0755, "$pubdir";
unless(system("/sbin/e-smith/signal-event openvpn-bridge-update") == 0){
$fm->error('ERROR_OCCURED','RULES_PAGE');
return undef;
}
$fm->success('SUCCESS','FIRST');
return undef;
}
# Validations
sub is_ip{
my ($fm,$ip) = @_;
return CGI::FormMagick::Validator::ip_number($fm, $ip);
}
sub ip_is_in_local_net {
my ($fm,$ip) = @_;
unless(is_ip($fm, $ip) eq 'OK'){
return $fm->localise('NOT_A_VALID_IP',{ip => $ip});
}
my $local_ip = $config_db->get('LocalIP')->value();
my $local_netmask = $config_db->get('LocalNetmask')->value;
my ($local_network, $local_broadcast) =
esmith::util::computeNetworkAndBroadcast( $local_ip, $local_netmask );
my ($ip_network,$ip_broadcast) =
esmith::util::computeNetworkAndBroadcast($ip, $local_netmask);
if ($ip_network ne $local_network){
return $fm->localise('NOT_IN_LOCAL_NET',{ip => $ip});
}
return "OK";
}
sub ip_is_in_local_net_or_blank {
my ($fm,$ip) = @_;
if ($ip eq ''){
return 'OK';
}
return ip_is_in_local_net ($fm,$ip);
}
sub end_is_after_start{
my ($fm,$end) = @_;
my $start = $fm->{cgi}->param('start_pool');
my $start_ip = new Net::IP($start);
my $end_ip = new Net::IP($end);
unless ($end_ip->bincomp('gt',$start_ip)){
return $fm->localise('START_AFTER_END');
}
return 'OK';
}
sub not_in_dhcp_range
{
my $fm = shift;
my $address = shift;
my $status = $config_db->get('dhcpd')->prop('status') || "disabled";
return "OK" unless $status eq "enabled";
my $start = $config_db->get('dhcpd')->prop('start');
my $end = $config_db->get('dhcpd')->prop('end');
if (esmith::util::IPquadToAddr($start)
<= esmith::util::IPquadToAddr($address)
&&
esmith::util::IPquadToAddr($address)
<= esmith::util::IPquadToAddr($end)){
return $fm->localise("ADDR_IN_DHCP_RANGE",{ip => $address});
}
else{
return "OK";
}
}
sub validate_common_name
{
my ($fm, $common_name) = @_;
unless ($common_name =~ /^([a-zA-Z0-9][\_\.\-a-zA-Z0-9]*)$/){
return $fm->localise('INVALID_CHARS',{string => $common_name});
}
return "OK";
}
sub is_url
{
my ($fm, $url) = @_;
unless ($url =~ /^(http:\/\/)|(https:\/\/)/){
return $fm->localise('NOT_A_VALID_URL',{string => $url});
}
return "OK";
}
###### those could almost be copy paste for bridge and s2s
##
=head2 get_hmac_status
=cut
sub get_hmac_status{
my ($fm) = @_;
my $hmac = get_current_hmac();
$hmac= "<span style='color:red'>". $fm->localise('CHANGEME_INSECURE'). ": $hmac</span> " unless ($hmac eq "whirlpool" || $hmac =~ /(512|256|384|224)$/);
return $hmac;
}
=head2 get_cipher_status
list obtained using
openvpn --show-digests | egrep 'digest size' | awk {'print "'\''" $1 "'\'' => '\''" $1 "'\''," '}
=cut
sub get_cipher_status{
my ($fm) = @_;
my $cipher = get_current_cipher();
$cipher = "<span style='color:red'>". $fm->localise('CHANGEME_INSECURE'). ": $cipher</span> " unless ($cipher =~ /(128|192|256|512|SEED)/ );
return $cipher;
}
=head2 get_current_hmac
=cut
sub get_current_hmac{
my ($self) = @_;
my $cvpn= $config_db->get('openvpn-bridge') or return "SHA256" ;
return "SHA1" unless defined $cvpn->prop('HMAC');
return $cvpn->prop('HMAC') ;
}
=head2 get_current_cipher
list obtained using
openvpn --show-digests | egrep 'digest size' | awk {'print "'\''" $1 "'\'' => '\''" $1 "'\''," '}
=cut
sub get_current_cipher{
my ($self) = @_;
my $cvpn= $config_db->get('openvpn-bridge') or return "AES-128-CBC";
return "BF-CBC" unless defined $cvpn->prop('Cipher');
return $cvpn->prop('Cipher') ;
}
=head2 get_digests_options
=cut
sub get_digests_options{
my ($self) = @_;
my $translate = $self->localise('DEFAULT');
my $suggested = $self->localise('SUGGESTED');
my %options= (
'whirlpool' => 'whirlpool (512)',
'SHA512' => 'SHA512',
'SHA384' => 'SHA384',
'SHA256' => 'SHA256' . ": $suggested",
'SHA224' => 'SHA224',
'SHA1' => 'SHA1 (160)' . ": $translate",
'SHA' => 'SHA (160)',
'ecdsa-with-SHA1' => 'ecdsa-with-SHA1 (160)',
'RIPEMD160' => 'RIPEMD160',
'MD5' => 'MD5 (128)',
'MD4' => 'MD4 (128)',
);
return \%options;
}
=head2 get_ciphers_options
list obtained using
openvpn --show-ciphers | egrep '^[A-Z]{2}' | sed 's/ by//; s/ default//; s/block,/block/; s/)// ' | awk {'print " '\''" $1 "'\'' => '\''" $1 $2 " " $4 " " $5 " " $7")'\''," '}
then reduced to remove most of insecure ciphers
Using a CBC or GCM mode is recommended.
In static key mode only CBC mode is allowed.
=cut
sub get_ciphers_options{
my ($self) = @_;
my $translate = $self->localise('DEFAULT');
my $suggested = $self->localise('SUGGESTED');
my %options= (
'AES-128-CBC' => 'AES-128-CBC (128 key, 128 block)'.": $suggested",
'AES-128-CFB' => 'AES-128-CFB (128 key, 128 block)',
'AES-128-CFB1' => 'AES-128-CFB1 (128 key, 128 block)',
'AES-128-CFB8' => 'AES-128-CFB8 (128 key, 128 block)',
'AES-128-GCM' => 'AES-128-GCM (128 key, 128 block)',
'AES-128-OFB' => 'AES-128-OFB (128 key, 128 block)',
'AES-192-CBC' => 'AES-192-CBC (192 key, 128 block)',
'AES-192-CFB' => 'AES-192-CFB (192 key, 128 block)',
'AES-192-CFB1' => 'AES-192-CFB1 (192 key, 128 block)',
'AES-192-CFB8' => 'AES-192-CFB8 (192 key, 128 block)',
'AES-192-GCM' => 'AES-192-GCM (192 key, 128 block)',
'AES-192-OFB' => 'AES-192-OFB (192 key, 128 block)',
'AES-256-CBC' => 'AES-256-CBC (256 key, 128 block)',
'AES-256-CFB' => 'AES-256-CFB (256 key, 128 block)',
'AES-256-CFB1' => 'AES-256-CFB1 (256 key, 128 block)',
'AES-256-CFB8' => 'AES-256-CFB8 (256 key, 128 block)',
'AES-256-GCM' => 'AES-256-GCM (256 key, 128 block)',
'AES-256-OFB' => 'AES-256-OFB (256 key, 128 block)',
'CAMELLIA-128-CBC' => 'CAMELLIA-128-CBC (128 key, 128 block)',
'CAMELLIA-128-CFB' => 'CAMELLIA-128-CFB (128 key, 128 block)',
'CAMELLIA-128-CFB1' => 'CAMELLIA-128-CFB1 (128 key, 128 block)',
'CAMELLIA-128-CFB8' => 'CAMELLIA-128-CFB8 (128 key, 128 block)',
'CAMELLIA-128-OFB' => 'CAMELLIA-128-OFB (128 key, 128 block)',
'CAMELLIA-192-CBC' => 'CAMELLIA-192-CBC (192 key, 128 block)',
'CAMELLIA-192-CFB' => 'CAMELLIA-192-CFB (192 key, 128 block)',
'CAMELLIA-192-CFB1' => 'CAMELLIA-192-CFB1 (192 key, 128 block)',
'CAMELLIA-192-CFB8' => 'CAMELLIA-192-CFB8 (192 key, 128 block)',
'CAMELLIA-192-OFB' => 'CAMELLIA-192-OFB (192 key, 128 block)',
'CAMELLIA-256-CBC' => 'CAMELLIA-256-CBC (256 key, 128 block)',
'CAMELLIA-256-CFB' => 'CAMELLIA-256-CFB (256 key, 128 block)',
'CAMELLIA-256-CFB1' => 'CAMELLIA-256-CFB1 (256 key, 128 block)',
'CAMELLIA-256-CFB8' => 'CAMELLIA-256-CFB8 (256 key, 128 block)',
'CAMELLIA-256-OFB' => 'CAMELLIA-256-OFB (256 key, 128 block)',
'SEED-CBC' => 'SEED-CBC (128 key, 128 block)',
'SEED-CFB' => 'SEED-CFB (128 key, 128 block)',
'SEED-OFB' => 'SEED-OFB (128 key, 128 block)',
'BF-CBC' => 'BF-CBC(128 key, 64 block)'. ": $translate ",
);
return \%options;
}
1;