initial commit of file from CVS for smeserver-openvpn-bridge on Sat Sep 7 19:57:25 AEST 2024
This commit is contained in:
27
root/usr/lib/systemd/system/openvpn-bridge.service
Normal file
27
root/usr/lib/systemd/system/openvpn-bridge.service
Normal 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
|
||||
|
@@ -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;
|
Reference in New Issue
Block a user