catflap 0.0.2 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.rubocop.yml +21 -0
- data/.rubocop_todo.yml +7 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +49 -0
- data/LICENSE +20 -0
- data/README.md +134 -0
- data/Rakefile +6 -0
- data/bin/catflap +71 -64
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/catflap.gemspec +32 -0
- data/etc/config.yaml +30 -0
- data/etc/init.d/catflap +89 -0
- data/etc/passfile.yaml +7 -0
- data/lib/catflap.rb +108 -64
- data/lib/catflap/command.rb +102 -0
- data/lib/catflap/firewall.rb +56 -0
- data/lib/catflap/http.rb +288 -0
- data/lib/catflap/netfilter/writer.rb +127 -0
- data/lib/catflap/plugins/firewall/iptables.rb +104 -0
- data/lib/catflap/plugins/firewall/netfilter.rb +114 -0
- data/lib/catflap/plugins/firewall/plugin.rb +67 -0
- data/lib/catflap/version.rb +5 -0
- data/lib/netfilter/writer.rb +125 -0
- data/ui/css/catflap.css +44 -0
- data/ui/images/catflap.png +0 -0
- data/ui/index.rhtml +23 -0
- data/ui/js/catflap.js +85 -0
- data/ui/js/sha256.js +166 -0
- metadata +109 -11
- data/lib/catflap-http.rb +0 -111
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'catflap'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
require 'pry'
|
11
|
+
Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/catflap.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'catflap/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'catflap'
|
8
|
+
s.version = Catflap::VERSION
|
9
|
+
s.summary = 'Manage NetFilter-based rules to grant port access on-demand ' \
|
10
|
+
'commandline or REST API requests.'
|
11
|
+
s.description = 'A simple solution to provide on-demand service access (e.g.
|
12
|
+
port 80 on webserver), where a more robust and secure VPN solution is not
|
13
|
+
available. Essentially, it is a more user-friendly form of "port knocking".
|
14
|
+
The original proof-of-concept implementation was run for almost three years
|
15
|
+
by Demotix, to protect development and staging servers from search engine
|
16
|
+
crawlers and other unwanted traffic.'
|
17
|
+
s.authors = ['Nyk Cowham']
|
18
|
+
s.email = 'nykcowham@gmail.com'
|
19
|
+
s.homepage = 'https://github.com/nyk/catflap'
|
20
|
+
s.files = `git ls-files -z`
|
21
|
+
.split("\x0")
|
22
|
+
.reject { |f| f.match(%r{^(test|spec|tasks|features)/}) }
|
23
|
+
s.executables = ['catflap']
|
24
|
+
s.licenses = ['MIT']
|
25
|
+
s.requirements = 'NetFilters (iptables) installed and working.'
|
26
|
+
s.require_paths = ['lib']
|
27
|
+
s.add_dependency 'json', '>= 1.8.3'
|
28
|
+
s.add_development_dependency 'bundler', '~> 1.11'
|
29
|
+
s.add_development_dependency 'rake', '~> 10.0'
|
30
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
31
|
+
s.bindir = 'bin'
|
32
|
+
end
|
data/etc/config.yaml
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# This is an example of the main configuration file for catflap.
|
2
|
+
# By default catflap looks for this file in /usr/local/etc/catflap/config.yaml
|
3
|
+
# but you can change the location by using the --config-file <filepath> option
|
4
|
+
# of the bin/catflap command line interface.
|
5
|
+
|
6
|
+
server:
|
7
|
+
listen_addr: '0.0.0.0' # what ip address the catflap server should listen on.
|
8
|
+
port: 8778 # the TCP port that the catflap server listens to.
|
9
|
+
docroot: './ui' # you can override the ui location.
|
10
|
+
endpoint: '/catflap' # the endpoint for the REST API.
|
11
|
+
passfile: './etc/passfile.yaml' # pass phrases are stored here.
|
12
|
+
token_ttl: 15 # expire tokens after 15 seconds.
|
13
|
+
pid_path: '/var/run' # The path where the pid file should be written.
|
14
|
+
|
15
|
+
https:
|
16
|
+
port: 4773 # TCP oport that catflap https server listens to.
|
17
|
+
force: true # Force HTTP requests to redirect to HTTPS.
|
18
|
+
certificate: '' # Path to your SSL certificate file.
|
19
|
+
private_key: '' # Private key for your SSL certificate.
|
20
|
+
|
21
|
+
firewall:
|
22
|
+
plugin: 'netfilter' # options are netfilter or iptables
|
23
|
+
dports: '80,443' # lock multiple ports separating them by commas.
|
24
|
+
options: # options are specific to each firewall plugin driver.
|
25
|
+
chain: 'CATFLAP' # Namespace for the chains (e.g. CATFLAP-ALLOW, CATFLAP-DENY).
|
26
|
+
forward:
|
27
|
+
80: 8778
|
28
|
+
443: 4773
|
29
|
+
log_rejected: true
|
30
|
+
accept_local: true # this is set to false only when devs are testing catflap.
|
data/etc/init.d/catflap
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
### BEGIN INIT INFO
|
2
|
+
# Provides: catflap
|
3
|
+
# Default-Start: 2 3 4 5
|
4
|
+
# Default-Stop: 0 1 6
|
5
|
+
# Short-Description: Catflap daemon to provide API web service.
|
6
|
+
# Description: Provides a means to request and gain access
|
7
|
+
# to certain restricted ports like 80 and 443
|
8
|
+
### END INIT INFO
|
9
|
+
|
10
|
+
# Using the lsb functions to perform the operations.
|
11
|
+
. /lib/lsb/init-functions
|
12
|
+
# Process name ( For display )
|
13
|
+
NAME=catflap
|
14
|
+
# Daemon name, where is the actual executable
|
15
|
+
DAEMON=/usr/local/bin/catflap
|
16
|
+
|
17
|
+
# pid file for the daemon
|
18
|
+
PIDFILE=/var/run/catflap.pid
|
19
|
+
|
20
|
+
# Include catflap defaults if available
|
21
|
+
if [ -f /etc/default/catflap ] ; then
|
22
|
+
. /etc/default/catflap
|
23
|
+
fi
|
24
|
+
|
25
|
+
# If the daemon is not there, then exit.
|
26
|
+
test -x $DAEMON || exit 5
|
27
|
+
|
28
|
+
case $1 in
|
29
|
+
start)
|
30
|
+
# Checked the PID file exists and check the actual status of process
|
31
|
+
if [ -e $PIDFILE ]; then
|
32
|
+
status_of_proc -p $PIDFILE $DAEMON "$NAME process" && status="0" || status="$?"
|
33
|
+
# If the status is SUCCESS then don't need to start again.
|
34
|
+
if [ $status = "0" ]; then
|
35
|
+
exit # Exit
|
36
|
+
fi
|
37
|
+
fi
|
38
|
+
# Start the daemon.
|
39
|
+
log_daemon_msg "Starting the process" "$NAME"
|
40
|
+
# Start the daemon with the help of start-stop-daemon
|
41
|
+
# Log the message appropriately
|
42
|
+
if start-stop-daemon --start --quiet --oknodo --background --make-pidfile --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_OPTS " start" 2>&1; then
|
43
|
+
log_end_msg 0
|
44
|
+
else
|
45
|
+
log_end_msg 1
|
46
|
+
fi
|
47
|
+
;;
|
48
|
+
stop)
|
49
|
+
# Stop the daemon.
|
50
|
+
if [ -e $PIDFILE ]; then
|
51
|
+
status_of_proc -p $PIDFILE $DAEMON "Stoppping the $NAME process" && status="0" || status="$?"
|
52
|
+
if [ "$status" = 0 ]; then
|
53
|
+
start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE
|
54
|
+
/bin/rm -rf $PIDFILE
|
55
|
+
fi
|
56
|
+
else
|
57
|
+
log_daemon_msg "$NAME process is not running"
|
58
|
+
log_end_msg 0
|
59
|
+
fi
|
60
|
+
;;
|
61
|
+
restart)
|
62
|
+
# Restart the daemon.
|
63
|
+
$0 stop && sleep 2 && $0 start
|
64
|
+
;;
|
65
|
+
status)
|
66
|
+
# Check the status of the process.
|
67
|
+
if [ -e $PIDFILE ]; then
|
68
|
+
status_of_proc -p $PIDFILE $DAEMON "$NAME process" && exit 0 || exit $?
|
69
|
+
else
|
70
|
+
log_daemon_msg "$NAME Process is not running"
|
71
|
+
log_end_msg 0
|
72
|
+
fi
|
73
|
+
;;
|
74
|
+
reload)
|
75
|
+
# Reload the process. Basically sending some signal to a daemon to reload
|
76
|
+
# it configurations.
|
77
|
+
if [ -e $PIDFILE ]; then
|
78
|
+
start-stop-daemon --stop --signal USR1 --quiet --pidfile $PIDFILE --name $NAME
|
79
|
+
log_success_msg "$NAME process reloaded successfully"
|
80
|
+
else
|
81
|
+
log_failure_msg "$PIDFILE does not exists"
|
82
|
+
fi
|
83
|
+
;;
|
84
|
+
*)
|
85
|
+
# For invalid arguments, print the usage message.
|
86
|
+
echo "Usage: $0 {start|stop|restart|reload|status}"
|
87
|
+
exit 2
|
88
|
+
;;
|
89
|
+
esac
|
data/etc/passfile.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# This is a sample passfile. It is recommened that you use your own
|
2
|
+
# pass phrases. A pass phrase must consist of at least two words separated by
|
3
|
+
# a space. The first word of the phrase is used as a unique key for efficient
|
4
|
+
# lookup of multiple pass phrases.
|
5
|
+
passphrases:
|
6
|
+
frisky: 'frisky kitten'
|
7
|
+
peeky: 'peeky blinders'
|
data/lib/catflap.rb
CHANGED
@@ -1,87 +1,131 @@
|
|
1
|
+
require 'catflap/version'
|
1
2
|
require 'yaml'
|
2
|
-
require '
|
3
|
+
require 'digest'
|
3
4
|
|
5
|
+
##
|
6
|
+
# Catflap controller class
|
7
|
+
#
|
8
|
+
# This class controls the initialization of the configuration file, setting
|
9
|
+
# options, initializing the firewall plugin driver and loading pass phrases.
|
10
|
+
#
|
11
|
+
# @author Nyk Cowham <nykcowham@gmail.com>
|
4
12
|
class Catflap
|
13
|
+
# @!attribute noop
|
14
|
+
# @return [Boolean] true if no operation is to be performed.
|
15
|
+
# @!attribute verbose
|
16
|
+
# @return [Boolean] true if command output should be written to stdout.
|
17
|
+
# @!attribute [r] fwplugin
|
18
|
+
# @return [String] the name of the firewall driver plugin file and class.
|
19
|
+
# @!attribute [r] bind_addr
|
20
|
+
# @return [String] the IP address the Catflap server should listen to.
|
21
|
+
# @!attribute [r] port
|
22
|
+
# @return [Integer] the port number the Catflap server should listen to.
|
23
|
+
# @!attribute [r] docroot
|
24
|
+
# @return [String] file path location that HTML/CSS/JS files are located.
|
25
|
+
# @!attribute [r] endpoint
|
26
|
+
# @return [String] the name of the REST service endpoint (e.g. /catflap).
|
27
|
+
# @!attribute [r] https
|
28
|
+
# @return [Hash<Symbol, String>] the HTTPS server configuration options.
|
29
|
+
# @!attribute [r] daemonize
|
30
|
+
# @return [Boolean] true if the web server should run in the background.
|
31
|
+
# @!attribute [r] dports
|
32
|
+
# @return [String] comma-separated list of ports to guard. (e.g. '80,443').
|
33
|
+
# @!attribute [r] passphrases
|
34
|
+
# @return [Hash<String, String>] associative array of pass phrases. The
|
35
|
+
# key is the first word of the pass phrase where words are separated by
|
36
|
+
# a space or non-alphanumeric character (except for the underscore).
|
37
|
+
# @!attribute [r] passfile
|
38
|
+
# @return [String] file path of the YAML file containg pass phrases.
|
39
|
+
# @!attribute [r] token_ttl
|
40
|
+
# @return [Integer] the time-to-live in seconds before tokens expire.
|
41
|
+
# @!attribute [r] pid_path
|
42
|
+
# @return [String] path/directory where pid file should be written.
|
43
|
+
# @!attribute [r] redirect_url
|
44
|
+
# @return [String] URL browser should be redirect to after authentication.
|
45
|
+
# @!attribute [r] firewall
|
46
|
+
# @return [FirewallPlugin]
|
47
|
+
# a firewall object that is implemented by the firewall driver plugin.
|
5
48
|
|
6
|
-
attr_accessor :
|
49
|
+
attr_accessor :verbose, :noop, :daemonize
|
7
50
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@config = YAML.load_file config_file if File.readable? config_file
|
12
|
-
@port = @config['server']['port'] || 4777
|
13
|
-
@chain = @config['rules']['chain'] || 'catflap-accept'
|
14
|
-
@dports = @config['rules']['dports'] || '80,443'
|
15
|
-
@print = false
|
16
|
-
@noop = false
|
17
|
-
@log_rejected = true
|
18
|
-
end
|
51
|
+
attr_reader :fwplugin, :listen_addr, :port, :docroot, :endpoint, :dports,
|
52
|
+
:passfile, :passphrases, :token_ttl, :pid_path, :redirect_url,
|
53
|
+
:firewall, :https
|
19
54
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
55
|
+
# @param [String, nil] file_path file path of the YAML configuration file.
|
56
|
+
# @param [Boolean] noop set to true to suppress destructive operations.
|
57
|
+
# @param [Boolean] verbose set to true to print operations to stdout stream.
|
58
|
+
# @return [Catflap]
|
59
|
+
def initialize(file_path = nil, noop = false, verbose = false)
|
60
|
+
@noop = noop
|
61
|
+
@verbose = verbose
|
62
|
+
initialize_config(file_path)
|
63
|
+
initialize_firewall_plugin
|
64
|
+
load_passphrases
|
27
65
|
end
|
28
66
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
execute! output
|
36
|
-
end
|
67
|
+
# Initialize the configuration options from the YAML configuration file.
|
68
|
+
# @param [String, nil] file_path file path of the YAML configuration file.
|
69
|
+
# @return void
|
70
|
+
def initialize_config(file_path = nil)
|
71
|
+
@config = read_config_file(file_path)
|
72
|
+
alias_server_attributes
|
37
73
|
|
38
|
-
|
39
|
-
output = "iptables -F #{@chain}"
|
40
|
-
execute! output
|
74
|
+
@https ||= {}
|
41
75
|
end
|
42
76
|
|
43
|
-
|
44
|
-
|
77
|
+
# Alias the server configuration attributes to instance variables.
|
78
|
+
def alias_server_attributes
|
79
|
+
@config['server'].each do |key, value|
|
80
|
+
instance_variable_set('@' + key, value)
|
81
|
+
end
|
45
82
|
end
|
46
83
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
84
|
+
# Find YAML configuration file, load and read it.
|
85
|
+
# @param [String, nil] file_path if one was passed on command line.
|
86
|
+
# @return [Hash] the parsed YAML file as a nested hash.
|
87
|
+
def read_config_file(file_path)
|
88
|
+
# Look for config file in order of precedence.
|
89
|
+
unless file_path
|
90
|
+
['~/.catflap', '/usr/local/etc/catflap', '/etc/catflap'].each do |path|
|
91
|
+
file = File.expand_path(path + '/config.yaml')
|
92
|
+
if File.readable? file
|
93
|
+
file_path = file
|
94
|
+
break
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
51
98
|
|
52
|
-
|
53
|
-
check_user_input ip
|
54
|
-
output = "iptables -I #{@chain} 1 -s #{ip} -p tcp -m multiport --dports #{@dports} -j ACCEPT\n"
|
55
|
-
execute! output
|
99
|
+
YAML.load_file(file_path || './etc/config.yaml')
|
56
100
|
end
|
57
101
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
102
|
+
# Initialize the firewall plugin driver.
|
103
|
+
# @return [FirewallPlugin] an object of a class inheriting from FirewallPlugin
|
104
|
+
def initialize_firewall_plugin
|
105
|
+
plugin = @config['firewall']['plugin']
|
106
|
+
driver = plugin.capitalize + 'Driver'
|
107
|
+
require_relative "catflap/plugins/firewall/#{plugin}.rb"
|
108
|
+
@firewall =
|
109
|
+
Object.const_get(driver).new(@config, @noop, @verbose)
|
110
|
+
end
|
63
111
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
execute! output
|
112
|
+
# Load the pass phrase YAML file.
|
113
|
+
# @raise [IOError] if the file is missing or not readable.
|
114
|
+
# @return void
|
115
|
+
def load_passphrases
|
116
|
+
if @passfile && File.readable?(@passfile)
|
117
|
+
phrases = YAML.load_file(@passfile)
|
118
|
+
@passphrases = phrases['passphrases']
|
72
119
|
else
|
73
|
-
|
74
|
-
exit 1
|
120
|
+
raise IOError, "Cannot read the passfile: #{@passfile}!"
|
75
121
|
end
|
76
122
|
end
|
77
123
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
return IPAddr.new(suspect)
|
124
|
+
# Generates a SHA256 encrypted token based on a passphrase and a timestamp
|
125
|
+
# @param [String] pass the passphrase stored in passfile.
|
126
|
+
# @param [String] timestamp to salt the digest.
|
127
|
+
# @return [String] a token in the form of a SHA256 digest of a string.
|
128
|
+
def generate_token(pass, salt)
|
129
|
+
Digest::SHA256.hexdigest(pass + salt)
|
85
130
|
end
|
86
|
-
|
87
131
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'catflap'
|
2
|
+
require 'catflap/http'
|
3
|
+
|
4
|
+
##
|
5
|
+
# Command controller class.
|
6
|
+
#
|
7
|
+
# This class separates the implementation details of the command line
|
8
|
+
# interface from the actual interface. This allows for creating custom
|
9
|
+
# command tools by directly implementing this class.
|
10
|
+
# @author Nyk Cowham <nykcowham@gmail.com>
|
11
|
+
class CfCommand
|
12
|
+
include CfWebserver
|
13
|
+
|
14
|
+
# Initialize a new CatflapCli object
|
15
|
+
# @param [Hash<String, String>] options an associative array of options
|
16
|
+
# read from the configuration file built by Catflap::initialize_config().
|
17
|
+
# @return CatflapCli
|
18
|
+
# @see
|
19
|
+
# Catflap - options are generated from file: Catflap::initialize_config()
|
20
|
+
def initialize(options)
|
21
|
+
@options = options
|
22
|
+
@cf = Catflap.new(
|
23
|
+
options[:config_file], options[:noop], options[:verbose]
|
24
|
+
)
|
25
|
+
@cf.daemonize = @options[:daemonize]
|
26
|
+
end
|
27
|
+
|
28
|
+
# A handler function to dispatch commands received from the front-end to the
|
29
|
+
# firewall driver class or the Catflap web service.
|
30
|
+
# @param [String] command the command that is to be executed.
|
31
|
+
# @param [String] arg and argument for the command, (e.g. an IP address).
|
32
|
+
# @return void
|
33
|
+
# @raise ArgumentError when a required command argument is missing.
|
34
|
+
# @raise NameError when the command is not recognized.
|
35
|
+
def dispatch_commands(command, arg)
|
36
|
+
# handle commands and options.
|
37
|
+
case command
|
38
|
+
when 'version'
|
39
|
+
"Catflap version #{Catflap::VERSION}"
|
40
|
+
when 'start'
|
41
|
+
server_start(@cf, @options[:https])
|
42
|
+
when 'stop'
|
43
|
+
server_stop(@cf, @options[:https])
|
44
|
+
when 'status'
|
45
|
+
server_status(@cf, @options[:https])
|
46
|
+
when 'restart'
|
47
|
+
begin
|
48
|
+
server_stop(@cf, @options[:https])
|
49
|
+
server_start(@cf, @options[:https])
|
50
|
+
end
|
51
|
+
when 'reload'
|
52
|
+
@cf.load_passphrases
|
53
|
+
when 'purge'
|
54
|
+
@cf.firewall.purge_rules
|
55
|
+
when 'install'
|
56
|
+
@cf.firewall.install_rules
|
57
|
+
when 'uninstall'
|
58
|
+
@cf.firewall.uninstall_rules
|
59
|
+
when 'list'
|
60
|
+
puts @cf.firewall.list_rules
|
61
|
+
when 'grant'
|
62
|
+
raise ArgumentError, 'You must provide a valid IP address' if arg.nil?
|
63
|
+
@cf.firewall.add_address(arg) unless @cf.firewall.check_address(arg)
|
64
|
+
when 'revoke'
|
65
|
+
raise ArgumentError, 'You must provide a valid IP address' if arg.nil?
|
66
|
+
@cf.firewall.delete_address(arg) if @cf.firewall.check_address(arg)
|
67
|
+
when 'check'
|
68
|
+
raise ArgumentError, 'You must provide a valid IP address' unless arg
|
69
|
+
return @cf.firewall.check_address(arg)
|
70
|
+
when 'bulkload'
|
71
|
+
raise ArgumentError, 'You must provide a file path' unless arg
|
72
|
+
add_addresses_from_file(arg)
|
73
|
+
when nil # catflap --version can be run with no command, so that's ok.
|
74
|
+
else
|
75
|
+
raise NameError, "there is no command '#{command}'"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
# rubocop:enable Metrics/CyclomaticComplexity,Metrics/MethodLength
|
79
|
+
# rubocop:enable Metrics/PerceivedComplexity,Metrics/AbcSize
|
80
|
+
|
81
|
+
# Handler function to bulkload IP's to the firewall.
|
82
|
+
#
|
83
|
+
# Checking that the file path points to readable file ensures that we can
|
84
|
+
# safely accept the user-submitted parameter without any additional data
|
85
|
+
# sanitization.
|
86
|
+
# @param [String] filepath path to the bulkload file of IP addresses to add.
|
87
|
+
# @return void
|
88
|
+
# @raise IOError if the file cannot be found or is not readable.
|
89
|
+
#
|
90
|
+
# @note Every IP address in the file is validated to ensure that it resolves
|
91
|
+
# to a valid IP address.
|
92
|
+
# @see Firewall Firewall::assert_valid_ipaddr(ip)
|
93
|
+
def add_addresses_from_file(filepath)
|
94
|
+
if File.readable? filepath
|
95
|
+
File.open(filepath, 'r').each_line do |ip|
|
96
|
+
@cf.firewall.add_address(ip.chomp)
|
97
|
+
end
|
98
|
+
else
|
99
|
+
raise IOError, "The file #{filepath} is not readable!"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|