http://www.perlmonks.org?node_id=1216823


in reply to "This site is not secure" warning message

I analyzed this issue a bit more.

There are 10 DNS names that I know of for PerlMonks: /^( (www\.|css\.)? perlmonks \. (org|com|net) | perlmonks.pairsite.com )$/x

Except for perlmonks.pairsite.com, all of those resolve to the three IP addresses 209.197.123.153, 216.92.34.251, and 66.39.54.27. perlmonks.pairsite.com resolves to only the first of those, 209.197.123.153.

There are two SSL certificates:

  1. one for pair Networks, which matches only *.pairsite.com - this one is always served by 209.197.123.153, no matter which of the aforementioned 10 DNS names is used!
  2. one Let's Encrypt certificate, which matches any of the nine DNS names /^ (www\.|css\.)? perlmonks \. (org|com|net) $/x - this one is served by 216.92.34.251 and 66.39.54.27.

The issue is that any time one of the (org|com|net) addresses resolves to 209.197.123.153 (Round-robin DNS), the user will get a certificate warning.

I checked, and the Apache Wiki says this about Name-Based Virtual Hosts and SSL (emphasis mine):

As a rule, it is impossible to host more than one SSL virtual host on the same IP address and port. This is because Apache needs to know the name of the host in order to choose the correct certificate to setup the encryption layer. But the name of the host being requested is contained only in the HTTP request headers, which are part of the encrypted content. It is therefore not available until after the encryption is already negotiated. This means that the correct certificate cannot be selected, and clients will receive certificate mismatch warnings and be vulnerable to man-in-the-middle attacks.

In reality, Apache will allow you to configure name-based SSL virtual hosts, but it will always use the configuration from the first-listed virtual host (on the selected IP address and port) to setup the encryption layer. In reality, Apache will allow you to configure name-based SSL virtual hosts, but it will always use the configuration from the first-listed virtual host (on the selected IP address and port) to setup the encryption layer. In certain specific circumstances, it is acceptable to use a single SSL configuration for several virtual hosts. In particular, this will work if the SSL certificate applies to all the virtual hosts. ...

As a quick fix, 209.197.123.153 could be taken out of the DNS rotation, as already suggested by others, but this has the disadvantage that one of the servers will get much less of the load. Another possible solution would be to make sure that 209.197.123.153 serves up the Let's Encrypt certificate as well, and then set up a redirect from perlmonks.pairsite.com to perlmonks.org (both http and https), since I'm not sure how much that address is used anyway.

In any case, I think this is an important issue!

Update: Corion has since published this code on GitHub, please consider that the most current version: https://github.com/Corion/Perlmonks-Maintenance/blob/master/scripts/perlmonks-server-check.pl

#!/usr/bin/env perl use warnings; use strict; use Data::Dump; use Net::DNS; use LWP::UserAgent; require LWP::Protocol::https; # make sure this is installed use Class::Method::Modifiers qw/around/; my @ADDRS = qw/ perlmonks.org www.perlmonks.org css.perlmonks.org perlmonks.com www.perlmonks.com css.perlmonks.com perlmonks.net www.perlmonks.net css.perlmonks.net perlmonks.pairsite.com /; my $DNS = { # As of 2018-06-17: "css.perlmonks.com" => ["209.197.123.153", "216.92.34.251", "66 +.39.54.27"], "css.perlmonks.net" => ["209.197.123.153", "216.92.34.251", "66 +.39.54.27"], "css.perlmonks.org" => ["209.197.123.153", "216.92.34.251", "66 +.39.54.27"], "perlmonks.com" => ["209.197.123.153", "216.92.34.251", "66 +.39.54.27"], "perlmonks.net" => ["209.197.123.153", "216.92.34.251", "66 +.39.54.27"], "perlmonks.org" => ["209.197.123.153", "216.92.34.251", "66 +.39.54.27"], "perlmonks.pairsite.com" => ["209.197.123.153"], "www.perlmonks.com" => ["209.197.123.153", "216.92.34.251", "66 +.39.54.27"], "www.perlmonks.net" => ["209.197.123.153", "216.92.34.251", "66 +.39.54.27"], "www.perlmonks.org" => ["209.197.123.153", "216.92.34.251", "66 +.39.54.27"], }; if (0) { $DNS={}; my $resolver = new Net::DNS::Resolver(recurse => 1, debug => 0); for my $addr (@ADDRS) { # figure out the authoritative server $resolver->nameservers('8.8.8.8'); my $packet = $resolver->send($addr, 'SOA'); my @server = map {$_->mname} grep {$_->type eq 'SOA'} $packet->answer; die "@server" unless @server==1; $packet = $resolver->send($server[0], 'A'); my @nameservers = map {$_->address} grep {$_->type eq 'A'} $packet->answer; die "@server" unless @nameservers; # query the authoritative server $resolver->nameservers(@nameservers); $packet = $resolver->send($addr, 'A'); my @ips = sort map {$_->address} grep {$_->type eq 'A'} $packet->answer; printf "%23s %-35s %s\n", $addr, "(\@$server[0]/@nameservers)", join ' ', @ips; $DNS->{$addr} = \@ips; } dd $DNS; } our $force_peeraddr; around 'LWP::Protocol::http::_extra_sock_opts' => sub { my $orig = shift; die unless wantarray; my @rv = $orig->(@_); push @rv, PeerAddr => $force_peeraddr if defined $force_peeraddr; return @rv; }; around 'LWP::Protocol::https::_get_sock_info' => sub { my $orig = shift; my ($self, $res, $sock) = @_; my $cert = $sock->get_peer_certificate; my @san = $cert->peer_certificate('subjectAltNames'); while (@san) { my ($type_id, $value) = splice @san, 0, 2; $res->push_header("Client-SSL-Cert-SubjectAltName" => "$type_id: $value"); } $orig->(@_); }; my %certs; my $ua = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0 } ); for my $addr (sort keys %$DNS) { for my $host (sort @{ $DNS->{$addr} }) { warn "Requesting $addr from $host...\n"; local $force_peeraddr = $host; my $res = $ua->get("https://$addr"); die $res->status_line unless $res->is_success; my @peer = $res->header("client-peer"); die "@peer" unless @peer==1 && $peer[0] eq "$host:443"; my @issuer = $res->header("client-ssl-cert-issuer"); my @subject = $res->header("client-ssl-cert-subject"); my @san = $res->header("client-ssl-cert-subjectaltname"); my $certstr = "Issuer: @issuer\nSubject: @subject\n" ."Subject Alt Names: @san\n"; push @{ $certs{$certstr} }, "$host $addr"; } } for my $cert (sort keys %certs) { print "##### Certificate #####\n", $cert, "### Served by:\n"; printf "%15s %s\n", @$_ for map {[split]} @{ $certs{$cert} }; } __END__ # As of 2018-06-17: ##### Certificate ##### Issuer: /C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 Subject: /CN=perlmonks.org Subject Alt Names: 2: css.perlmonks.com 2: css.perlmonks.net 2: css.pe +rlmonks.org 2: perlmonks.com 2: perlmonks.net 2: perlmonks.org 2: www +.perlmonks.com 2: www.perlmonks.net 2: www.perlmonks.org ### Served by: 216.92.34.251 css.perlmonks.com 66.39.54.27 css.perlmonks.com 216.92.34.251 css.perlmonks.net 66.39.54.27 css.perlmonks.net 216.92.34.251 css.perlmonks.org 66.39.54.27 css.perlmonks.org 216.92.34.251 perlmonks.com 66.39.54.27 perlmonks.com 216.92.34.251 perlmonks.net 66.39.54.27 perlmonks.net 216.92.34.251 perlmonks.org 66.39.54.27 perlmonks.org 216.92.34.251 www.perlmonks.com 66.39.54.27 www.perlmonks.com 216.92.34.251 www.perlmonks.net 66.39.54.27 www.perlmonks.net 216.92.34.251 www.perlmonks.org 66.39.54.27 www.perlmonks.org ##### Certificate ##### Issuer: /C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=U +SERTrust RSA Organization Validation Secure Server CA Subject: /C=US/postalCode=15203/ST=Pennsylvania/L=Pittsburgh/street=Su +ite 510/street=2403 Sidney Street/O=pair Networks, Inc./OU=Provided b +y pair Networks/OU=PairWildcardSSL $250,000/CN=*.pairsite.com Subject Alt Names: 2: *.pairsite.com 2: pairsite.com ### Served by: 209.197.123.153 css.perlmonks.com 209.197.123.153 css.perlmonks.net 209.197.123.153 css.perlmonks.org 209.197.123.153 perlmonks.com 209.197.123.153 perlmonks.net 209.197.123.153 perlmonks.org 209.197.123.153 perlmonks.pairsite.com 209.197.123.153 www.perlmonks.com 209.197.123.153 www.perlmonks.net 209.197.123.153 www.perlmonks.org