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/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
|
3
|
+
Style/SignalException:
|
4
|
+
Enabled: false
|
5
|
+
StyleGuide: http://relaxed.ruby.style/#stylesignalexception
|
6
|
+
|
7
|
+
Style/RegexpLiteral:
|
8
|
+
MaxSlashes: 0
|
9
|
+
|
10
|
+
Metrics/AbcSize:
|
11
|
+
Max: 50
|
12
|
+
|
13
|
+
Metrics/CyclomaticComplexity:
|
14
|
+
Max: 22
|
15
|
+
|
16
|
+
Metrics/MethodLength:
|
17
|
+
Max: 42
|
18
|
+
|
19
|
+
Metrics/PerceivedComplexity:
|
20
|
+
Max: 12
|
21
|
+
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# This configuration was generated by `rubocop --auto-gen-config`
|
2
|
+
# on 2016-02-24 16:27:57 +0700 using RuboCop version 0.27.1.
|
3
|
+
# The point is for the user to remove these configuration records
|
4
|
+
# one by one as the offenses are removed from the code base.
|
5
|
+
# Note that changes in the inspected code, or installation of new
|
6
|
+
# versions of RuboCop, may require this file to be generated again.
|
7
|
+
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at Nicholas.Cowham@corbis.com. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
catflap (1.0.0)
|
5
|
+
json (>= 1.8.3)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
coderay (1.1.0)
|
11
|
+
diff-lcs (1.2.5)
|
12
|
+
json (1.8.3)
|
13
|
+
method_source (0.8.2)
|
14
|
+
pry (0.10.3)
|
15
|
+
coderay (~> 1.1.0)
|
16
|
+
method_source (~> 0.8.1)
|
17
|
+
slop (~> 3.4)
|
18
|
+
rake (10.5.0)
|
19
|
+
redcarpet (3.3.4)
|
20
|
+
rspec (3.4.0)
|
21
|
+
rspec-core (~> 3.4.0)
|
22
|
+
rspec-expectations (~> 3.4.0)
|
23
|
+
rspec-mocks (~> 3.4.0)
|
24
|
+
rspec-core (3.4.2)
|
25
|
+
rspec-support (~> 3.4.0)
|
26
|
+
rspec-expectations (3.4.0)
|
27
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
28
|
+
rspec-support (~> 3.4.0)
|
29
|
+
rspec-mocks (3.4.1)
|
30
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
31
|
+
rspec-support (~> 3.4.0)
|
32
|
+
rspec-support (3.4.1)
|
33
|
+
slop (3.6.0)
|
34
|
+
yard (0.8.7.6)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
bundler (~> 1.11)
|
41
|
+
catflap!
|
42
|
+
pry (~> 0.10.3)
|
43
|
+
rake (~> 10.0)
|
44
|
+
redcarpet (~> 3.3, >= 3.3.4)
|
45
|
+
rspec (~> 3.0)
|
46
|
+
yard (~> 0.8.7.6)
|
47
|
+
|
48
|
+
BUNDLED WITH
|
49
|
+
1.11.2
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Nyk Cowham
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
Catflap
|
2
|
+
=======
|
3
|
+

|
4
|
+
|
5
|
+
Catflap provides firewall level protection of multiple ports with a password
|
6
|
+
protected web gateway to allow developers and/or site demo/stage reviewers to
|
7
|
+
request entry after providing valid authentication credentials. Currently Catflap
|
8
|
+
supports Linux running the NetFilter (iptables) kernel-level firewall. However,
|
9
|
+
firewall specific implementations are provided as firewall plugin drivers and it
|
10
|
+
should be possible to write a separate plugin for the ipfw firewall on Mac OSX
|
11
|
+
and other FreeBSD derivatives.
|
12
|
+
|
13
|
+
Essentially, it is a more user-friendly form of "port knocking". The original
|
14
|
+
proof-of-concept implementation was run for almost three years by Demotix, to
|
15
|
+
protect development and staging servers from search engine crawlers and other
|
16
|
+
unwanted traffic.
|
17
|
+
|
18
|
+
# Use Cases
|
19
|
+
- Prevent web-bots and spiders from crawling and indexing your development,
|
20
|
+
demo and staging servers. Since they are protected by a firewall there are no
|
21
|
+
back doors.
|
22
|
+
- Provide a seamless login to sensitive web backends that may not have their own
|
23
|
+
complete user authentication enabled. One example is Kibana, used as a part of
|
24
|
+
the ELK logging service.
|
25
|
+
- Allow non-technical people to request access through a firewall when they do
|
26
|
+
not have a static IP (e.g. from home office) and no access to a VPN.
|
27
|
+
- Provides simple to use "port knocking" that does not require any technical
|
28
|
+
knowledge of command line networking from end users.
|
29
|
+
|
30
|
+
# Installation
|
31
|
+
Catflap is available as a ruby gem and can be installed with:
|
32
|
+
|
33
|
+
```
|
34
|
+
gem install catflap
|
35
|
+
```
|
36
|
+
|
37
|
+
You may also want to download the generic Linux init script
|
38
|
+
(https://github.com/nyk/catflap/blob/master/etc/init.d/catflap) and place that
|
39
|
+
in /etc/init.d/.
|
40
|
+
|
41
|
+
# Configuration
|
42
|
+
It is advisable to create a configuration file: /usr/local/etc/catflap.yaml
|
43
|
+
(this is the default location referenced by the init script, but you can specify
|
44
|
+
the location of your configuration file with the --config-file parameter to
|
45
|
+
the catflap command line tool.
|
46
|
+
|
47
|
+
This configuration file is a YAML file and the default configuration is listed
|
48
|
+
below:
|
49
|
+
|
50
|
+
```YAML
|
51
|
+
server:
|
52
|
+
listen_addr: '0.0.0.0' # What ip address the catflap server should listen on.
|
53
|
+
port: 4777 # The TCP port that the catflap server listens on.
|
54
|
+
docroot: './ui' # You can override the ui location.
|
55
|
+
endpoint: '/catflap' # The endpoint for the REST API.
|
56
|
+
passfile: './etc/passfile.yaml' # Pass phrases are stored here in this file.
|
57
|
+
token_ttl: 15 # Expire tokens after 15 seconds.
|
58
|
+
|
59
|
+
firewall:
|
60
|
+
plugin: 'netfilter' # Options are netfilter or iptables.
|
61
|
+
dports: '80,443' # Lock multiple ports separating them by commas.
|
62
|
+
options: # Options are specific to each firewall plugin driver.
|
63
|
+
chain: 'CATFLAP' # Two chains will be created <chain>-ALLOW & <chain>-DENY.
|
64
|
+
log_rejected: true # Enable logging of rejected requests.
|
65
|
+
accept_local: true # This is only set to false only when developers are testing catflap.
|
66
|
+
```
|
67
|
+
|
68
|
+
Once your configuration is in place you will then want to install the rules and
|
69
|
+
initialize catflap. This can be done with the command line tool:
|
70
|
+
|
71
|
+
```
|
72
|
+
sudo catflap -f /etc/catflap.yaml install
|
73
|
+
```
|
74
|
+
|
75
|
+
Catflap has a command line tool that you can use to add or remove addresses from
|
76
|
+
the access chain and other household maintenance. Just run 'catflap help' to see
|
77
|
+
the available options.
|
78
|
+
|
79
|
+
Now you will want to start the service. If you are using the init.d script this
|
80
|
+
is easy:
|
81
|
+
|
82
|
+
```
|
83
|
+
sudo service catflap start
|
84
|
+
```
|
85
|
+
|
86
|
+
If not you will need to start it with the command line directly (useful for
|
87
|
+
testing and debugging issues):
|
88
|
+
```
|
89
|
+
sudo catflap -f /etc/catflap.yaml start
|
90
|
+
```
|
91
|
+
|
92
|
+
# Gaining Access
|
93
|
+
Addresses and address ranges can be added using the command line with the 'add'
|
94
|
+
command, but remote users can request that their current IP address be granted
|
95
|
+
access by visiting the URL of the website that is being catflapped with their
|
96
|
+
web-browser. Catflap will redirect the target port (e.g. port 80) to the
|
97
|
+
Catflap port (e.g. 4777), so they will see the Catflap login screen. Once they
|
98
|
+
provide a valid pass phrase, their browser will refresh and they will see the
|
99
|
+
target website.
|
100
|
+
|
101
|
+
This is the default configuration, provided by the 'netfilter'
|
102
|
+
driver, which uses NAT on the firewall to forward the ports. You can use the
|
103
|
+
'ipfilter' driver instead, if you want to reject or drop packets rather than
|
104
|
+
automatically redirect to the Catflap login. The user would instead have to go
|
105
|
+
to the Catflap URL directly. However, the default 'netfilter' driver is
|
106
|
+
recommended if you want the best and most seamless end-user experience.
|
107
|
+
|
108
|
+
# Security considerations
|
109
|
+
Although we have been careful to avoid application level security vulnerabilities,
|
110
|
+
such as shell injection attacks, and no web user submitted data is passed to the
|
111
|
+
operating system without being sanitized (i.e. IP addresses are validated as being
|
112
|
+
valid IP addresses before being sent to the firewall interface), there are still
|
113
|
+
some areas of security concern to be aware of:
|
114
|
+
- The web service must run with root privileges, at the very least be run sudo
|
115
|
+
as a user with root privileges to add rules to the firewall. Such privileges are
|
116
|
+
unavoidable because the firewall runs in the kernel of the operating system.
|
117
|
+
A future release will separate the firewall execution process from the web
|
118
|
+
server, so the web server will run as an unprivileged user and only the
|
119
|
+
'Executor' process will run with higher privileges on an internal TCP network.
|
120
|
+
- The pass phrases in the passfile.yaml file are not encrypted. This file should
|
121
|
+
be placed in a private directory owned by root, chmod 600. If an unauthorized user
|
122
|
+
can read that file, then you have larger security problems than Catflap :)
|
123
|
+
- Unless you use SSL encryption it is not easy, but possible, for a network sniffer to capture
|
124
|
+
a valid token and possibly reuse the token to open the port for their own IP
|
125
|
+
address. This risk is very much lessened by the use of timestamps to expire
|
126
|
+
authentication tokens, but there is still some potential risk exposure. That
|
127
|
+
risk is eliminated entirely by encrypting traffic with TLS/SSL.
|
128
|
+
- It is recommended to flush the Catflap access rules every day or so (e.g. using
|
129
|
+
a cron job that calls 'sudo catflap purge' command). This is analogous to expiring
|
130
|
+
user login sessions.
|
131
|
+
- If you want to revoke access on a particular pass phrase, you must remove the
|
132
|
+
pass phrase from the passfile and ALSO flush the CATFLAP-ALLOW firewall chain, by
|
133
|
+
using the 'catflap purge' command, or remove each IP address with the
|
134
|
+
'catflap revoke <ip>' command.
|
data/Rakefile
ADDED
data/bin/catflap
CHANGED
@@ -1,82 +1,89 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
3
|
|
3
4
|
require 'optparse'
|
4
|
-
require 'catflap'
|
5
|
+
require 'catflap/command'
|
5
6
|
|
7
|
+
# Parse options
|
6
8
|
options = {}
|
7
|
-
|
8
|
-
opts.
|
9
|
-
opts.
|
10
|
-
|
11
|
-
options['install'] = true
|
9
|
+
OptionParser.new do |opts|
|
10
|
+
opts.banner = ''
|
11
|
+
opts.on('-d', '--daemonize', 'Run services as background processes') do
|
12
|
+
options[:daemonize] = true
|
12
13
|
end
|
13
|
-
opts.on('-
|
14
|
-
|
14
|
+
opts.on('-f', '--config-file <filepath>', String, 'Use config file' \
|
15
|
+
' to override default values') do |filepath|
|
16
|
+
options[:config_file] = filepath
|
15
17
|
end
|
16
|
-
opts.on('-
|
17
|
-
options[
|
18
|
+
opts.on('-V', '--version', 'Display the version of catflap') do
|
19
|
+
options[:version] = true
|
18
20
|
end
|
19
|
-
opts.on('-
|
20
|
-
options[
|
21
|
+
opts.on('-n', '--noop', 'Do not run destructive operations on firewall') do
|
22
|
+
options[:noop] = true
|
21
23
|
end
|
22
|
-
opts.
|
23
|
-
|
24
|
-
opts.on('-c', '--check <ipaddr>', String, 'Check if an IP address has access alreqdy') do |ip|
|
25
|
-
options['check'] = ip
|
24
|
+
opts.on('-s', '--https', 'Use HTTPS/SSL to start or manage service') do
|
25
|
+
options[:https] = true
|
26
26
|
end
|
27
|
-
opts.on('-
|
28
|
-
options[
|
27
|
+
opts.on('-v', '--verbose', 'Display additional information to screen') do
|
28
|
+
options[:verbose] = true
|
29
29
|
end
|
30
|
-
opts.on('-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
47
|
-
opts.on('-p', '--print', 'Print the iptables generated to screen') do
|
48
|
-
cf.print = true
|
49
|
-
end
|
50
|
-
opts.on('-h', '--help', 'Print this help page.') do
|
30
|
+
opts.on('-h', '--help', 'Print this help page.') do
|
31
|
+
puts "Usage: catflap <command> [<arg>]\n\n"
|
32
|
+
puts 'Commands:'
|
33
|
+
puts "\tstart \t\t\t Start a catflap server"
|
34
|
+
puts "\tstop \t\t\t Stop a catflap server"
|
35
|
+
puts "\trestart \t\t\t Restart a catflap server"
|
36
|
+
puts "\treload \t\t\t Reload the pass phrases without restarting the server"
|
37
|
+
puts "\tstatus \t\t\t Display the status of a catflap server"
|
38
|
+
puts "\tinstall \t\t Install and initialize the catflap rule chain"
|
39
|
+
puts "\tuninstall \t\t Uninstall catflap rules from firewall"
|
40
|
+
puts "\tcheck <ip> \t\t Check if <ip> already has access"
|
41
|
+
puts "\tgrant <ip> \t\t Add <ip> to allow access"
|
42
|
+
puts "\trevoke <ip> \t\t Remove access for <ip>"
|
43
|
+
puts "\tpurge \t\t\t Remove all catflap managed access grants"
|
44
|
+
puts "\tbulkload <filename> \t Bulk load a list of IPs from file"
|
45
|
+
puts "\tlist \t\t\t Display a list of all catflap managed access grants"
|
51
46
|
puts opts
|
52
47
|
exit 0
|
53
48
|
end
|
54
|
-
end.parse! ARGV
|
49
|
+
end.parse! ARGV # the options are stripped from ARGV after parsing.
|
50
|
+
command, arg = ARGV # destructure what is left into a command and its arguments.
|
51
|
+
|
52
|
+
cli = CfCommand.new options
|
55
53
|
|
56
54
|
begin
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
cf.install_rules! if options['install']
|
69
|
-
cf.uninstall_rules! if options['uninstall']
|
70
|
-
cf.add_address! options['add'] if options['add']
|
71
|
-
cf.delete_address! options['del'] if options['del']
|
72
|
-
cf.add_addresses_from_file! options['filepath'] if options['filepath']
|
73
|
-
cf.check_address options['check'] if options['check']
|
74
|
-
cf.list_rules if options['list']
|
75
|
-
rescue ArgumentError
|
76
|
-
puts "Invalid Argument: make sure IP address or range is correct (i.e. CIDR format)\n"
|
55
|
+
# pass command and arg to command dispatcher.
|
56
|
+
status = cli.dispatch_commands command, arg
|
57
|
+
|
58
|
+
puts status if command == 'version'
|
59
|
+
|
60
|
+
if command == 'check'
|
61
|
+
if status
|
62
|
+
puts "The ip '#{arg}' has access GRANTED"
|
63
|
+
else
|
64
|
+
puts "The ip '#{arg}' does NOT have access"
|
65
|
+
end
|
77
66
|
end
|
78
|
-
else
|
79
|
-
require 'catflap-http'
|
80
|
-
CatflapWebserver::start_server cf
|
81
|
-
end
|
82
67
|
|
68
|
+
if command == 'status'
|
69
|
+
if status[:pid] > 0
|
70
|
+
puts "Server listening on #{status[:address]}:#{status[:port]}" \
|
71
|
+
" <pid:#{status[:pid].to_s.chomp}>"
|
72
|
+
else
|
73
|
+
puts "Server is not running on #{status[:address]}:#{status[:port]}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
rescue Resolv::ResolvError => err
|
78
|
+
warn 'Malformed IP address error: ' << err.message
|
79
|
+
rescue Psych::SyntaxError => err
|
80
|
+
warn 'There is a YAML syntax error in your config file: ' << err.message
|
81
|
+
rescue ArgumentError => err
|
82
|
+
warn 'Missing argument error: ' << err.message
|
83
|
+
rescue IOError => err
|
84
|
+
warn 'File error: ' << err.message
|
85
|
+
rescue NameError => err
|
86
|
+
warn 'Command unknown error: ' << err.message
|
87
|
+
rescue StandardError => err
|
88
|
+
warn 'There was an error: ' << err.message
|
89
|
+
end
|