1#!/usr/bin/env perl
2
3# BEGIN BPS TAGGED BLOCK {{{
4#
5# COPYRIGHT:
6#
7# This software is Copyright (c) 1996-2021 Best Practical Solutions, LLC
8#                                          <sales@bestpractical.com>
9#
10# (Except where explicitly superseded by other copyright notices)
11#
12#
13# LICENSE:
14#
15# This work is made available to you under the terms of Version 2 of
16# the GNU General Public License. A copy of that license should have
17# been provided with this software, but in any event can be snarfed
18# from www.gnu.org.
19#
20# This work is distributed in the hope that it will be useful, but
21# WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23# General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program; if not, write to the Free Software
27# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28# 02110-1301 or visit their web page on the internet at
29# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
30#
31#
32# CONTRIBUTION SUBMISSION POLICY:
33#
34# (The following paragraph is not intended to limit the rights granted
35# to you to modify and distribute this software under the terms of
36# the GNU General Public License and is only of importance to you if
37# you choose to contribute your changes and enhancements to the
38# community by submitting them to Best Practical Solutions, LLC.)
39#
40# By intentionally submitting any modifications, corrections or
41# derivatives to this work, or any other work intended for use with
42# Request Tracker, to Best Practical Solutions, LLC, you confirm that
43# you are the copyright holder for those contributions and you grant
44# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
45# royalty-free, perpetual, license to use, copy, create derivative
46# works based on those contributions, and sublicense and distribute
47# those contributions and any derivatives thereof.
48#
49# END BPS TAGGED BLOCK }}}
50use strict;
51use warnings;
52
53use Getopt::Long;
54use FindBin;
55use Pod::Usage;
56use File::Spec::Functions qw(rel2abs);
57
58my %opt = (
59    root => ($ENV{RTHOME} || "/opt/rt4"),
60
61    fcgid   => 0,
62    fastcgi => 0,
63    perl    => 0,
64
65    modules => "/usr/lib/apache2/modules",
66);
67
68GetOptions(  \%opt,
69    "root=s",
70
71    "rt3|3!",
72
73    "fcgid!",
74    "fastcgi!",
75    "perl!",
76
77    "port|p=i",
78    "ssl:i",
79    "single|X",
80    "auth|A:s",
81
82    "modules=s",
83
84    "help|h|?",
85) or pod2usage( 1 );
86pod2usage( {verbose => 2} ) if $opt{help};
87
88# All paths must be absolute
89$opt{$_} = rel2abs($opt{$_})
90    for qw(root modules);
91
92# Determine what module to use
93my $mod;
94if ($opt{fcgid} + $opt{fastcgi} + $opt{perl} > 1) {
95    die "Can only supply one of fcgid, fastcgi, or perl\n";
96} elsif ($opt{fcgid} + $opt{fastcgi} + $opt{perl} == 0) {
97    my @guess = qw(fastcgi fcgid perl);
98    @guess = grep {-f "$opt{modules}/mod_$_.so"} @guess;
99    die "Neither mod_fcgid, mod_fastcgi, nor mod_perl are installed; aborting\n"
100        unless @guess;
101    warn "No deployment given -- assuming mod_$guess[0] deployment\n";
102    $mod = $guess[0];
103} else {
104    $mod = (grep {$opt{$_}} qw(fastcgi fcgid perl))[0];
105}
106
107# Sanity check that the root contains an RT install
108die "$opt{root} doesn't look like an RT install\n"
109    unless -e "$opt{root}/lib/RT.pm";
110
111# Detect if we are actually rt3
112if (not -e "$opt{root}/sbin/rt-server.fcgi"
113        and -e "$opt{root}/bin/mason_handler.fcgi") {
114    $opt{rt3}++;
115    warn "RT3 install detected!\n";
116}
117
118# Parse etc/RT_SiteConfig.pm for the default port
119my $RTCONF;
120$opt{port} ||= parseconf( "WebPort" );
121unless ($opt{port}) {
122    warn "Defaulting to port 8888\n";
123    $opt{port} = 8888;
124}
125
126# Set ssl port if they want it but didn't provide a number
127$opt{ssl} = 4430 if defined $opt{ssl} and not $opt{ssl};
128
129# Default auth to on if they set $WebRemoteUserAuth
130$opt{auth} = '' if not exists $opt{auth} and parseconf( "WebRemoteUserAuth" );
131
132# Set an auth path if they want it but didn't pass a path
133if (defined $opt{auth} and not $opt{auth}) {
134    $opt{auth} = "$opt{root}/var/htpasswd";
135    unless (-f $opt{auth}) {
136        open(my $fh, ">", $opt{auth}) or die "Can't create default htpasswd: $!";
137        print $fh 'root:$apr1$TZA4Y0DL$DS5ZhDH8QrhB.uAtvNJmh.' . "\n";
138        close $fh or die "Can't create default htpasswd: $!";
139    }
140} elsif ($opt{auth} and not -f $opt{auth}) {
141    die "Can't read htpasswd file $opt{auth}!";
142}
143
144# Parse out the WebPath
145my $path = parseconf( "WebPath" ) || "";
146
147my $template = join("", <DATA>);
148$template =~ s/\$PORT/$opt{port}/g;
149$template =~ s!\$PATH/!$path/!g;
150$template =~ s!\$PATH!$path || "/"!ge;
151$template =~ s/\$SSL/$opt{ssl} || 0/ge;
152$template =~ s/\$AUTH/$opt{auth}/ge;
153$template =~ s/\$RTHOME/$opt{root}/g;
154$template =~ s/\$MODULES/$opt{modules}/g;
155$template =~ s/\$TOOLS/$FindBin::Bin/g;
156$template =~ s/\$PROCESSES/$opt{single} ? 1 : 3/ge;
157
158my $conf = "$opt{root}/var/apache.conf";
159open(CONF, ">", $conf)
160    or die "Can't write $conf: $!";
161print CONF $template;
162close CONF;
163
164my @opts = ("-f", $conf, "-D" . uc($mod) );
165push @opts, "-DSSL" if $opt{ssl};
166push @opts, "-DRT3" if $opt{rt3};
167push @opts, "-DSINGLE" if $opt{single};
168push @opts, "-DREDIRECT" if $path;
169push @opts, "-DAUTH" if $opt{auth};
170
171# Wait for a previous run to terminate
172if ( open( PIDFILE, "<", "$opt{root}/var/apache2.pid") ) {
173    my $pid = <PIDFILE>;
174    chomp $pid;
175    close PIDFILE;
176    if ($pid and kill 0, $pid) {
177        warn "Waiting for previous run (pid $pid) to finish...\n";
178        sleep 1 while kill 0, $pid;
179    }
180}
181
182# Clean out the log in preparation
183my $log = "$opt{root}/var/log/apache-error.log";
184unlink($log);
185
186# Start 'er up
187warn "Starting apache server on http://localhost:$opt{port}$path/"
188    . ($opt{ssl} ? " and https://localhost:$opt{ssl}$path/" : "") . "\n";
189!system("apache2", @opts, "-k", "start")
190    or die "Can't exec apache2: $@";
191# Ignore the return value, as we expect it to be ^C'd
192system("tail", "-f", $log);
193warn "Shutting down apache...\n";
194!system("apache2", @opts, "-k", "stop")
195    or die "Can't exec apache2: $@";
196
197
198sub parseconf {
199    my ($optname) = @_;
200    # We're going to be evil, and try to parse the config
201    unless (defined $RTCONF) {
202        unless ( open(CONF, "<", "$opt{root}/etc/RT_SiteConfig.pm") ) {
203            warn "Can't open $opt{root}/etc/RT_SiteConfig.pm: $!\n";
204            $RTCONF = "";
205            return;
206        }
207        $RTCONF = join("", <CONF>);
208        close CONF;
209    }
210
211    return unless $RTCONF =~ /^\s*Set\(\s*\$$optname\s*(?:,|=>)\s*['"]?(.*?)['"]?\s*\)/m;
212    return $1;
213}
214
215=head1 NAME
216
217rt-apache - Wrapper to start Apache running RT
218
219=head1 DESCRIPTION
220
221This script exists to make it easier to run RT under Apache for testing.
222It is not intended as a way to deploy RT, or to provide example Apache
223configuration for RT.  For instructions on how to deploy RT with Apache,
224please read the provided F<docs/web_deployment.pod> file.
225
226Running this script will start F<apache2> with a custom-built
227configuration file, built based on command-line options and the contents
228of your F<RT_SiteConfig.pm>.  It will work with either RT 3.8.x or RT
2294.0.x.  As it is primarily for simple testing, it runs Apache as the
230current user.
231
232=head1 OPTIONS
233
234C<rt-apache> will parse your F<RT_SiteConfig.pm> for its C<WebPath> and
235C<WebPort> configuration, and adjust its defaults accordingly.
236
237=over
238
239=item --root B<path>
240
241The path to the RT install to serve.  This defaults to the C<RTHOME>
242environment variable, or C</opt/rt4>.
243
244=item --fastcgi, --fcgid, --perl
245
246Determines the Apache module which is used.  By default, the first one
247of that list which exists will be used.  See also L</--modules>.
248
249=item --port B<number>, -p
250
251Choses the port to listen on.  By default, this is parsed from the
252F<RT_SiteConfig.pm>, and falling back to 8888.
253
254=item --ssl [B<number>]
255
256Also listens on the provided port with HTTPS, using a self-signed
257certificate for C<localhost>.  If the port number is not specified,
258defaults to port 4430.
259
260=item --auth [F</path/to/htpasswd>], -A
261
262Turns on HTTP Basic Authentication; this is done automatically if
263C<$WebRemoteUserAuth> is set in the F<RT_SiteConfig.pm>.  The provided
264path should be to a F<htpasswd> file; if not given, defaults to a file
265containing only user C<root> with password C<password>.
266
267=item --single, -X
268
269Run only one process or thread, for ease of debugging.
270
271=item --rt3, -3
272
273Declares that the RT install in question is RT 3.8.x.  C<rt-apache> can
274usually detect this for you, however.
275
276=item --modules B<path>
277
278The path to the Apache2 modules directory, which is expected to contain
279at least one of F<mod_fcgid.so>, F<mod_fastcgi.so>, or F<mod_perl.so>.
280Defaults to F</usr/lib/apache2/modules>.
281
282=back
283
284=cut
285
286__DATA__
287Listen $PORT
288<IfDefine SSL>
289   Listen $SSL
290</IfDefine>
291
292ServerName localhost
293ServerRoot $RTHOME/var
294PidFile    $RTHOME/var/apache2.pid
295<IfVersion < 2.4>
296    LockFile   $RTHOME/var/apache2.lock
297</IfVersion>
298ServerAdmin root@localhost
299
300<IfVersion >= 2.4>
301    LoadModule mpm_prefork_module $MODULES/mod_mpm_prefork.so
302    LoadModule authz_core_module $MODULES/mod_authz_core.so
303</IfVersion>
304LoadModule authz_host_module  $MODULES/mod_authz_host.so
305LoadModule env_module         $MODULES/mod_env.so
306LoadModule alias_module       $MODULES/mod_alias.so
307LoadModule mime_module        $MODULES/mod_mime.so
308TypesConfig $TOOLS/mime.types
309
310<IfDefine SINGLE>
311    <IfModule mpm_prefork_module>
312        StartServers          1
313        MinSpareServers       1
314        MaxSpareServers       1
315        MaxClients            1
316        MaxRequestsPerChild   0
317    </IfModule>
318
319    <IfModule mpm_worker_module>
320        StartServers          1
321        MinSpareThreads       1
322        MaxSpareThreads       1
323        ThreadLimit           1
324        ThreadsPerChild       1
325        MaxClients            1
326        MaxRequestsPerChild   0
327    </IfModule>
328</IfDefine>
329
330<IfDefine PERL>
331    LoadModule perl_module    $MODULES/mod_perl.so
332</IfDefine>
333<IfDefine FASTCGI>
334    LoadModule fastcgi_module $MODULES/mod_fastcgi.so
335</IfDefine>
336<IfDefine FCGID>
337    LoadModule fcgid_module   $MODULES/mod_fcgid.so
338</IfDefine>
339<IfDefine SSL>
340    LoadModule ssl_module     $MODULES/mod_ssl.so
341    <IfVersion >= 2.4>
342        LoadModule socache_shmcb_module $MODULES/mod_socache_shmcb.so
343    </IfVersion>
344</IfDefine>
345
346<IfModule !log_config_module>
347    LoadModule log_config_module $MODULES/mod_log_config.so
348</IfModule>
349ErrorLog    "$RTHOME/var/log/apache-error.log"
350TransferLog "$RTHOME/var/log/apache-access.log"
351LogLevel notice
352
353<Directory />
354    Options FollowSymLinks
355    AllowOverride None
356    <IfVersion >= 2.4>
357        Require all denied
358    </IfVersion>
359    <IfVersion < 2.4>
360        Order deny,allow
361        Deny from all
362    </IfVersion>
363</Directory>
364
365AddDefaultCharset UTF-8
366
367<IfDefine REDIRECT>
368    LoadModule rewrite_module $MODULES/mod_rewrite.so
369    RewriteEngine on
370    RewriteRule ^(?!\Q$PATH\E) - [R=404]
371</IfDefine>
372
373<IfDefine AUTH>
374    <IfVersion >= 2.4>
375        LoadModule authn_core_module $MODULES/mod_authn_core.so
376    </IfVersion>
377    LoadModule auth_basic_module $MODULES/mod_auth_basic.so
378    LoadModule authn_file_module $MODULES/mod_authn_file.so
379    LoadModule authz_user_module $MODULES/mod_authz_user.so
380    <Location $PATH>
381        Require valid-user
382        AuthType basic
383        AuthName "RT access"
384        AuthBasicProvider file
385        AuthUserFile $AUTH
386    </Location>
387    <Location $PATH/REST/1.0/NoAuth/mail-gateway>
388        <IfVersion >= 2.4>
389            Require local
390        </IfVersion>
391        <IfVersion < 2.4>
392            Order deny,allow
393            Deny from all
394            Allow from localhost
395            Satisfy any
396        </IfVersion>
397    </Location>
398</IfDefine>
399<IfDefine !AUTH>
400    <Location $PATH>
401        <IfVersion >= 2.4>
402            Require all granted
403        </IfVersion>
404        <IfVersion < 2.4>
405            Order allow,deny
406            Allow from all
407        </IfVersion>
408    </Location>
409</IfDefine>
410
411<IfDefine !RT3>
412########## 4.0 mod_perl
413<IfDefine PERL>
414    PerlSetEnv RT_SITE_CONFIG $RTHOME/etc/RT_SiteConfig.pm
415    <Location $PATH>
416        SetHandler modperl
417        PerlResponseHandler Plack::Handler::Apache2
418        PerlSetVar psgi_app $RTHOME/sbin/rt-server
419    </Location>
420    <Perl>
421        use Plack::Handler::Apache2;
422        Plack::Handler::Apache2->preload("$RTHOME/sbin/rt-server");
423    </Perl>
424</IfDefine>
425
426########## 4.0 mod_fastcgi
427<IfDefine FASTCGI>
428    FastCgiIpcDir $RTHOME/var
429    FastCgiServer $RTHOME/sbin/rt-server.fcgi -processes $PROCESSES -idle-timeout 300
430    ScriptAlias $PATH $RTHOME/sbin/rt-server.fcgi/
431    <Location $PATH>
432        Options +ExecCGI
433        AddHandler fastcgi-script fcgi
434    </Location>
435</IfDefine>
436
437########## 4.0 mod_fcgid
438<IfDefine FCGID>
439    FcgidProcessTableFile $RTHOME/var/fcgid_shm
440    FcgidIPCDir $RTHOME/var
441    FcgidMaxRequestLen 1073741824
442    ScriptAlias $PATH $RTHOME/sbin/rt-server.fcgi/
443    <Location $PATH>
444        Options +ExecCGI
445        AddHandler fcgid-script fcgi
446    </Location>
447</IfDefine>
448</IfDefine>
449
450
451<IfDefine RT3>
452########## 3.8 mod_perl
453<IfDefine PERL>
454    PerlSetEnv RT_SITE_CONFIG $RTHOME/etc/RT_SiteConfig.pm
455    PerlRequire "$RTHOME/bin/webmux.pl"
456    <Location $PATH/NoAuth/images>
457        SetHandler default
458    </Location>
459    <Location $PATH>
460        SetHandler perl-script
461        PerlResponseHandler RT::Mason
462    </Location>
463</IfDefine>
464
465########## 3.8 mod_fastcgi
466<IfDefine FASTCGI>
467    FastCgiIpcDir $RTHOME/var
468    FastCgiServer $RTHOME/bin/mason_handler.fcgi -processes $PROCESSES -idle-timeout 300
469    ScriptAlias $PATH $RTHOME/bin/mason_handler.fcgi/
470    <Location $PATH>
471        Options +ExecCGI
472        AddHandler fastcgi-script fcgi
473    </Location>
474</IfDefine>
475
476########## 3.8 mod_fcgid
477<IfDefine FCGID>
478    FcgidProcessTableFile $RTHOME/var/fcgid_shm
479    FcgidIPCDir $RTHOME/var
480    FcgidMaxRequestLen 1073741824
481    ScriptAlias $PATH $RTHOME/bin/mason_handler.fcgi/
482    <Location $PATH>
483        Options +ExecCGI
484        AddHandler fcgid-script fcgi
485    </Location>
486</IfDefine>
487</IfDefine>
488
489<IfDefine SSL>
490    SSLRandomSeed startup builtin
491    SSLRandomSeed startup file:/dev/urandom 512
492    SSLRandomSeed connect builtin
493    SSLRandomSeed connect file:/dev/urandom 512
494    SSLSessionCache shmcb:$RTHOME/var/ssl_scache(512000)
495    <IfVersion < 2.4>
496        SSLMutex file:$RTHOME/var/ssl_mutex
497    </IfVersion>
498    <VirtualHost *:$SSL>
499        SSLEngine on
500        SSLCertificateFile    $TOOLS/localhost.crt
501        SSLCertificateKeyFile $TOOLS/localhost.key
502    </VirtualHost>
503</IfDefine>
504