[LON-CAPA-cvs] cvs: doc /install/linux install.pl
raeburn
raeburn at source.lon-capa.org
Mon Jul 16 20:19:51 EDT 2018
raeburn Tue Jul 17 00:19:51 2018 EDT
Modified files:
/doc/install/linux install.pl
Log:
- Modification/creation of configuration files for Apache/SSL for SNI (port 443)
when replicating content from /raw/, and for redirects to http://.
-------------- next part --------------
Index: doc/install/linux/install.pl
diff -u doc/install/linux/install.pl:1.46 doc/install/linux/install.pl:1.47
--- doc/install/linux/install.pl:1.46 Wed Jul 11 01:58:41 2018
+++ doc/install/linux/install.pl Tue Jul 17 00:19:51 2018
@@ -26,6 +26,7 @@
use strict;
use File::Copy;
use Term::ReadKey;
+use Socket;
use Sys::Hostname::FQDN();
use DBI;
use Cwd();
@@ -76,7 +77,7 @@
&mt('Stopping execution.')."\n";
exit;
} else {
- print LOG '$Id: install.pl,v 1.46 2018/07/11 01:58:41 raeburn Exp $'."\n";
+ print LOG '$Id: install.pl,v 1.47 2018/07/17 00:19:51 raeburn Exp $'."\n";
}
#
@@ -281,6 +282,13 @@
return ($distro,$packagecmd,$updatecmd,$installnow);
}
+#
+# get_hostname() prompts the user to provide the server's hostname.
+#
+# If invalid input is provided, the routine is called recursively
+# until, a valid hostname is provided.
+#
+
sub get_hostname {
my $hostname;
print &mt('Enter the hostname of this server, e.g., loncapa.somewhere.edu'."\n");
@@ -301,6 +309,41 @@
return $hostname;
}
+#
+# get_hostname() prompts the user to provide the server's IPv4 IP address
+#
+# If invalid input is provided, the routine is called recursively
+# until, a valid IPv4 address is provided.
+#
+
+sub get_hostip {
+ my $hostip;
+ print &mt('Enter the IP address of this server, e.g., 192.168.10.24'."\n");
+ my $choice = <STDIN>;
+ chomp($choice);
+ $choice =~ s/(^\s+|\s+$)//g;
+ my $badformat = 1;
+ if ($choice eq '') {
+ print &mt("IP address you entered was either blank or contained only white space.\n");
+ } else {
+ if ($choice =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) {
+ if (($1<=255) && ($2<=255) && ($3<=255) && ($4<=255)) {
+ $badformat = 0;
+ }
+ }
+ if ($badformat) {
+ print &mt('Host IP you entered was invalid -- a host IP has the format d.d.d.d where each d is an integer between 0 and 255')."\n";
+ } else {
+ $hostip = $choice;
+ }
+ }
+ while ($hostip eq '') {
+ $hostip = &get_hostip();
+ }
+ print "\n";
+ return $hostip;
+}
+
sub check_prerequisites {
my ($packagecmd,$distro) = @_;
my $gotprereqs;
@@ -411,7 +454,7 @@
}
my ($mysqlon,$mysqlsetup,$mysqlrestart,$dbh,$has_pass,$has_lcdb,%recommended,
$downloadstatus,$filetouse,$production,$testing,$apachefw,$tostop,
- $uses_systemctl,$hostname);
+ $uses_systemctl,$hostname,$hostip);
my $wwwuid = &uid_of_www();
my $wwwgid = getgrnam('www');
if (($wwwuid eq '') || ($wwwgid eq '')) {
@@ -420,7 +463,7 @@
unless( -e "/usr/local/sbin/pwauth") {
$recommended{'pwauth'} = 1;
}
- my $hostname = Sys::Hostname::FQDN::fqdn();
+ $hostname = Sys::Hostname::FQDN::fqdn();
if ($hostname eq '') {
$hostname =&get_hostname();
} else {
@@ -429,7 +472,17 @@
$hostname =&get_hostname();
}
}
- print_and_log(&mt('Hostname is [_1]',$hostname)."\n");
+ $hostname = Sys::Hostname::FQDN::fqdn();
+ $hostip = Socket::inet_ntoa(scalar(gethostbyname($hostname)) || 'localhost');
+ if ($hostip eq '') {
+ $hostip=&get_hostip();
+ } else {
+ print &mt("Host IP address detected: $hostip. Is that correct? ~[Y/n~]");
+ if (!&get_user_selection(1)) {
+ $hostip=&get_hostip();
+ }
+ }
+ print_and_log("\n".&mt('Hostname is [_1] and IP address is [_2]',$hostname,$hostip)."\n");
$mysqlon = &check_mysql_running($distro);
if ($mysqlon) {
my $mysql_has_wwwuser = &check_mysql_wwwuser();
@@ -458,16 +511,20 @@
}
}
}
+ my ($sslhostsfilesref,$has_std,$has_int,$rewritenum,$nochgstd,$nochgint);
($recommended{'firewall'},$apachefw) = &chkfirewall($distro);
($recommended{'runlevels'},$tostop,$uses_systemctl) = &chkconfig($distro,$instdir);
$recommended{'apache'} = &chkapache($distro,$instdir);
- $recommended{'apachessl'} = &chkapachessl($distro,$instdir,$hostname);
+ ($recommended{'apachessl'},$sslhostsfilesref,$has_std,$has_int,$rewritenum,
+ $nochgstd,$nochgint) = &chkapachessl($distro,$instdir,$hostname,$hostip);
$recommended{'stopsrvcs'} = &chksrvcs($distro,$tostop);
($recommended{'download'},$downloadstatus,$filetouse,$production,$testing)
= &need_download();
return ($distro,$gotprereqs,$localecmd,$packagecmd,$updatecmd,$installnow,
$mysqlrestart,\%recommended,$dbh,$has_pass,$has_lcdb,$downloadstatus,
- $filetouse,$production,$testing,$apachefw,$uses_systemctl,$hostname);
+ $filetouse,$production,$testing,$apachefw,$uses_systemctl,$hostname,
+ $hostip,$sslhostsfilesref,$has_std,$has_int,$rewritenum,$nochgstd,
+ $nochgint);
}
sub check_mysql_running {
@@ -882,42 +939,292 @@
return $fixapache;
}
+#
+# chkapachessl() determines whether a server's Apache SSL configuration
+# needs updating to support LON-CAPA.
+#
+# LON-CAPA uses VirtualHosts for port 443, and requires that they are
+# defined in one Apache configuration file containing two VirtualHost
+# blocks, in order:
+#
+# (1) a block with no ServerName, or with ServerName set to the
+# server's hostname. This block should contain:
+#
+# <IfModule mod_rewrite.c>
+# LON-CAPA rewrite rules defined in sslrewrite.conf
+# </IfModule>
+#
+# (2) a block with ServerName set to internal-$hostname
+# (where $hostname is server's hostname).
+# This block should contain the config and rewrite rules
+# found in loncapassl.conf.
+#
+# chkapachessl() retrieves the names of .conf files in
+# the directory appropriate for the particular Linux distro,
+# and then checks to see which .conf file is the best candidate as
+# the single file containing VirtualHosts definitions and
+# <IfModule mod_rewrite.c> </IfModule> rewrite blocks.
+#
+# The best candidate is the one containing a block:
+# <VirtualHost ????? :443>
+# (where ????? might be _default_ or * or an IP address)
+# <IfModule mod_rewrite.c>
+# </IfModule>
+# </VirtualHost>
+# with the fewest differences between the contents of the
+# IfModule block and the expected contents (from sslrewrite.conf)
+#
+# If there are no files with rewrite blocks, then a candidate file
+# is chosen from the .conf files containing VirtualHosts definitions.
+#
+# If the user includes "Configure SSL for Apache web server" as
+# one of the actions to take to prepare the server for LON-CAPA
+# installation, then the output from &chkapachessl() will be
+# used to determined which file will contain VirtualHost configs.
+#
+# If there are no files containing VirtualHosts definitions, then
+# <VirtualHost *:443> </VirtualHost> blocks will be appended to
+# the standard Apache SSL config for the particular distro:
+# ssl.conf for RHEL/CentOS/Scientific/Fedora, vhost-ssl.conf
+# for SuSE/SLES, and default-ssl.conf for Ubuntu.
+#
+# Once a file is selected, the contents of sslrewrite.conf and
+# loncapassl.conf are compared with appropriate blocks in the file
+# and the user will be prompted to agree to insertion of missing
+# lines and/or deletion of surplus lines.
+#
+
sub chkapachessl {
- my ($distro,$instdir,$hostname) = @_;
+ my ($distro,$instdir,$hostname,$hostip) = @_;
my $fixapachessl = 1;
- my $stdconf = "$instdir/loncapassl.conf";
- if (!-e $stdconf) {
+ my $sslintconf = "$instdir/loncapassl.conf";
+ my $sslrewriteconf = "$instdir/sslrewrite.conf";
+ my (%sslfiles,%rewrites,%vhostonly,$has_std,$has_int,$rewritenum,$nochgint,$nochgstd);
+ $nochgstd = 0;
+ $nochgint = 0;
+ if (!-e $sslintconf) {
+ $fixapachessl = 0;
+ print &mt('Warning: LON-CAPA SSL Apache configuration file [_1] needed for installation check.',$sslintconf)."\n";
+ } elsif (!-e $sslrewriteconf) {
$fixapachessl = 0;
- print &mt('Warning: No LON-CAPA SSL Apache configuration file found for installation check.')."\n";
+ print &mt('Warning: LON-CAPA SSL Apache configuration file [_1] needed for installation check is missing.',$sslrewriteconf)."\n";
} else {
- my $sslfile;
+ my $ssldir;
if ($distro =~ /^(debian|ubuntu)(\d+)$/) {
- $sslfile = '/etc/apache2/sites-available/loncapassl.conf';
+ $ssldir = '/etc/apache2/sites-available';
} elsif ($distro =~ /(suse|sles)/) {
- $sslfile = '/etc/apache2/vhosts.d/loncapassl.conf';
+ $ssldir = '/etc/apache2/vhosts.d';
} else {
- $sslfile = '/etc/httpd/conf.d/loncapassl.conf';
+ $ssldir = '/etc/httpd/conf.d';
}
- if ((-e $sslfile) && (-e $stdconf)) {
- if (open(PIPE, "diff -y -bi --suppress-common-lines $stdconf $sslfile |")) {
- my $diffres = <PIPE>;
- close(PIPE);
- chomp($diffres);
- if ($diffres =~ /^\QServerName internal-{[[[[Hostname]]]]}\E\s+\|\s+\QServerName internal-\E$hostname$/) {
- $fixapachessl = 0;
+ my @rewritessl = ();
+ if (open(my $fh,'<',$sslrewriteconf)) {
+ my $skipnext = 0;
+ while (<$fh>) {
+ chomp();
+ s/(^\s+|\s+$)//g;
+ next if ($_ eq '');
+ next if ($_ eq '<IfModule mod_rewrite.c>');
+ next if ($_ eq '</IfModule>');
+ if ($_ eq 'RewriteCond %{REMOTE_ADDR} {[[[[HostIP]]]]}') {
+ if (($hostip ne '') && ($hostip ne '127.0.0.1')) {
+ push(@rewritessl,'RewriteCond %{REMOTE_ADDR} '.$hostip);
+ next;
+ } else {
+ $skipnext = 1;
+ }
+ } elsif (($_ eq 'RewriteRule (.*) - [L]') && ($skipnext)) {
+ $skipnext = 0;
+ next;
+ }
+ push(@rewritessl,$_);
+ }
+ }
+ my @intssl = ();
+ if (open(my $fh,'<',$sslintconf)) {
+ while(<$fh>) {
+ chomp();
+ s/(^\s+|\s+$)//g;
+ next if ($_ eq '');
+ if ($_ eq 'ServerName internal-{[[[[Hostname]]]]}') {
+ if ($hostname ne '') {
+ push(@intssl,'ServerName internal-'.$hostname);
+ next;
+ }
+ }
+ next if ($_ eq '<VirtualHost *:443>');
+ next if ($_ eq '</VirtualHost>');
+ push(@intssl,$_);
+ }
+ }
+ if (-d $ssldir) {
+ my @actualint = ();
+ if (opendir(my $dir,$ssldir)) {
+ my @sslconf_files;
+ foreach my $file (grep(!/^\.+/,readdir($dir))) {
+ next if (($distro =~ /(suse|sles)/) && ($file =~ /\.template$/));
+ next if ($file =~ /\.rpmnew$/);
+ if (open(my $fh,'<',"$ssldir/$file")) {
+ while (<$fh>) {
+ if (/^\s*<VirtualHost\s+[^:]*\:443>\s*$/) {
+ push(@sslconf_files,$file);
+ last;
+ }
+ }
+ close($fh);
+ }
+ }
+ closedir($dir);
+ if (@sslconf_files) {
+ foreach my $file (@sslconf_files) {
+ if (open(my $fh,'<',"$ssldir/$file")) {
+ my ($virtualhost,$rewrite,$num) = (0,0,0);
+ my ($currname,$has_rewrite);
+ while (<$fh>) {
+ chomp();
+ next if (/^\s*$/);
+ if ($virtualhost) {
+ if (/^\s*<\/VirtualHost>/) {
+ if ($currname !~ /^\Qinternal-$hostname\E/) {
+ if ($has_rewrite) {
+ delete($vhostonly{$file});
+ } else {
+ $vhostonly{$file} = 1;
+ }
+ }
+ $sslfiles{$currname}{$file} = 1;
+ $virtualhost = 0;
+ $currname = '';
+ $has_rewrite = '';
+ next;
+ } elsif (/^\s*ServerName\s+([^\s]+)\s*$/) {
+ $currname = $1;
+ }
+ if ($currname =~ /^\Qinternal-$hostname\E/) {
+ s/(^\s+|\s+$)//g;
+ push(@actualint,$_);
+ $has_int = $file;
+ } else {
+ if ($rewrite) {
+ if (/^\s*<\/IfModule>/) {
+ $rewrite = 0;
+ $num ++;
+ } else {
+ s/(^\s+|\s+$)//g;
+ push(@{$rewrites{$file}[$num]},$_);
+ }
+ } elsif (/^\s*<IfModule\s+mod_rewrite\.c>/) {
+ $rewrite = 1;
+ $has_rewrite = 1;
+ if ($currname eq '') {
+ $currname = $hostname;
+ }
+ $rewrites{$file}[$num] = [];
+ }
+ }
+ } elsif (/^\s*<VirtualHost\s+[^:]*\:443>\s*$/) {
+ $virtualhost = 1;
+ }
+ }
+ close($fh);
+ }
+ }
+ }
+ if (keys(%rewrites)) {
+ my $mindiffsall;
+ foreach my $file (sort(keys(%rewrites))) {
+ if (ref($rewrites{$file}) eq 'ARRAY') {
+ my $mindiffs;
+ for (my $i=0; $i<@{$rewrites{$file}}; $i++) {
+ if (ref($rewrites{$file}[$i]) eq 'ARRAY') {
+ my @diffs = &compare_arrays($rewrites{$file}[$i],\@rewritessl);
+ if (@diffs == 0) {
+ $fixapachessl = 0;
+ $mindiffs = 0;
+ $rewritenum = 1+$i;
+ last;
+ } else {
+ if ($mindiffs eq '') {
+ $mindiffs = scalar(@diffs);
+ $rewritenum = 1+$i;
+ } elsif (scalar(@diffs) <= $mindiffs) {
+ $mindiffs = scalar(@diffs);
+ $rewritenum = 1+$i;
+ }
+ }
+ }
+ }
+ if ($mindiffsall eq '') {
+ $mindiffsall = $mindiffs;
+ $has_std = $file;
+ } elsif ($mindiffs <= $mindiffsall) {
+ $mindiffsall = $mindiffs;
+ $has_std = $file;
+ }
+ if ($mindiffsall == 0) {
+ $nochgstd = 1;
+ }
+ }
+ }
+ } elsif (keys(%vhostonly) > 0) {
+ if (($has_int ne '') && (exists($vhostonly{$has_int}))) {
+ $has_std = $has_int;
+ }
+ }
+ if (@actualint) {
+ my @diffs = &compare_arrays(\@actualint,\@intssl);
+ if (@diffs) {
+ $fixapachessl = 1;
+ } else {
+ $nochgint = 1;
+ }
+ } else {
+ $fixapachessl = 1;
}
}
}
unless ($fixapachessl) {
if ($distro =~ /^(debian|ubuntu)(\d+)$/) {
- unless ((-l '/etc/apache2/sites-enabled/loncapassl.conf') &&
- (readlink('/etc/apache2/sites-enabled/loncapassl.conf') eq '/etc/apache2/sites-available/loncapassl.conf')) {
- print_and_log(&mt("Warning, use: 'sudo a2ensite loncapassl.conf' to activate LON-CAPA SSL Apache config\n"));
+ my $enabled_dir = '/etc/apache2/sites-enabled';
+ if (keys(%sslfiles)) {
+ foreach my $key (sort(keys(%sslfiles))) {
+ if (ref($sslfiles{$key}) eq 'HASH') {
+ foreach my $file (sort(keys(%{$sslfiles{$key}}))) {
+ unless ((-l "$enabled_dir/$file") &&
+ (readlink("$enabled_dir/$file") eq "$ssldir/$file")) {
+ print_and_log(&mt("Warning, use: 'sudo a2ensite $file' to activate LON-CAPA SSL Apache config\n"));
+ }
+ }
+ }
+ }
}
}
}
}
- return $fixapachessl;
+ return ($fixapachessl,\%sslfiles,$has_std,$has_int,$rewritenum,$nochgstd,$nochgint);
+}
+
+#
+# compare_arrays() expects two refs to arrays as args.
+#
+# The contents of the two arrays are compared, and if they
+# are different, and array of the differences is returned.
+#
+
+sub compare_arrays {
+ my ($arrayref1,$arrayref2) = @_;
+ my (@difference,%count);
+ @difference = ();
+ %count = ();
+ if ((ref($arrayref1) eq 'ARRAY') && (ref($arrayref2) eq 'ARRAY')) {
+ foreach my $element (@{$arrayref1}, @{$arrayref2}) { $count{$element}++; }
+ foreach my $element (keys(%count)) {
+ if ($count{$element} == 1) {
+ push(@difference,$element);
+ }
+ }
+ }
+ return @difference;
}
sub chksrvcs {
@@ -1365,7 +1672,8 @@
my $dsn = "DBI:mysql:database=mysql";
my ($distro,$gotprereqs,$localecmd,$packagecmd,$updatecmd,$installnow,$mysqlrestart,
$recommended,$dbh,$has_pass,$has_lcdb,$downloadstatus,$filetouse,$production,
- $testing,$apachefw,$uses_systemctl,$hostname) = &check_required($instdir,$dsn);
+ $testing,$apachefw,$uses_systemctl,$hostname,$hostip,$sslhostsfiles,$has_std,
+ $has_int,$rewritenum,$nochgstd,$nochgint) = &check_required($instdir,$dsn);
if ($distro eq '') {
print "\n".&mt('Linux distribution could not be verified as a supported distribution.')."\n".
&mt('The following are supported: [_1].',
@@ -1543,19 +1851,35 @@
}
if ($callsub{'apachessl'}) {
+ my $targetdir = '/etc/httpd/conf.d';
if ($distro =~ /^(suse|sles)/) {
- ©_apache_sslconf_file($instdir,'/etc/apache2/vhosts.d',$hostname);
+ $targetdir = '/etc/apache2/vhosts.d';
} elsif ($distro =~ /^(debian|ubuntu)/) {
- my $apache2_sites_available_dir = '/etc/apache2/sites-available';
- if (©_apache_sslconf_file($instdir,$apache2_sites_available_dir,$hostname)) {
- my $apache2_sites_enabled_dir = '/etc/apache2/sites-enabled';
- my $made_symlink = eval { symlink("$apache2_sites_available_dir/loncapassl.conf","$apache2_sites_enabled_dir/loncapassl.conf"); 1 };
- if ($made_symlink) {
- print_and_log(&mt('Enabling "[_1]" Apache SSL configuration.','loncapassl.conf')."\n");
+ $targetdir = '/etc/apache2/sites-available';
+ }
+ my ($new_rewrite,$new_int) =
+ ©_apache_sslconf_files($distro,$hostname,$hostip,$instdir,$targetdir,$sslhostsfiles,
+ $has_std,$has_int,$rewritenum,$nochgstd,$nochgint);
+ if ($distro =~ /^(debian|ubuntu)/) {
+ my $apache2_sites_enabled_dir = '/etc/apache2/sites-enabled';
+ if (-d $apache2_sites_enabled_dir) {
+ if ($has_std ne '') {
+ unless ((-l "$apache2_sites_enabled_dir/$has_std") && (readlink(("$apache2_sites_enabled_dir/$has_std") eq "$targetdir/$has_std"))) {
+ my $made_symlink = eval { symlink("$targetdir/$has_std","$apache2_sites_enabled_dir/$has_std"); 1};
+ if ($made_symlink) {
+ print_and_log(&mt('Enabling "[_1]" Apache SSL configuration.',$has_std)."\n");
+ }
+ }
+ }
+ if (($has_int ne '') && ($has_int ne $has_std)) {
+ unless ((-l "$apache2_sites_enabled_dir/$has_int") && (readlink("$apache2_sites_enabled_dir/$has_int") eq "$targetdir/$has_int")) {
+ my $made_symlink = eval { symlink("$targetdir/$has_int","$apache2_sites_enabled_dir/$has_int"); 1 };
+ if ($made_symlink) {
+ print_and_log(&mt('Enabling "[_1]" Apache SSL configuration.',$has_int)."\n");
+ }
+ }
}
}
- } else {
- ©_apache_sslconf_file($instdir,'/etc/httpd/conf.d',$hostname);
}
print_and_log("\n");
} else {
@@ -2061,43 +2385,479 @@
###############################################
##
-## Copy/Modify loncapassl.conf
+## Copy loncapassl.conf and sslrewrite.conf
##
###############################################
-sub copy_apache_sslconf_file {
- my ($instdir,$targetdir,$hostname) = @_;
- my ($success,$error);
+#
+# The Apache SSL configuration used by LON-CAPA is contained in
+# two files: sslrewrite.conf and loncapassl.conf.
+#
+# Starting with LON-CAPA 2.12, name-based virtual hosts are used
+# with port 443. The default virtual host (i.e., the one listed
+# first) is for the server's standard hostname, and that is the one
+# which will respond to client browser requests for https:// pages.
+#
+# Accordingly, a system administrator will need to edit the config
+# config file to include paths to a signed SSL certificate (public),
+# chain (public) and key (private) pem files. The certificate should
+# have been signed by a recognized certificate authority ((e.g.,
+# InCommon or Let's Encrypt).
+#
+# The sslrewrite.conf file contains the rewrite configuration for
+# the default virtual host. The rewrite rules defined are used to
+# allow internal HEAD requests to /cgi-bin/mimetex.cgi to be served
+# http://, in order to support vertical alignment of mimetex images
+# (one of the options for rendering Math content); (b) allow requests
+# for certain URLs (external resource, and syllabus, if external URL
+# used) to be served http:// to accommodate the use of iframes which
+# would otherwise result in browser blocking of mixed active content.
+#
+# The loncapassl.conf file contains the configuration for the
+# "internal" virtual host, which will respond to requests for https://
+# pages from other LON-CAPA servers in the network to which the node
+# belongs. The ServerName is internal-<hostname> where <hostname>
+# is the server's hostname. There is no need to create a DNS entry
+# for internal-<hostname>, as LON-CAPA 2.12 automatically performs
+# the required hostname to IP mapping.
+#
+# Requests to /raw on the "internal" virtual host require a valid
+# SSL client certificate, signed by the certificate authority
+# for the LON-CAPA network to which the node belongs.
+#
+# The configuration file to which the contents of sslrewrite.conf
+# and loncapassl.conf will be written will have either been identified
+# when &chkapachessl() was run, or if no files were found with
+# existing rewrite blocks, then a candidate file will be chosen
+# from the .conf files containing VirtualHosts definitions.
+# If there is more than one suitable candidate file, the system
+# administrator will be prompted to select from the available files.
+#
+# If there are no files containing VirtualHosts definitions, then
+# <VirtualHost *:443> </VirtualHost> blocks will be appended to
+# the standard Apache SSL config for the particular distro:
+# ssl.conf for RHEL/CentOS/Scientific/Fedora, vhost-ssl.conf
+# for SuSE/SLES, and default-ssl.conf for Ubuntu.
+#
+# Once a file is selected, the contents of sslrewrite.conf and
+# loncapassl.conf are compared with appropriate blocks in the file
+# and the user will be prompted to agree to insertion of missing lines
+# and/or deletion of surplus lines.
+#
+
+sub copy_apache_sslconf_files {
+ my ($distro,$hostname,$hostip,$instdir,$targetdir,$targetfilesref,
+ $has_std,$has_int,$rewritenum,$nochgstd,$nochgint) = @_;
+ my ($new_std,$new_int);
+ my (@internal, at standard,%int_by_linenum,%int_by_linetext,
+ %rule_by_linenum,%rule_by_linetext,%foundint);
if (-e "$instdir/loncapassl.conf") {
if (open(my $fh,'<',"$instdir/loncapassl.conf")) {
- if (open(my $out,'>',"$targetdir/loncapassl.conf")) {
- while (<$fh>) {
- if (/^\QServerName internal-\E/) {
- chomp();
- s/^(\QServerName internal-\E)(.*)$/$1$hostname\n/;
+ my $num = 1;
+ while (<$fh>) {
+ chomp();
+ if (/^ServerName/) {
+ s/(\Qinternal-{[[[[Hostname]]]]}\E)/internal-$hostname/;
+ }
+ push(@internal,$_);
+ $int_by_linenum{$num} = $_;
+ s/(^\s+|\s+$)//g;
+ push(@{$int_by_linetext{$_}},$num);
+ $num ++;
+ }
+ close($fh);
+ }
+ }
+ if (-e "$instdir/sslrewrite.conf") {
+ if (open(my $fh,'<',"$instdir/sslrewrite.conf")) {
+ my $num = 1;
+ while (<$fh>) {
+ chomp();
+ if (/\Q{[[[[HostIP]]]]}\E/) {
+ s/(\QRewriteCond %{REMOTE_ADDR} {[[[[HostIP]]]]}\E)/RewriteCond %{REMOTE_ADDR} $hostip/;
+ }
+ push(@standard,$_);
+ $rule_by_linenum{$num} = $_;
+ s/(^\s+|\s+$)//g;
+ push(@{$rule_by_linetext{$_}},$num);
+ $num ++;
+ }
+ close($fh);
+ }
+ }
+ if (!$nochgstd) {
+ if ($has_std eq '') {
+ my $file;
+ if ($has_int ne '') {
+ if (open(my $fh,'<',"$targetdir/$has_int")) {
+ my @saved = <$fh>;
+ close($fh);
+ if (open(my $fhout, '>',"$targetdir/$has_int")) {
+ print $fhout "<VirtualHost *:443>\n".
+ "ServerName $hostname\n".
+ join("\n", at standard)."\n".
+ "</VirtualHost>\n\n".
+ join('', at saved);
+ close($fhout);
+ $new_int = $has_int;
+ }
+ }
+ }
+ } else {
+ if ($rewritenum eq '') {
+ &append_to_vhost($targetdir,$has_std,$hostname,\%rule_by_linenum,'std');
+ $new_std = $has_std;
+ } else {
+ $new_std = &modify_ssl_config($targetdir,$has_std,$hostname,$rewritenum,
+ \%rule_by_linetext,\%rule_by_linenum,'std');
+ }
+ }
+ }
+ if (!$nochgint) {
+ if ($has_int eq '') {
+ if ($has_std ne '') {
+ if (open(my $fhout,'>>',"$targetdir/$has_std")) {
+ print $fhout "\n".join("\n", at internal)."\n";
+ close($fhout);
+ $new_int = $has_std;
+ }
+ }
+ } else {
+ $new_int = &modify_ssl_config($targetdir,$has_int,$hostname,$rewritenum,\%int_by_linetext,\%int_by_linenum,'int');
+ }
+ }
+ if (($has_std eq '') && ($has_int eq '')) {
+ my ($file,$numfiles) = &get_sslconf_filename($distro,$targetdir,$targetfilesref);
+ if ($numfiles == 0) {
+ if (open(my $fhout, '>>', "$targetdir/$file")) {
+ print $fhout "<VirtualHost *:443>\n".
+ "ServerName $hostname\n".
+ join("\n", at standard)."\n".
+ "</VirtualHost>\n\n".
+ join("\n", at internal)."\n";
+ close($fhout);
+ $new_std = $file;
+ $new_int = $file;
+ }
+ } elsif ($numfiles == 1) {
+ &append_to_vhost($targetdir,$file,$hostname,\%rule_by_linenum,'std');
+ if (open(my $fhout, '>>', "$targetdir/$file")) {
+ print $fhout "\n".join("\n", at internal)."\n";
+ close($fhout);
+ $new_std = $file;
+ $new_int = $file;
+ }
+ } elsif ($numfiles == -1) {
+ print_and_log(&mt('Failed to copy contents of [_1] or [_2] to a file in [_3]',
+ "'loncapassl.conf'","'sslrewrite.conf'","'$targetdir'")."\n");
+ }
+ }
+ if ($nochgstd) {
+ print_and_log(&mt('No change required to file: [_1] in [_2], (no difference between [_3] and rewrite block.)',
+ "'$has_std'","'$targetdir'","'sslrewrite.conf'"));
+ }
+ if ($nochgint) {
+ print_and_log(&mt('No change required to file: [_1] in [_2], (no difference between [_3] and virtualhost block.)',
+ "'$has_int'","'$targetdir'","'loncapassl.conf'"));
+ }
+ if ($new_int) {
+ print_and_log(&mt('Successfully copied contents of [_1] to [_2].',"'loncapassl.conf'","'$targetdir/$new_int'")."\n");
+ chmod(0444,"$targetdir/loncapassl.conf");
+ }
+ if ($new_std) {
+ print_and_log(&mt('Successfully copied contents of [_1] to [_2].',"'sslrewrite.conf'","'$targetdir/$new_std'")."\n");
+ chmod(0444,"$targetdir/loncapassl.conf");
+ }
+ return ($new_int,$new_std);
+}
+
+#
+# append_to_vhost() is called to add rewrite rules (in a
+# <IfModule +mod_rewrite.c> </IfModule> block), provided
+# in the sslrewrite.conf configuration file, to an Apache
+# SSL configuration file within a VirtualHost for port 443
+# (for server's public-facing hostname).
+#
+sub append_to_vhost {
+ my ($targetdir,$filename,$hostname,$by_linenum,$type) = @_;
+ return unless (ref($by_linenum) eq 'HASH');
+ my ($startvhost,$endvhost);
+ if (-e "$targetdir/$filename") {
+ my (@lines,$currname,$virtualhost,$hasname);
+ if (open(my $fh,'<',"$targetdir/$filename")) {
+ my $currline = 0;
+ while (<$fh>) {
+ $currline ++;
+ push(@lines,$_);
+ chomp();
+ s/(^\s+|\s+$)//g;
+ if (/^<VirtualHost\s+[^:]*\:443>/) {
+ $virtualhost = 1;
+ unless ($endvhost) {
+ $startvhost = $currline;
}
- print $out $_;
}
- $success = 1;
+ if ($virtualhost) {
+ if (/^ServerName\s+([^\s]+)\s*$/) {
+ $currname = $1;
+ unless ($endvhost) {
+ if ((($currname eq '') || ($currname eq $hostname)) && ($type eq 'std')) {
+ $hasname = 1;
+ }
+ }
+ }
+ if (/^<\/VirtualHost>/) {
+ $virtualhost = 0;
+ unless ($endvhost) {
+ if (((($currname eq '') || ($currname eq $hostname)) && ($type eq 'std')) ||
+ (($currname eq 'internal-'.$hostname) && ($type eq 'int'))) {
+ $endvhost = $currline;
+ } else {
+ undef($startvhost);
+ }
+ }
+ }
+ }
+ }
+ close($fh);
+ }
+ if ($endvhost) {
+ if (open(my $fout,'>',"$targetdir/$filename")) {
+ for (my $i=0; $i<@lines; $i++) {
+ if ($i == $startvhost) {
+ unless (($hasname) && ($type eq 'std')) {
+ print $fout "ServerName $hostname\n";
+ }
+ }
+ if ($i == $endvhost-1) {
+ foreach my $item (sort { $a <=> $b } keys(%{$by_linenum})) {
+ print $fout $by_linenum->{$item}."\n";
+ }
+ }
+ print $fout $lines[$i];
+ }
+ close($fout);
+ }
+ }
+ }
+ return $endvhost;
+}
+
+#
+# get_sslconf_filename() is called when the Apache SSL configuration
+# option has been selected and there are no files containing
+# VirtualHost definitions containing rewrite blocks,
+#
+# In this case get_sslconf_filename() is used to chose from the
+# available .conf files containing VirtualHosts definitions. If
+# there is ambiguity about which file to use, &apacheconf_choice()
+# will be called to prompt the user to choose one of the possible
+# files.
+#
+
+sub get_sslconf_filename {
+ my ($distro,$targetdir,$targetfilesref) = @_;
+ my ($configfile,$numfiles, at possfiles);
+ if (ref($targetfilesref) eq 'HASH') {
+ if (keys(%{$targetfilesref}) > 0) {
+ foreach my $name (sort(keys(%{$targetfilesref}))) {
+ if (ref($targetfilesref->{$name}) eq 'HASH') {
+ foreach my $file (sort(keys(%{$targetfilesref->{$name}}))) {
+ next if ($file eq '');
+ next if (!-e "$targetdir/$file");
+ unless (grep(/^\Q$file\E$/, at possfiles)) {
+ push(@possfiles,$file);
+ }
+ }
+ }
+ }
+ }
+ if (@possfiles == 0) {
+ $configfile = 'ssl.conf';
+ if ($distro =~ /^(suse|sles)/) {
+ $configfile = 'vhost-ssl.conf';
+ } elsif ($distro =~ /^(debian|ubuntu)/) {
+ $configfile = 'default-ssl.conf';
+ }
+ $numfiles = 0;
+ print &mt('No configuration files in [_1] contain a <VirtualHost *:443> </VirtualHost> block which can be used to house Apache rewrite rules from https to http.',$targetdir)."\n\n".
+ &mt('Accordingly, the contents of sslrewrite.conf will be included in a <VirtualHost *:443> </VirtualHost> block which will be added to a file named: [_1].',$configfile)."\n\n";
+ } elsif (@possfiles == 1) {
+ $configfile = $possfiles[0];
+ $numfiles = 1;
+ print &mt('A single configuration file in [_1] contains a <VirtualHost *:443> </VirtualHost> block.',$targetdir)."\n".
+ &mt('The contents of sslrewrite.conf will be added to this block.')."\n\n";
+ } else {
+ print &mt('More than one Apache config file contains a <VirtualHost *:443> </VirtualHost> block.')."\n\n".&mt('The possible files are:')."\n";
+ my $counter = 1;
+ my $max = scalar(@possfiles);
+ foreach my $file (@possfiles) {
+ print "$counter. $file\n";
+ $counter ++;
+ }
+ print "\n".&mt('Enter a number between 1 and [_1] to indicate which file should be modified to include the contents of sslrewrite.conf.',$max)."\n";
+ my $choice = &apacheconf_choice($max);
+ if (($choice =~ /^\d+$/) && ($choice >= 1) && ($choice <= $max)) {
+ $configfile = $possfiles[$choice-1];
+ $numfiles = 1;
} else {
- $error = "Could not write to $targetdir/loncapassl.conf";
+ $numfiles = -1;
}
- } else {
- $error = "Could not read from $instdir/loncapassl.conf";
}
- } else {
- $error = "File to copy from: $instdir/loncapassl.conf does not exist";
}
- if ($success) {
- print_and_log(&mt('Successfully copied [_1] to [_2].',"'loncapassl.conf'","'$targetdir/loncapassl.conf'")."\n");
- chmod(0444,"$targetdir/loncapassl.conf");
- } else {
- print_and_log(&mt('Failed to copy [_1] to [_2].',"'loncapassl.conf'","'$targetdir/loncapassl.conf'")."\n");
- if ($error) {
- print_and_log("$error\n");
+ return ($configfile,$numfiles);
+}
+
+#
+# &apacheconf_choice() prompts a user to choose an integer between 1 and the
+# maximum number of available of possible Apache SSL config files found
+# at the distros standard location for Apache config files containing
+# VirtualHost definitions.
+#
+# This routine is called recursively until the user enters a valid integer.
+#
+
+sub apacheconf_choice {
+ my ($max) = @_;
+ my $choice = <STDIN>;
+ chomp($choice);
+ $choice =~ s/(^\s+|\s+$)//g;
+ my $configfile;
+ if (($choice =~ /^\d+$/) && ($choice >= 1) && ($choice <= $max)) {
+ $configfile = $choice;
+ }
+ while ($configfile eq '') {
+ print &mt('Invalid choice. Please enter a number between 1 and [_1].',$max)."\n";
+ $configfile = &apacheconf_choice($max);
+ }
+ print "\n";
+ return $configfile;
+}
+
+#
+# &modify_ssl_config() is called to modify the contents of an Apache SSL config
+# file so that it has two <VirtualHost *:443> </VirtualHost> blocks containing
+# (a) the default VirtualHost with the <IfModule mod_rewrite.c> </IfModule> block
+# provided in sslrewrites.conf, and (b) an "internal" VirtualHost with the
+# content provided in loncapassl.conf.
+#
+# This routine will prompted you to agree to insertion of lines present in the
+# shipped conf file, but missing from the local config file, and also for
+# deletion of lines present in the local config file, but not required in
+# the shipped conf file.
+#
+
+sub modify_ssl_config {
+ my ($targetdir,$filename,$hostname,$rewritenum,$by_linetext,$by_linenum,$type) = @_;
+ return unless ((ref($by_linetext) eq 'HASH') && (ref($by_linenum) eq 'HASH'));
+ if (-e "$targetdir/$filename") {
+ my (@lines,$virtualhost,$currname,$rewrite);
+ if (open(my $fh,'<',"$targetdir/$filename")) {
+ my %found;
+ my %possible;
+ my $currline = 0;
+ my $rewritecount = 0;
+ while (<$fh>) {
+ $currline ++;
+ push(@lines,$_);
+ chomp();
+ s/(^\s+|\s+$)//g;
+ if (/^\s*<VirtualHost\s+[^:]*\:443>\s*$/) {
+ $virtualhost = 1;
+ }
+ if ($virtualhost) {
+ if ((exists($by_linetext->{$_})) && (ref($by_linetext->{$_}) eq 'ARRAY') &&
+ (@{$by_linetext->{$_}} > 0)) {
+ $possible{$currline} = shift(@{$by_linetext->{$_}});
+ }
+ if (/^\s*<\/VirtualHost>/) {
+ if ((($currname eq 'internal-'.$hostname) && ($type eq 'int')) ||
+ ((($currname eq $hostname) || ($currname eq '')) && ($type eq 'std') &&
+ ($rewritecount == $rewritenum))) {
+ %found = (%found,%possible);
+ } else {
+ foreach my $line (sort {$b <=> $a } keys(%possible)) {
+ my $num = $possible{$line};
+ if (ref($by_linetext->{$by_linenum->{$num}}) eq 'ARRAY') {
+ unshift(@{$by_linetext->{$by_linenum->{$num}}},$num);
+ }
+ }
+ }
+ undef(%possible);
+ $virtualhost = 0;
+ $currname = '';
+ } elsif (/^\s*ServerName\s+([^\s]+)\s*$/) {
+ $currname = $1;
+ } elsif (/^\s*<IfModule\s+mod_rewrite\.c>/) {
+ $rewrite = 1;
+ } elsif (/^\s*<\/IfModule>/) {
+ $rewritecount ++;
+ $rewrite = 0;
+ }
+ }
+ }
+ close($fh);
+ if (open(my $fout,'>',"$targetdir/$filename")) {
+ my $currline = 0;
+ my ($lastfound,$done);
+ my $numfound = 0;
+ foreach my $line (@lines) {
+ $currline ++;
+ if ($done) {
+ print $fout $line;
+ } elsif ($lastfound) {
+ if ($found{$currline}) {
+ for (my $i=$lastfound+1; $i<$found{$currline}; $i++) {
+ print &mt('The following line is missing from the current <VirtualHost *:443> </VirtualHost> block:')."\n".
+ $by_linenum->{$i}."\n".
+ &mt('Add this line? ~[Y/n~]');
+ if (&get_user_selection(1)) {
+ print $fout $by_linenum->{$i}."\n";
+ }
+ }
+ $numfound ++;
+ $lastfound = $found{$currline};
+ print $fout $line;
+ if ($numfound == scalar(keys(%found))) {
+ $done = 1;
+ for (my $i=$found{$currline}+1; $i<=scalar(keys(%{$by_linenum})); $i++) {
+ print &mt('The following line is missing from the current <VirtualHost *:443> </VirtualHost> block:')."\n".
+ $by_linenum->{$i}."\n".
+ &mt('Add this line? ~[Y/n~]');
+ if (&get_user_selection(1)) {
+ print $fout $by_linenum->{$i}."\n";
+ }
+ }
+ }
+ } else {
+ print &mt('The following line found within a <VirtualHost *:443> </VirtualHost> block does not match that expected by LON-CAPA:')."\n".
+ $line.
+ &mt('Delete this line? ~[Y/n~]');
+ if (!&get_user_selection(1)) {
+ print $fout $line;
+ }
+ }
+ } elsif ($found{$currline}) {
+ $numfound ++;
+ $lastfound = $found{$currline};
+ for (my $i=1; $i<$found{$currline}; $i++) {
+ print &mt('The following line is missing from the current <VirtualHost *:443> </VirtualHost> block:')."\n".
+ $by_linenum->{$i}."\n".
+ &mt('Add this line? ~[Y/n~]');
+ if (&get_user_selection(1)) {
+ print $fout $by_linenum->{$i}."\n";
+ }
+ }
+ print $fout $line;
+ } else {
+ print $fout $line;
+ }
+ }
+ close($fout);
+ }
}
}
- return $success;
+ return $filename;
}
#########################################################
More information about the LON-CAPA-cvs
mailing list