#!/usr/bin/perl use strict; use warnings; use Config::General; use Data::Dumper; use Text::Xslate qw(mark_raw); use File::Slurp; use File::Copy; open STDERR, '>', "/dev/null"; my $LCWM = '/etc/nginx/lcwm2'; my $A2CFG = '/etc/httpd/conf/httpd.conf' ; my $NGCFG = '/etc/nginx/nginx.conf' ; my $NGCFGen = "$NGCFG.gen" ; my $NGCFGbak = "$NGCFG." . time() ; my $A2PORT = 81 ; my $A2PORTS = 8443 ; my $NGPORT = 80 ; my $NGPORTS = 443 ; my $NGCFGBASE = "$LCWM/nginx.base" ; my $NGCFGVHOSTSTAT = "$LCWM/vhost_static.conf" ; my $NGCFG_CUSTOM_DIR = "$LCWM/custom"; my $A2CLEAN = "$LCWM/ap2_clean.conf" ; my $DEFLISTEN = " default sndbuf=98304 backlog=2048 deferred" ; my $template_def_ips = qq{ # aka null.tld + $DEFLISTEN server { : for \$ipv4 -> \$ip { listen <: \$ip :>:$NGPORT $DEFLISTEN; : } : for \$ipv6 -> \$ip { listen <: \$ip :>:$NGPORT $DEFLISTEN; : } access_log off; error_log /dev/null; server_name ""; return 444; } }; my $template_ng_vhost = q{ #---vhost for domain <: $server_name :> on IP <: $listen :> <: $ssl :> ---- server { : for $ips.ipv4 -> $ip { listen <: $ip :>:<: $ngport :> <: $ssl :>; : } : for $ips.ipv6 -> $ip { listen <: $ip :>:<: $ngport :> <: $ssl :>; : } server_name <: $server_name :> <: $alias :> ; root <: $root :> ; access_log off; # /var/log/nginx/<: $server_name :>.access.log main buffer=32k; error_log /var/log/nginx/<: $server_name :>.error.log warn; : if $include_slash == "" { location @apache { proxy_pass http://127.0.0.1:<: $a2port :>; proxy_redirect http://<: $server_name :>:<: $a2port :> http://<: $server_name :>; proxy_redirect http://www.<: $server_name :>:<: $a2port :> http://www.<: $server_name :>; proxy_redirect http://webmail.<: $server_name :>:<: $a2port :> http://webmail.<: $server_name :>; proxy_redirect http://admin.<: $server_name :>:<: $a2port :> http://admin.<: $server_name :>; } location / { try_files maintenance.html @apache; } : } else { <: $include_slash :> : } <: $include_static :> : if $ssl != "" { ssl_certificate <: $ssl_certificate :> ; ssl_certificate_key <: $ssl_certificate_key :> ; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; ssl_dhparam /etc/ssl/certs/dhparam.pem; ssl_protocols TLSv1.2; ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; ssl_prefer_server_ciphers on; #ssl_stapling on; #ssl_stapling_verify on; resolver 8.8.8.8; : } } }; sub update_pem { # Bash analog: # cat $SSLSERT > $SSLSERT".pem" # cat $SSLCA >> $SSLSERT".pem" # my ($SSLSERT, $SSLCA) = @_ ; my $OUT = $SSLSERT . ".pem"; write_file( $OUT, {append => 0 }, read_file($SSLSERT) ) ; write_file( $OUT, {append => 1 }, read_file($SSLCA) ) if ( $SSLCA ); return $OUT; } sub uniq { # remove duplicates from array # my %seen; return grep { !$seen{$_}++ } @_; } sub get_ips { # get array of ips from VirtualHost string, like this "IP1:http IP2:http [ipv6_1]:http [ipv6_2]:http" # my ($text) = @_ ; my %hash; my @ipv6 = uniq ( $text =~ m/\[.+?\]/sg ) ; my @ipv6_wo_local = grep ! /\[fe80/, @ipv6 ; @ipv6_wo_local = grep ! /\[2001/, @ipv6_wo_local ; my @ipv4 = uniq ( $text =~ m/[0-9.]{7,}/sg ) ; $hash{ipv6} = \@ipv6_wo_local ; $hash{ipv4} = \@ipv4 ; return \%hash ; } sub cook_ng_vhost { # convert [httpd.conf->Config::General]=>@vh_array->{$vhost} to %ng_hash=>[Text::Xslate->nginx.conf] # my ($vh, $ip_port) = @_ ; my $port = $vh->{SSLEngine} ? $NGPORTS : $NGPORT ; my %hash; my $server_name = $vh->{ServerName}; my @statics = glob "/home/*/configs/$server_name.static /etc/nginx/*/custom/$server_name.static"; my @slashes = glob "/home/*/configs/$server_name.slash /etc/nginx/*/custom/$server_name.slash"; my $include_static = "/fake_path"; my $include_slash = "/fake_path"; $include_static = $statics[0] if $statics[0] ; $include_slash = $slashes[0] if $slashes[0] ; #print $server_name . " " . $include_static . " " . $include_slash . "\n"; $hash{'alias'} = ref $vh->{ServerAlias} eq 'ARRAY' ? join(" ", @{$vh->{ServerAlias}}) : $vh->{ServerAlias} ; $hash{'root'} = $vh->{DocumentRoot} ; $hash{'server_name'} = $vh->{ServerName} ; $hash{'main_ip'} = get_ips($ip_port)->{ipv4}[0] ; $hash{'a2port'} = $A2PORT ; $hash{'ips'} = get_ips($ip_port) ; $hash{'ngport'} = $vh->{SSLEngine} ? $NGPORTS : $NGPORT ; $hash{'ssl'} = $vh->{SSLEngine} ? "ssl" : ""; $hash{'ssl_certificate'} = $vh->{SSLEngine} ? update_pem( $vh->{SSLCertificateFile}, $vh->{SSLCACertificateFile} ) : "" ; $hash{'ssl_certificate_key'} = $vh->{SSLCertificateKeyFile} ; $hash{'include_static'} = (-f $include_static ) ? read_file( $include_static ) : "include $NGCFGVHOSTSTAT;" ; $hash{'include_slash'} = (-f $include_slash ) ? read_file( $include_slash ) : "" ; $hash{'include_static'} = mark_raw( $hash{'include_static'} ) ; $hash{'include_slash'} = mark_raw( $hash{'include_slash'} ) ; return \%hash; } sub cook_ng_array { open(my $a2cfg, '<:encoding(utf8)', $A2CFG ) or die "unable to open $A2CFG: $!\n"; open(my $a2clean, "+>", $A2CLEAN) or die "$0: can't create temporary file: $!\n"; while (<$a2cfg>) { print $a2clean "$_" if ( /^\/ ) } ; close $a2cfg; close $a2clean; my %conf = Config::General->new($A2CLEAN)->getall(); unlink $A2CLEAN; my @ng_array = (); for my $ip_port (keys %{$conf{VirtualHost}}) { if ( ref $conf{VirtualHost}{$ip_port} eq 'ARRAY' ) { for my $vh ( @{$conf{VirtualHost}{$ip_port}} ) { if (exists $vh->{ServerName} and exists $vh->{ServerAlias}) { push @ng_array, cook_ng_vhost($vh, $ip_port); } } } else { my $vh = $conf{VirtualHost}{$ip_port} ; if (exists $vh->{ServerName} and exists $vh->{ServerAlias}) { push @ng_array, cook_ng_vhost( $vh, $ip_port); } } } return \@ng_array; } sub render_ng { my $tx = Text::Xslate->new(); write_file($NGCFGen, {append => 0 }, read_file( $NGCFGBASE ) ); #print Dumper @ng_array; ## TO DO, rm durty code my $get_string_with_all_ips = `grep VirtualHost $A2CFG`; write_file( $NGCFGen, {append => 1 }, $tx->render_string( $template_def_ips, get_ips ( $get_string_with_all_ips ) ) ); ## /TO DO foreach my $vhost (values cook_ng_array){ write_file( $NGCFGen, {append => 1 }, $tx->render_string( $template_ng_vhost, $vhost ) ); } write_file( $NGCFGen, {append => 1 }, "\n\#close section http {\n}" ) ; print "Config done ... " ; } sub reconf_ng { my @args = ("/usr/sbin/nginx", "-t", "-c", "$NGCFGen" ); # Debug copy($NGCFGen, '/etc/nginx/test.conf'); # ----- if ( system(@args) == 0 ) { print "syntax is ok ... "; } else { print "syntax check failed ...\n"; die "system @args failed: $?"; } move($NGCFG, $NGCFGbak); copy($NGCFGen, $NGCFG); print "backup done ... "; #@args = ("/usr/sbin/nginx", "-s", "reload" ); @args = ("service", "nginx", "restart" ); if ( system(@args) == 0 or system(@args) == 256) { print "nginx reloaded. ALL OK\n"; } else { print system(@args); copy($NGCFGbak, $NGCFG); print "nginx reload failed, revert config done ...\n"; die "system @args failed: $?"; } } render_ng ; reconf_ng ;