aeris / cryptcheck

Verify some SSL/TLS website or XMPP implementation
GNU Affero General Public License v3.0
187 stars 19 forks source link

Check TLS certificates with IPv6 addresses #52

Open HLFH opened 4 years ago

HLFH commented 4 years ago

As acme-ip is not yet implemented into boulder, I generate TLS certificates for IP addresses with minica that facilitates issuance of self-signed certificates.

So I do:

docker run my_image_id https my_ipv6_address

And I get:

Supported methods Address not available - connect(2) for [my_ipv6_address]:443

Do you plan to support the TLS certificates with IPv6 addresses check in the future? I see it works for IPv4 addresses like 1.1.1.1: https://cryptcheck.fr/https/1.1.1.1

Thanks, HLFH

aeris commented 4 years ago

It already support IPv6:

$ docker run --rm aeris22/cryptcheck https 2001:bc8:1200:4:208:a2ff:fe0c:67ea   
2001:bc8:1200:4:208:a2ff:fe0c:67ea:443

Supported methods
  Method TLSv1_2

Supported ciphers
  Cipher TLSv1_2 ECDHE-ECDSA-AES128-GCM-SHA256 [aead]
    PFS : ECC 256 bits
  Cipher TLSv1_2 ECDHE-ECDSA-AES256-GCM-SHA384 [aead]
    PFS : ECC 256 bits
  Cipher TLSv1_2 ECDHE-ECDSA-CHACHA20-POLY1305 [aead]
    PFS : ECC 256 bits
  Cipher TLSv1_2 ECDHE-ECDSA-AES128-SHA256 []
    PFS : ECC 256 bits
  Cipher TLSv1_2 ECDHE-ECDSA-AES256-SHA384 []
    PFS : ECC 256 bits

But you need to have working IPv6 stack on the host performing check and to activate IPv6 on docker daemon:

# cat /etc/docker/daemon.json 
{
  "ipv6": true,
  "fixed-cidr-v6": "xxxx:xxxx:xxxx::/80"
}

Currently web frontend is not "IPv6 ready" because of confusion about :, guessed as port separator, on naked IPv6. But fully dual-stacked hostname is checked for both IPv4 & IPv6, as on https://cryptcheck.fr/https/imirhil.fr

HLFH commented 4 years ago

@aeris

As stated in the Docker documentation:

IPv6 networking is only supported on Docker daemons running on Linux hosts. Docker for Mac does not support IPv6 networking.

So I migrated Docker from Mac platform to ArchLinux and I followed your advice for /etc/docker/daemon.json:

{
  "ipv6": true,
  "fixed-cidr-v6": "xxxx:xxxx:xxxx:xxxx::/56"
}

And it worked in CLI.

Regarding the web frontend, I would presume the confusion can be fixed with the fact that RFC 3986, section 3.2.2 clarifies that:

A host identified by an Internet Protocol literal address, version 6 [RFC3513] or later, is distinguished by enclosing the IP literal within square brackets ("[" and "]"). This is the only place where square bracket characters are allowed in the URI syntax. In anticipation of future, as-yet-undefined IP literal address formats, an implementation may use an optional version flag to indicate such a format explicitly rather than rely on heuristic determination.

The Cloudflare DNS service is accessible in IPv6 on:

If we use in CLI the https command to check TLS certificates with IPv6 addresses, the HSTS check should not be done as RFC 6797 Appendix A excludes IP addresses:

HSTS Hosts are identified only via domain names -- explicit IP address identification of all forms is excluded.

aeris commented 4 years ago

If we use in CLI the https command to check TLS certificates with IPv6 addresses, the HSTS check should not be done as RFC 6797 Appendix A excludes IP addresses:

HSTS Hosts are identified only via domain names -- explicit IP address identification of all forms is excluded.

I assume https command is used with a FQDN and not an IP, because of the trouble of vhost only testable with a valid FQDN (or you catch the default vhost which is neither reproducible nor guessable nor stable). And by design CryptCheck always validates both endpoint for dual-IP-stacked endpoint, testing naked IP is not very usefull, standard users don't use such address. The only check where nake address seems acceptable is the tls command (basically this is already HTTPS = TLS + HSTS).

But I will try to disable HSTS on naked IP address to be compliant with the RFC.

HLFH commented 4 years ago

I presume the Docker build is using ruby 2.3.8.

I want to check the TLS certificate of Cloudflare at URI [2606:4700:4700::1111]:443.

docker run --rm aeris22/cryptcheck tls 2606:4700:4700::1111 443

I am getting:

docker run --rm aeris22/cryptcheck tls 2606:4700:4700::1111 443
2606:4700:4700::1111:443

Supported methods
  Method TLSv1_2
  Method TLSv1_1
  Method TLSv1

Supported ciphers
  Cipher TLSv1_2 ECDHE-ECDSA-AES128-GCM-SHA256 [aead]
    PFS : ECC 256 bits
  Cipher TLSv1_2 ECDHE-ECDSA-AES256-GCM-SHA384 [aead]
    PFS : ECC 256 bits
  Cipher TLSv1_2 ECDHE-ECDSA-CHACHA20-POLY1305 [aead]
    PFS : ECC 256 bits
  Cipher TLSv1_2 ECDHE-ECDSA-CHACHA20-POLY1305-D [aead]
    PFS : ECC 256 bits
  Cipher TLSv1_2 ECDHE-ECDSA-AES128-SHA256 []
    PFS : ECC 256 bits
  Cipher TLSv1_2 ECDHE-ECDSA-AES256-SHA384 []
    PFS : ECC 256 bits
  Cipher TLSv1_2 ECDHE-ECDSA-AES128-SHA [sha1]
    PFS : ECC 256 bits
  Cipher TLSv1_2 ECDHE-ECDSA-AES256-SHA [sha1]
    PFS : ECC 256 bits
  Cipher TLSv1_1 ECDHE-ECDSA-AES128-SHA [sha1]
    PFS : ECC 256 bits
  Cipher TLSv1_1 ECDHE-ECDSA-AES256-SHA [sha1]
    PFS : ECC 256 bits
  Cipher TLSv1 ECDHE-ECDSA-AES128-SHA [sha1]
    PFS : ECC 256 bits
  Cipher TLSv1 ECDHE-ECDSA-AES256-SHA [sha1]
    PFS : ECC 256 bits

Cipher suite preferences
  TLSv1_2  : ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-ECDSA-CHACHA20-POLY1305, ECDHE-ECDSA-CHACHA20-POLY1305-D, ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES128-SHA256, ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES256-SHA, ECDHE-ECDSA-AES256-SHA384
  TLSv1_1  : ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES256-SHA
  TLSv1  : ECDHE-ECDSA-AES128-SHA, ECDHE-ECDSA-AES256-SHA

Supported elliptic curves
  ECC curve secp256k1
  ECC curve prime256v1
Curves preference : prime256v1, secp256k1

Fallback SCSV : not supported

Certificates
  Certificate /C=US/ST=California/L=San Francisco/O=Cloudflare, Inc./CN=cloudflare-dns.com [2393062632280249844899714234728687389] issued by /C=US/O=DigiCert Inc/CN=DigiCert ECC Secure Server CA
    Key : ECC prime256v1 256 bits
    Identity : invalid
    Trust : trusted

Grade : V
{
    :critical => {
        :mdc2_sign => false,
         :md2_sign => false,
         :md4_sign => false,
         :md5_sign => false,
         :sha_sign => false,
        :sha1_sign => false,
              :ecc => false,
            :sslv2 => false,
            :sslv3 => false,
              :dss => false,
        :anonymous => false,
             :null => false,
           :export => false,
              :des => false,
              :md5 => false,
              :rc4 => false,
          :sweet32 => false
    },
       :error => {
            :ecc => false,
        :tlsv1_0 => true,
        :tlsv1_1 => true,
            :pfs => false
    },
     :warning => {
         :ecc => false,
        :sha1 => true,
         :dhe => false
    },
        :good => {
        :fallback_scsv => false,
                 :aead => true
    },
       :great => {},
        :best => {}
}

I wonder why the Identity is invalid. I presume the file cert.rb is being called on this very specific function:

::OpenSSL::SSL.verify_certificate_identity @cert, host

Does that mean with the current Docker build, it is returning false to show in the CLI the Identity is invalid?

When I am using ruby 2.7.1 to try this function on the same host:

openssl s_client -showcerts -connect [2606:4700:4700::1111]:443 </dev/null 2>/dev/null|openssl x509 -outform PEM >cert.pem

I am getting:

-----BEGIN CERTIFICATE-----
MIIFxjCCBUygAwIBAgIQAczjGN6fVn+rKySQH62nHTAKBggqhkjOPQQDAjBMMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSYwJAYDVQQDEx1EaWdp
Q2VydCBFQ0MgU2VjdXJlIFNlcnZlciBDQTAeFw0xOTAxMjgwMDAwMDBaFw0yMTAy
MDExMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw
FAYDVQQHEw1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBDbG91ZGZsYXJlLCBJbmMu
MRswGQYDVQQDExJjbG91ZGZsYXJlLWRucy5jb20wWTATBgcqhkjOPQIBBggqhkjO
PQMBBwNCAATFIHCMIEJQKB59REF8MHkpHGNeHUSbxfdxOive0qKksWw9ash3uMuP
LlBT/fQYJn9hN+3/wr7pC125fuHfHOJ0o4ID6DCCA+QwHwYDVR0jBBgwFoAUo53m
H/naOU/AbuiRy5Wl2jHiCp8wHQYDVR0OBBYEFHCV3FyjjmYH28uBEMar58OoRX+g
MIGsBgNVHREEgaQwgaGCEmNsb3VkZmxhcmUtZG5zLmNvbYIUKi5jbG91ZGZsYXJl
LWRucy5jb22CD29uZS5vbmUub25lLm9uZYcEAQEBAYcEAQAAAYcEop+ENYcQJgZH
AEcAAAAAAAAAAAAREYcQJgZHAEcAAAAAAAAAAAAQAYcQJgZHAEcAAAAAAAAAAAAA
ZIcQJgZHAEcAAAAAAAAAAABkAIcEop8kAYcEop8uATAOBgNVHQ8BAf8EBAMCB4Aw
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMGkGA1UdHwRiMGAwLqAsoCqG
KGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zc2NhLWVjYy1nMS5jcmwwLqAsoCqG
KGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zc2NhLWVjYy1nMS5jcmwwTAYDVR0g
BEUwQzA3BglghkgBhv1sAQEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGln
aWNlcnQuY29tL0NQUzAIBgZngQwBAgIwewYIKwYBBQUHAQEEbzBtMCQGCCsGAQUF
BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wRQYIKwYBBQUHMAKGOWh0dHA6
Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEVDQ1NlY3VyZVNlcnZlckNB
LmNydDAMBgNVHRMBAf8EAjAAMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdgCk
uQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAWiVHhSLAAAEAwBHMEUC
IQDlnoPeMXtFkRsy3Vs0eovk3ILKt01x6bgUdMlmQTFIvAIgcAn0lFSjiGzHm2eO
jDZJzMiP5Uaj0Jwub9GO8RkxkkoAdQCHdb/nWXz4jEOZX73zbv9WjUdWNv9KtWDB
tOr/XqCDDwAAAWiVHhVsAAAEAwBGMEQCIFC0n0JModeol8b/Qicxd5Blf/o7xOs/
Bk0j9hdc5N7jAiAQocYnHL9iMqTtFkh0vmSsII5NbiakM/2yDEXnwkPRvAB3ALvZ
37wfinG1k5Qjl6qSe0c4V5UKq1LoGpCWZDaOHtGFAAABaJUeFJEAAAQDAEgwRgIh
AL3OPTBzOZpS5rS/uLzqMOiACCFQyY+mTJ+L0I9TcB3RAiEA4+SiPz0/5kFxvrk7
AKYKdvelgV1hiiPbM2YHY+/0BIkwCgYIKoZIzj0EAwIDaAAwZQIwez76hX2HTMur
/I3XRuwfdmVoa8J6ZVEVq+AZsE7DyQh7AV4WNLU+092BrPbnyVUFAjEAzUf5jdz1
pyc74lgOunC7LBE6cPtWbzfGpJiYyT/T+c5eIAwRYziKT0DKbaql7tiZ
-----END CERTIFICATE-----
irb
require 'openssl'
raw = File.read "cert.pem"
cert = OpenSSL::X509::Certificate.new raw
::OpenSSL::SSL.verify_certificate_identity cert, '2606:4700:4700::1111'
=> true #and not false

For this latter test, the openssl version is OpenSSL 1.1.1f. For the related 1.1.1.1 URI with the aeris22/cryptcheck Docker build, I am getting the Identity as "valid", so it seems there is a bug here when we do the check for the IPv6 addresses.

aeris commented 4 years ago

There is a bug in Ruby OpenSSL v2.1.2 binding. Comparison is done with string without any normalization before comparison. 2606:4700:4700::1111 is so basically compared to 2606:4700:4700:0:0:0:0:1111 which is false… It was corrected on master but not released at the moment.

HLFH commented 4 years ago

I quote you:

I'm currently working on a huge rework to try supporting TLSv1.3 with a dual stacked Ruby (2.3 & 2.6).

Yusuke Endoh from the ruby team has just said:

Ruby 2.7 bundles an unreleased version 2.2.0 of openssl that includes the PR. The version management of gems bundled with Ruby by default are not well-organised.

Happily, your huge rework (unless it is cryptcheck-engine project without the openssl dep?), with Ruby 2.6 included will solve this bug as Ruby bundles the unreleased version 2.2.0 of openssl from ruby 2.6.0 preview 3 onwards.

aeris commented 4 years ago

It's not related to the full rewrite of openssl dep :joy: I just try to be able to run checking code on 2.6, to be able to access TLSv1.3 too.