Subversion Repositories web_pages

Rev

Rev 15 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 15 Rev 16
Line 1... Line 1...
1
#! /usr/bin/env perl
1
#! /usr/bin/env perl
-
 
2
 
2
# Copyright (c) 2025, Daily Data, Inc.
3
# Copyright (c) 2025, Daily Data, Inc.
3
# All rights reserved.
4
# All rights reserved.
4
#
5
#
5
# Redistribution and use in source and binary forms, with or without modification,
6
# Redistribution and use in source and binary forms, with or without modification,
6
# are permitted provided that the following conditions are met:
7
# are permitted provided that the following conditions are met:
Line 22... Line 23...
22
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26
# DAMAGE.
27
# DAMAGE.
-
 
28
#
-
 
29
# This script defines the routers that can be managed by loadOpnSense.pl
-
 
30
# It allows adding, editing, and removing router configurations stored in a JSON file.
-
 
31
#
-
 
32
# Change History:
-
 
33
#  v1.0.0 2025-10-01 RWR
-
 
34
#     Initial version 
27
 
35
 
28
use strict;
36
use strict;
29
use warnings;
37
use warnings;
30
 
38
 
-
 
39
# use libraries from the directory this script is in
-
 
40
BEGIN {
-
 
41
   use FindBin;
-
 
42
   use File::Spec;
-
 
43
   # use libraries from the directory this script is in
-
 
44
   use Cwd 'abs_path';
-
 
45
   use File::Basename;
-
 
46
   use lib dirname( abs_path( __FILE__ ) );
-
 
47
}
-
 
48
 
31
use JSON qw( decode_json encode_json );
49
use JSON qw( decode_json encode_json );
32
use Data::Dumper;
50
use Data::Dumper;
33
use lib '.';
-
 
34
use opnsense;
51
use opnsense;
35
 
52
 
-
 
53
our $VERSION = "1.0.0";
-
 
54
 
36
# default config file name if not specified on command line
55
# Default config file name if not specified on command line. Defaults to 'routers.json' in the script directory
37
my $configFileName = 'config.json';
56
my $configFileName = $FindBin::RealBin . '/routers.json';
-
 
57
# fields used in the router configuration
38
my @fields = ( 'url', 'apiKey', 'apiSecret', 'ovpnIndex', 'template', 'hostname', 'localport' );
58
my @fields = ( 'url', 'apiKey', 'apiSecret', 'ovpnIndex', 'template', 'hostname', 'localPort' );
-
 
59
 
-
 
60
# Check if running as root
-
 
61
if ($> != 0) {
-
 
62
   die "Error: This script must be run as root.\n";
-
 
63
}
39
 
64
 
-
 
65
#-----------------------------
-
 
66
# usage: Print usage message
-
 
67
#-----------------------------
40
sub usage {
68
sub usage {
41
   print "Usage: $0 <opnsense_config_file>\n";
69
   print "Usage: $0 <opnsenseConfigFile>\n";
42
   exit 1;
70
   exit 1;
43
}
71
}
44
 
72
 
45
# readConfig
73
#-----------------------------
46
# Reads in the configuration file and returns a hash ref of the data
74
# readConfig: Reads in the configuration file and returns a hash ref of the data
47
# if file does not exist, returns empty hash ref
75
# If file does not exist, returns empty hash ref
48
# file - name of file to read (default config.json if not specified)
76
# file - name of file to read (default $configFileName)
-
 
77
#-----------------------------
49
sub readConfig {
78
sub readConfig {
50
   my ( $file ) = @_;
79
   my ($file) = @_;
51
   my $data = {};
80
   my $data = {};
52
   if ( -e $file ) {
81
   if (-e $file) {
53
      open( my $fh, '<', $file ) or die "Cannot open $file: $!";
82
      open(my $fh, '<', $file) or die "Cannot open $file: $!";
54
      local $/;  # slurp mode
83
      local $/;  # slurp mode
55
      my $json_text = <$fh>;
84
      my $jsonText = <$fh>;
56
      close $fh;
85
      close $fh;
57
      $data = decode_json($json_text);
86
      $data = decode_json($jsonText);
58
   }
87
   }
59
   return $data;
88
   return $data;
60
}
89
}
61
 
90
 
-
 
91
#-----------------------------
-
 
92
# writeConfig: Writes the given data to the specified file in JSON format
-
 
93
# file - name of file to write (default $configFileName)
-
 
94
# data - reference to the data to write
-
 
95
# permissions on the file are set to 0600 and ownership to root
-
 
96
# since it contains sensitive information
-
 
97
#-----------------------------
62
sub writeConfig {
98
sub writeConfig {
63
   my ( $file, $data ) = @_;
99
   my ( $file, $data ) = @_;
64
   open( my $fh, '>', $file ) or die "Cannot open $file: $!";
100
   open( my $fh, '>', $file ) or die "Cannot open $file: $!";
65
   print $fh encode_json($data);
101
   print $fh encode_json($data);
66
   close $fh;
102
   close $fh;
-
 
103
   chmod 0600, $file or warn "Could not set permissions on $file: $!";
-
 
104
   chown 0, 0, $file or warn "Could not set ownership to root on $file: $!";
67
}
105
}
68
 
106
 
-
 
107
#-----------------------------
-
 
108
# trim: Remove leading and trailing whitespace from a string
-
 
109
# s - the string to trim
-
 
110
#-----------------------------
69
sub trim {
111
sub trim {
70
   my ($s) = @_;
112
   my ($s) = @_;
71
   $s =~ s/^\s+|\s+$//g if defined $s;
113
   $s =~ s/^\s+|\s+$//g if defined $s;
72
   return $s;
114
   return $s;
73
}
115
}
74
 
116
 
-
 
117
#-----------------------------
-
 
118
# menuSelect: Display a menu of options and prompt the user to select one
-
 
119
# prompt - the prompt to display to the user
-
 
120
# options - reference to an array of option strings
-
 
121
# additionalText - reference to an array of additional text strings to display next to each option
-
 
122
# returns the selected option string (1-based index)
-
 
123
#-----------------------------
75
sub menuSelect {
124
sub menuSelect {
76
   my ( $prompt, $options, $additionalText ) = @_;
125
   my ( $prompt, $options, $additionalText ) = @_;
77
   print "$prompt\n";
126
   print "$prompt\n";
78
   for my $i ( 0 .. $#$options ) {
127
   for my $i ( 0 .. $#$options ) {
79
      print "  " . ($i+1) . ". " . $options->[$i] . ($additionalText->[$i] ? $additionalText->[$i] : "") . "\n";
128
      print "  " . ($i+1) . ". " . $options->[$i] . ($additionalText->[$i] ? $additionalText->[$i] : "") . "\n";
80
   }
129
   }
81
   print "Select option (1-" . ($#$options + 1) . "): ";
130
   print "Select option (1-" . ($#$options + 1) . "): ";
82
   my $sel = <STDIN>;
131
   my $sel = <STDIN>;
83
   chomp $sel;
132
   chomp $sel;
84
   if ( $sel !~ /^\d+$/ || $sel < 1 || $sel > ($#$options + 1) ) {
133
   if ( $sel !~ /^\d+$/ || $sel < 1 || $sel > ($#$options + 1) ) {
85
      die "Invalid selection\n";
134
      warn "Invalid selection\n";
-
 
135
      return menuSelect( $prompt, $options, $additionalText );
86
   }
136
   }
87
   return $options->[$sel - 1];
137
   return $options->[$sel - 1];
88
}
138
}
89
 
139
 
-
 
140
#-----------------------------
-
 
141
# selectOvpnIndex: Prompts the user to select an ovpnIndex from the list
-
 
142
# in the OPNsense router specified in the config hash
-
 
143
# config - reference to the router's configuration hash
-
 
144
# returns the selected ovpnIndex string or undef if none selected
-
 
145
#-----------------------------
-
 
146
sub selectOvpnIndex {
-
 
147
   my ( $config ) = @_;
-
 
148
   if ( defined $config->{'url'} && defined $config->{'apiKey'} && defined $config->{'apiSecret'} ) {
-
 
149
      my $opnsense = new opnsense(
-
 
150
         url    => $config->{'url'},
-
 
151
         apiKey    => $config->{'apiKey'},
-
 
152
         apiSecret => $config->{'apiSecret'},
-
 
153
      );
-
 
154
      my $providers = $opnsense->getVpnProviders();
-
 
155
      if ( ref($providers) eq 'HASH' && keys %$providers ) {
-
 
156
         my @providerIndex = sort keys %$providers;
-
 
157
         my @additionalText = map { 
-
 
158
               defined $config->{'ovpnIndex'} &&
-
 
159
               ($_ eq $config->{'ovpnIndex'} ? " (current) " : " - ") . $providers->{$_} 
-
 
160
               } @providerIndex;
-
 
161
         my $selectedProvider = &menuSelect( "Select VPN Provider", \@providerIndex, \@additionalText );
-
 
162
         return $selectedProvider;
-
 
163
      } else {
-
 
164
         warn "No VPN providers found or error retrieving providers. Please check your connection details.\n";
-
 
165
      }
-
 
166
   } else {
-
 
167
      warn "Please set url, apiKey, and apiSecret before selecting ovpnIndex.\n";
-
 
168
   }
-
 
169
   return undef;
-
 
170
}
-
 
171
 
-
 
172
#-----------------------------
-
 
173
# editRouter: Edit the configuration for a router
-
 
174
# config - reference to the router's configuration hash
-
 
175
# routerName - name of the router being edited
-
 
176
# keys - reference to an array of keys (fields) that can be edited
-
 
177
# works on one router at a time, allows setting fields for that router
-
 
178
# returns true if configuration was saved, false if not
-
 
179
#-----------------------------
90
sub editRouter {
180
sub editRouter {
91
   my ( $config, $keys ) = @_;
181
   my ( $config, $routerName, $keys ) = @_;
92
   my $option = '';
182
   my $option = '';
-
 
183
   my $saved = 1;
-
 
184
   my $currentRouter = $config->{$routerName};
93
   $config->{'template'} = 'PlainOpenVPN' unless defined $config->{'template'};
185
   $currentRouter->{'template'} = 'PlainOpenVPN' unless defined $currentRouter->{'template'};
94
   my $vpnProviders = ();
186
   my $vpnProviders = ();
95
   while ( $option ne 'Done' ) {
187
   while ( $option ne 'Done' ) {
96
      print "\nEditing router configuration:\n";
188
      print "\nEditing router configuration for $routerName" . ($saved ? "" : " (Changed)") . ":\n";
97
      $option = &menuSelect( 
189
      $option = &menuSelect( 
98
         "Select field to edit", 
190
         "Select field to edit", 
99
         [ @$keys, 'Done' ], 
191
         [ @$keys, 'Save Config', 'Done' ], 
100
         [ map { defined $config->{$_} ? " (current: $config->{$_})" : " (not set)" } @$keys, '' ] 
192
         [ map { defined $currentRouter->{$_} ? " (current: $currentRouter->{$_})" : " (not set)" } @$keys, '' ] 
101
         );
193
         );
102
      if ( $option ne 'Done' ) {
-
 
103
         if ( $option eq 'ovpnIndex' ) {
194
      if ( $option eq 'ovpnIndex' ) {
104
            # special handling for ovpnIndex to show available providers
195
         $currentRouter->{'ovpnIndex'} = &selectOvpnIndex( $currentRouter );
105
            if ( defined $config->{'url'} && defined $config->{'apiKey'} && defined $config->{'apiSecret'} ) {
-
 
106
               my $opnsense = new opnsense(
196
         $saved = 0; # mark as unsaved
107
                  url    => $config->{'url'},
-
 
108
                  apiKey    => $config->{'apiKey'},
197
      } elsif ( $option eq 'Save Config' ) {
109
                  apiSecret => $config->{'apiSecret'},
198
         writeConfig( $configFileName, $config );
110
               );
-
 
111
               my $providers = $opnsense->get_vpn_providers();
-
 
112
               if ( ref($providers) eq 'HASH' && keys %$providers ) {
-
 
113
                  #die Dumper( $providers );
-
 
114
                  my @providerIndex = sort keys %$providers;
-
 
115
                  my @additionalText = map { 
-
 
116
                        defined $config->{'ovpnIndex'} &&
-
 
117
                        ($_ eq $config->{'ovpnIndex'} ? " (current) " : "") . $providers->{$_} 
-
 
118
                        } @providerIndex;
199
         $saved = 1; # mark as saved
119
                  my $selectedProvider = &menuSelect( "Select VPN Provider", \@providerIndex, \@additionalText );
-
 
120
                  $config->{'ovpnIndex'} = $selectedProvider;
200
         print "Configuration saved to $configFileName\n";
121
                  next; # skip the rest of the loop
-
 
122
               } else {
201
      } elsif ($option eq 'Done') {
123
                  print "No VPN providers found or error retrieving providers. Please check your connection details.\n";
202
         $option = &menuSelect( "Exiting configuration editor, but changes were not saved.", [ 'Exit', 'Save and Exit' ] );
124
               }
-
 
125
            } else {
-
 
126
               print "Please set url, apiKey, and apiSecret before selecting ovpnIndex.\n";
-
 
127
               next; # skip the rest of the loop
203
         if ( $option eq 'Save and Exit' ) {
128
            }
-
 
129
         }  else {
-
 
130
            print "Enter value for $option: ";
204
            writeConfig( $configFileName, $config );
131
            my $value = <STDIN>;
-
 
132
            chomp $value;
-
 
133
            $value = trim($value);
205
            $saved = 1; # mark as saved
134
            $config->{$option} = $value;
206
            print "Configuration saved to $configFileName\n";
135
         }
207
         }
-
 
208
         last;
-
 
209
      } else {
-
 
210
         $saved = 0; # mark as unsaved
-
 
211
         print "Enter value for $option: ";
-
 
212
         my $value = <STDIN>;
-
 
213
         chomp $value;
-
 
214
         $value = trim($value);
-
 
215
         $currentRouter->{$option} = $value if $value ne '';
136
      }
216
      }
137
   }
217
   }
138
   return $config;
218
   return $saved;
139
}
219
}
140
 
220
 
141
$configFileName = shift if defined shift;
221
$configFileName = shift if @ARGV;
142
 
-
 
-
 
222
# read the configuration file, if it exists, and place hash in $config
143
my $config = &readConfig( $configFileName );
223
my $config = &readConfig( $configFileName );
144
 
-
 
-
 
224
# display menu of existing routers plus option to add new router
145
my $routerName = &menuSelect( "Select router to configure", [ keys %$config, 'Add new router' ] );
225
my $routerName = &menuSelect( "Select router to configure", [ keys %$config, 'Add new router' ] );
-
 
226
# adding a new router, so get the name, then create an empty hashref for it
146
if ( $routerName eq 'Add new router' ) {
227
if ( $routerName eq 'Add new router' ) {
147
   print "Enter new router name: ";
228
   print "Enter new router name: ";
148
   $routerName = <STDIN>;
229
   $routerName = <STDIN>;
149
   chomp $routerName;
230
   chomp $routerName;
150
   $routerName = trim($routerName);
231
   $routerName = trim($routerName);
151
   $config->{$routerName} = {};
232
   $config->{$routerName} = {};
152
}
233
}
-
 
234
# edit the router configuration
-
 
235
&editRouter( $config, $routerName, \@fields );
153
 
236
 
154
$config->{$routerName} = &editRouter( $config->{$routerName}, \@fields );
-
 
155
 
-
 
156
#print Dumper( $config );
-
 
157
 
237
 
158
writeConfig( $configFileName, $config );
-
 
159
 
238
1;
-
 
239
 
160

Generated by GNU Enscript 1.6.5.90.
240

Generated by GNU Enscript 1.6.5.90.