kiranchavala opened a new issue, #14505:
URL: https://github.com/apache/cloudstack/issues/14505

   ### problem
   
   When a Direct Download template/ISO is registered with an `http://` URL that 
redirects to `https://`
   
   http://download.cirros-cloud.net/0.6.2/cirros-0.6.2-x86_64-disk.img >  
https://github.com/cirros-dev/cirros/releases/download/0.6.2/cirros-0.6.2-x86_64-disk.img
   
   
   ```
   curl -sI http://download.cirros-cloud.net/0.6.2/cirros-0.6.2-x86_64-disk.img
   HTTP/1.1 302 Found
   Date: Tue, 27 Oct 2026 12:37:30 GMT
   Server: Apache
   Location: 
https://github.com/cirros-dev/cirros/releases/download/0.6.2/cirros-0.6.2-x86_64-disk.img
   Cache-Control: max-age=600
   Expires: Tue, 27 Oct 2026 12:47:30 GMT
   Content-Type: text/html; charset=iso-8859-1
   
   ````
   
   
   
   host fails with a TLS error, even when the target's root CA is present in 
the JRE trust store (`cacerts`):
   
   ```
       ERROR [direct.download.HttpDirectTemplateDownloader] Cannot reach URL: 
http://download.cirros-cloud.net/0.6.2/cirros-0.6.2-x86_64-disk.img
       due to: (certificate_unknown) PKIX path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException:
       unable to find valid certification path to requested target
           ...
           at 
org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
           at 
org.apache.cloudstack.direct.download.HttpDirectTemplateDownloader.checkUrl(HttpDirectTemplateDownloader.java:120)
           at 
org.apache.cloudstack.direct.download.DirectDownloadHelper.checkUrlExistence(DirectDownloadHelper.java:75)
           at 
com.cloud.hypervisor.kvm.resource.wrapper.LibvirtCheckUrlCommand.execute(LibvirtCheckUrlCommand.java:43)
   
   ```
   
   Related: #12878 (same trust-store root cause for the `https://` Let's 
Encrypt case).
   
   
   ### versions
   
   ACS 4.22.x and 4.23 
   Hypervisor: KVM,  OpenJDK 17
   
   
   
   ### The steps to reproduce the bug
   
   1. Set `store.download.follow.redirects` to `true`.
   2. On the kvm host make sure latest ca certificate is present 
   
   ```
    dnf update -y ca-certificates
    update-ca-trust extract
   ```
   
   2. Make sure the KVM host's JRE `cacerts` trusts the target CAs
   
   ```
   [root@ref-trl-12464-k-Mol8-kiran-chavala-kvm1 ~]# keytool -list -cacerts 
-storepass changeit 2>/dev/null | grep -iE 'sectigo|isrg'
   isrgrootx1, 27 Oct 2026, trustedCertEntry,
   isrgrootx2, 27 Oct 2026, trustedCertEntry,
   sectigopublicserverauthenticationroote46, 27 Oct 2026, trustedCertEntry,
   sectigopublicserverauthenticationrootr46, 27 Oct 2026, trustedCertEntry,
   
   ```
   These certs are not present in /etc/cloudstack/agent/cloud.jks
   
   
   3. Register a direct download template:
   
   ```
          cmk register template name=cirros-http displaytext=cirros 
format=QCOW2 hypervisor=KVM \
            
url=http://download.cirros-cloud.net/0.6.2/cirros-0.6.2-x86_64-disk.img \
            ostypeid=<id> zoneid=<id> directdownload=true
   
   ```
   
   4. Registration fails with `URL: ... validation failed on host ...`; the 
agent log shows the PKIX error above.
   
   ```
       ERROR [direct.download.HttpDirectTemplateDownloader] Cannot reach URL: 
http://download.cirros-cloud.net/0.6.2/cirros-0.6.2-x86_64-disk.img
       due to: (certificate_unknown) PKIX path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException:
       unable to find valid certification path to requested target
           ...
           at 
org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
           at 
org.apache.cloudstack.direct.download.HttpDirectTemplateDownloader.checkUrl(HttpDirectTemplateDownloader.java:120)
           at 
org.apache.cloudstack.direct.download.DirectDownloadHelper.checkUrlExistence(DirectDownloadHelper.java:75)
           at 
com.cloud.hypervisor.kvm.resource.wrapper.LibvirtCheckUrlCommand.execute(LibvirtCheckUrlCommand.java:43)
   ```
   5. Registering the equivalent ` 
https://github.com/cirros-dev/cirros/releases/download/0.6.2/cirros-0.6.2-x86_64-disk.img`
 URL succeeds for the HEAD check, showing that only the HTTP downloader path is 
affected.
   
   Workaround 
   
   is to upload every public root in the redirect chain via 
`uploadTemplateDirectDownloadCertificate`, which is  meant for private CAs.
   
   ```
   keytool -exportcert -rfc -cacerts -storepass changeit   -alias 
sectigopublicserverauthenticationroote46 -file /root/sectigo-e46.pem
   keytool -exportcert -rfc -cacerts -storepass changeit -alias isrgrootx1 
-file /root/isrg-root-x1.pem
   
   cmk upload templatedirectdownloadcertificate hypervisor=KVM name=sectigo-e46 
zoneid=7ddb0067-e94b-4320-a08d-2b0019b83a20 certificate="$(cat 
/root/sectigo-e46.pem)"
   
   cmk upload templatedirectdownloadcertificate hypervisor=KVM 
name=isrg-root-x1 zoneid=7ddb0067-e94b-4320-a08d-2b0019b83a20 
certificate="$(cat /root/isrg-root-x1.pem)"
   
   On kvm host 
   
    PASS=$(sed -n 's/^keystore.passphrase=//p' 
/etc/cloudstack/agent/agent.properties)
     keytool -list -keystore /etc/cloudstack/agent/cloud.jks -storepass "$PASS" 
| grep -i sectigo
   
   
   
   ```
   
   
   ### What to do about it?
   
   Root case form Claude ai 
   
   ```
   
   - `LibvirtComputingResource.enableSSLForKvmAgent()` sets the JVM-wide 
`javax.net.ssl.trustStore` to
     `/etc/cloudstack/agent/cloud.jks`, which normally contains only the 
CloudStack CA.
   - `HttpsDirectTemplateDownloader` builds its own SSLContext that merges 
`cloud.jks` **and** the JRE `cacerts`,
     so `https://` URLs validate correctly.
   - `HttpDirectTemplateDownloader` (used for `http://` URLs) uses 
commons-httpclient 3.x, whose `https`
     protocol handler uses the JVM default SSL socket factory. When the 
redirect is followed to HTTPS, the
     certificate is checked against `cloud.jks` only, and public CAs (Let's 
Encrypt, Sectigo, DigiCert, ...)
     are rejected.
   
   ```
   
   Fix
   
   ```
   - Migrate `HttpDirectTemplateDownloader` from commons-httpclient 3.x to 
Apache HttpClient 4.x (as
     `HttpsDirectTemplateDownloader` already uses), configured with an 
`SSLConnectionSocketFactory` built from
     the merged `cloud.jks` + JRE `cacerts` trust material, so redirects to 
HTTPS are validated the same way as
     direct `https://` URLs.
   - Factor the trust-store merging logic out of 
`HttpsDirectTemplateDownloader.getSSLContext()` into a shared
     helper so both downloaders (and `getRemoteFileSize`) use one context. If 
`cloud.jks` can't be loaded, the
     helper should still load `cacerts` rather than falling back to 
`SSLContext.getDefault()`, which is `cloud.jks`-only.
   ```
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to