File Coverage

blib/lib/Captive/Portal/Role/Config.pm
Criterion Covered Total %
statement 55 55 100.0
branch 5 8 62.5
condition n/a
subroutine 15 15 100.0
pod 2 2 100.0
total 77 80 96.2


line stmt bran cond sub pod time code
1             package Captive::Portal::Role::Config;
2              
3 6     6   9775 use strict;
  6         17  
  6         311  
4 6     6   37 use warnings;
  6         14  
  6         415  
5              
6             =head1 NAME
7              
8             Captive::Portal::Role::Config - config reader for Captive::Portal
9              
10             =head1 DESCRIPTION
11              
12             Config file parser and storage for cfg hash. The configuration syntax is perl.
13              
14              
15             =cut
16              
17             our $VERSION = '4.10';
18              
19 6     6   38 use Log::Log4perl qw(:easy);
  6         18  
  6         75  
20 6     6   18849 use FindBin qw($Bin);
  6         9899  
  6         987  
21 6     6   7574 use File::Spec::Functions qw(splitdir rootdir catfile catdir);
  6         23470  
  6         653  
22              
23 6     6   51 use Role::Basic;
  6         15  
  6         66  
24              
25             # just bin/../ => bin
26             my @bin_parts = splitdir($Bin);
27             pop(@bin_parts);
28              
29 6     6   1874 use constant TRUE => 1;
  6         13  
  6         456  
30 6     6   35 use constant FALSE => 0;
  6         12  
  6         329  
31              
32 6     6   32 use constant ON => 1;
  6         19  
  6         287  
33 6     6   41 use constant OFF => 0;
  6         17  
  6         401  
34              
35 6     6   31 use constant YES => 'yes';
  6         14  
  6         337  
36 6     6   32 use constant NO => '';
  6         19  
  6         293  
37              
38 6     6   164 use vars qw($APP_NAME $APP_DIR);
  6         13  
  6         8913  
39              
40             $APP_NAME = 'capo';
41             $APP_DIR = catdir(@bin_parts);
42              
43             # SINGLETON
44             my $cfg_hash = {};
45              
46             =head1 PRESET GLOBAL PACKAGE VARIABLES
47              
48             The following variables are predefined and can be used for interpolation in config values.
49              
50             $APP_NAME = 'capo'
51              
52             $APP_DIR = "$Bin/../"
53              
54             =head1 PRESET DEFAULTS
55              
56             =over 4
57              
58             =item DOCUMENT_ROOT => "$APP_DIR/static"
59              
60             Basedir for static content like images, css or error pages.
61              
62             =item TEMPLATE_INCLUDE_PATH => "$APP_DIR/templates/local/:$APP_DIR/templates/orig"
63              
64             Directories to search for templates.
65              
66             =item RUN_USER => 'wwwrun'
67              
68             Drop privileges to RUN_USER.
69              
70             =item RUN_GROUP => 'www',
71              
72             Drop privileges to RUN_GROUP.
73              
74             =item SESSIONS_DIR => "/var/cache/$APP_NAME"
75              
76             Where to store the session files. This directory must exist und must be readable/writeable by RUN_USER.
77              
78             =item SSL_REQUIRED => ON
79              
80             A JS script looks for SSL encryption of the login/splash page and throws an error when not. Maybe a man-in-the-middle plays http-https proxy like sslstrip(8). If the mitm strips JS then this doesn't help anyway. The users must check the location bar for HTTPS these days, sigh.
81              
82             =item SESSION_MAX => 48 * 3600 # 2d
83              
84             Max session time until a forced disconnect.
85              
86             =item IDLE_TIME => 60 * 10 # 10 min
87              
88             How long to wait for activity from ip/mac until a session is marked idle.
89              
90             =item KEEP_OLD_STATE_PERIOD => 1 * 60 * 60, # 1h
91              
92             How long to keep idle session records on disk for fast reconnect with proper ip/mac/cookie match.
93              
94             =back
95              
96             =head1 LOCAL PARAMETERS
97              
98             =over 4
99              
100             =item ADMIN_SECRET
101              
102             Passphrase for detailed sessions view.
103              
104             =item AUTHEN_SIMPLE_MODULES
105              
106             Authentication is handled by the Authen::Simple framework. You may stack any of the Authen::Simple::... plugins for authentication, see the $Bin/../etc/config.pl template.
107              
108             =item IPTABLES->capture_if => 'eth1'
109              
110             The inside gateway interface, e.g. 'eth1'. All http traffic, not allowed by any predefined rule, is captured and redirected to the capo.fcgi script.
111              
112             =item IPTABLES->capture_net => '192.168.0.0/22'
113              
114             The inside IP network in CIDR notation, e.g. '192.168.0.0/22'
115              
116             =item IPTABLES->capture_ports => [80, 8080]
117              
118             What tcp ports should be captured and redirected, e.g. [ 80, 8080]
119              
120             =item IPTABLES->redirect_port => 5281
121              
122             The port where the HTTP-server is listen in order to rewrite this http request to an https request.
123              
124             The above settings result in a NAT rule equivalent to:
125              
126             iptables -t nat -A PREROUTING -i eth1 -s 192.168.0.0/22 ! -d 192.168.0.0/22 \
127             -p tcp -m multiport --dports 80,8080 -j REDIRECT --to-port 5281
128              
129             =item IPTABLES->throttle => OFF
130              
131             You may throttle HTTP/HTTPS requests/sec per client IP. Some clients/gadgets fire a lot of HTTP traffic without human intervention. Depending on your hardware and your encryption resources this will overload your gateway.
132              
133             =item IPTABLES->throttle_ports => [ 80, 5281]
134              
135             You should protect/throttle port 80 and the redirect_port (see above).
136              
137              
138             =item IPTABLES->throttle_seconds => 30
139              
140             =item IPTABLES->throttle_hitcount => 15
141              
142             Both parameters define the average and the burst. Average is hitcount/seconds and burst is hitcount in seconds. With the values of 30 and 15, the average would be 15hits/30s => 1hit/2s. The burst would be 15hits in 30 seconds.
143              
144             The above settings result in iptable rules equivalent to:
145              
146             # throttle/drop new connections
147             iptables -t filter -A INPUT -p tcp --syn -m multiport --dports 80,5281 \
148             -m recent --name capo_throttle --rcheck --seconds 30 --hitcount 15 -j DROP
149              
150             # at last accept new connections but set/update the recent table
151             iptables -t filter -A INPUT -p tcp --syn -m multiport --dports 80,5281 \
152             -m recent --name capo_throttle --set -j ACCEPT
153              
154             =item IPTABLES->open_services
155              
156             Allow access to open local services like DHCP, DNS, NTP, ...
157              
158             =item IPTABLES->open_clients
159              
160             Allow access for some dumb clients without authentication.
161              
162             =item IPTABLES->open_servers
163              
164             Allow access to some open servers.
165              
166             =item IPTABLES->open_networks
167              
168             Allow access to some open networks.
169              
170             =item I18N_LANGUAGES
171              
172             Supported languages for system messages and HTML templates.
173              
174             =item I18N_FALLBACK_LANG
175              
176             Fallback language if the client message isn't supported in the system message catalog and templates.
177              
178             =item I18N_MSG_CATALOG
179              
180             Translations of the system messages.
181              
182             =back
183              
184             =cut
185              
186             my %pre_defaults = (
187             DOCUMENT_ROOT => catdir( $APP_DIR, 'static' ),
188              
189             TEMPLATE_INCLUDE_PATH => catdir( $APP_DIR, 'templates', 'local' ) . ':'
190             . catdir( $APP_DIR, 'templates', 'orig' ),
191              
192             SESSIONS_DIR => catdir( rootdir(), 'var', 'cache', $APP_NAME ),
193              
194             RUN_USER => 'wwwrun',
195             RUN_GROUP => 'www',
196              
197             SSL_REQUIRED => ON,
198             SESSION_MAX => 2 * 24 * 60 * 60, # 2 days
199             KEEP_OLD_STATE_PERIOD => 1 * 60 * 60, # 1h
200              
201             IDLE_TIME => 10 * 60, # 10min before set to idle
202              
203             I18N_LANGUAGES => [ 'en', ],
204             I18N_FALLBACK_LANG => 'en',
205             );
206              
207             # Role::Basic exports ALL subroutines, there is currently no other way to
208             # prevent exporting private methods, sigh
209             #
210             my ($_priv_post_defaults, $_priv_check_cfg);
211              
212             =head1 ROLES
213              
214             =over
215              
216             =item $capo->parse_cfg_file($filename)
217              
218             Parse config file, merge with defaults. Die on error.
219              
220             =cut
221              
222             sub parse_cfg_file {
223 8     8 1 19 my $self = shift;
224 8         17 my $cfg_file = shift;
225 8 50       44 LOGDIE "missing parameter 'config_file'" unless defined $cfg_file;
226              
227 8         38 DEBUG "preset cfg_hash with default values";
228 8         145 $cfg_hash = {%pre_defaults};
229              
230 8         65 DEBUG "parse config file $cfg_file";
231 8         8690 my $parsed_cfg_file = do $cfg_file;
232              
233             # check the config file for syntactic errors
234 8 100       14044 LOGDIE "couldn't parse $cfg_file: $@" if $@;
235 7 50       33 LOGDIE "couldn't do $cfg_file: $!"
236             unless defined $parsed_cfg_file;
237 7 50       33 LOGDIE "couldn't run $cfg_file" unless $parsed_cfg_file;
238              
239 7         52 DEBUG "merge parsed values with preset default values to cfg_hash";
240 7         196 $cfg_hash = { %$cfg_hash, %$parsed_cfg_file };
241              
242 7         61 $self->$_priv_check_cfg();
243              
244 5         24 $self->$_priv_post_defaults();
245              
246 5         32 return 1;
247             }
248              
249             =item $capo->cfg()
250              
251             Getter, return a shallow copy of the config hashref.
252              
253             =cut
254              
255 267     267 1 10100 sub cfg { return {%$cfg_hash}; }
256              
257             #
258             # Add some defaults after reading cfg file. Must be postponed to
259             # interpolate of already set params.
260             #
261              
262             $_priv_post_defaults = sub {
263             my $self = shift;
264              
265             # defined as anonymous sub,
266             # else Role::Basic would export this as role, sigh!
267              
268             DEBUG "add post_parse config default values, if needed";
269              
270             unless ( exists $cfg_hash->{LOCK_FILE} ) {
271             $cfg_hash->{LOCK_FILE} =
272             catfile( $cfg_hash->{SESSIONS_DIR}, 'capo-ctl.lock' );
273             }
274             };
275              
276             #
277             # semantic params validation of cfg_hash
278             #
279              
280             $_priv_check_cfg = sub {
281              
282             # defined as anonymous sub,
283             # else Role::Basic would export this as role, sigh!
284              
285             DEBUG "do cfg_hash params validation";
286              
287             # check the config file for sematic errors and warnings
288             if ( $cfg_hash->{BOILERPLATE} ) {
289             LOGDIE 'FATAL: the config file is in BOILERPLATE state';
290             }
291              
292             unless ( $cfg_hash->{SESSIONS_DIR} ) {
293             LOGDIE 'FATAL: missing SESSIONS_DIR in cfg file';
294             }
295              
296             if ( $cfg_hash->{MOCK_MAC} ) {
297             ERROR "uncomment 'MOCK_MAC' for production in cfg file";
298             }
299              
300             if ( $cfg_hash->{MOCK_FIREWALL} ) {
301             ERROR "uncomment 'MOCK_FIREWALL' for production in cfg file";
302             }
303              
304             if ( $cfg_hash->{MOCK_AUTHEN} ) {
305             ERROR "uncomment 'MOCK_AUTHEN' for production in cfg file";
306             }
307             else {
308             ERROR 'missing Authen::Simple modules in cfg file'
309             unless $cfg_hash->{'AUTHEN_SIMPLE_MODULES'};
310             }
311              
312             unless ( $cfg_hash->{ADMIN_SECRET} ) {
313             ERROR 'missing ADMIN_SECRET in cfg file';
314             }
315              
316             unless ( $cfg_hash->{SSL_REQUIRED} ) {
317             ERROR 'set SSL_REQUIRED for production in cfg file';
318             }
319              
320             unless ( $cfg_hash->{IPTABLES}{capture_if} ) {
321             ERROR "missing 'capture_if' in cfg file";
322             }
323              
324             unless ( $cfg_hash->{IPTABLES}{capture_net} ) {
325             ERROR "missing 'capture_net' in cfg file";
326             }
327              
328             unless ( $cfg_hash->{IPTABLES}{capture_ports} ) {
329             ERROR "missing 'capture_ports' in cfg file";
330             }
331              
332             unless ( $cfg_hash->{IPTABLES}{redirect_port} ) {
333             ERROR "missing 'redirect_port' in cfg file";
334             }
335             };
336              
337             1;
338              
339             =back
340              
341             =head1 AUTHOR
342              
343             Karl Gaissmaier, C<< >>
344              
345             =head1 LICENSE AND COPYRIGHT
346              
347             Copyright 2010-2013 Karl Gaissmaier, all rights reserved.
348              
349             This distribution is free software; you can redistribute it and/or modify it
350             under the terms of either:
351              
352             a) the GNU General Public License as published by the Free Software
353             Foundation; either version 2, or (at your option) any later version, or
354              
355             b) the Artistic License version 2.0.
356              
357             =cut
358              
359             #vim: sw=4