Groovy scripting context cannot make HTTPS requests to hosts using internal CA certificates #2806
Alejandro Oton Garcia opened 1 week ago

When using Groovy scripts in custom issue field definitions, any HTTPS request to a host signed by an internal CA fails with an SSL handshake error, even when the CA certificates have been correctly imported into OneDev's conf\trust-certs directory.

The error the Groovy script shows is

javax.net.ssl.SSLHandshakeException: (certificate_unknown) PKIX path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid 
certification path to requested target

We have an internal repository where we host internally built modules. For enhanced issue tracking, I wanted to add an Enum type custom field where whoever opens an issue could choose which module and version this was in relation to. The modules get published to an internal NuGet feed, so the Groovy script basically parses the NuGet feed and returns a list of these modules. However, this is what the custom field shows.

image.png

Example of the Groovy script below

def progetUrl = "https://proget.domain.com"
def feedName  = "my-powershell-modules"

try {
    def url = new URL("${progetUrl}/api/packages/${feedName}/latest")
    def response = url.text
    
    def mapper = onedev.getInstance(com.fasterxml.jackson.databind.ObjectMapper.class)
    def packages = mapper.readValue(response, List.class)
    
    def choices = [:]
    packages.findAll { it.name?.startsWith("My") }.each { pkg ->
        def key = pkg.name + " (" + pkg.version + ")"
        choices.put(key, null)
    }
    return choices

} catch (Exception e) {
    def errKey = "Error: " + e.message
    def choices = [:]
    choices.put(errKey, null)
    return choices
}
  • Robin Shen commented 1 week ago

    Your custom script needs to be use OneDev SSL factory to work with self-signed certificate. Something like this:

       def connection = url.openConnection()
    
       if (connection instanceof javax.net.ssl.HttpsURLConnection) {
           def sslFactory = io.onedev.server.OneDev.getInstance(
               nl.altindag.ssl.SSLFactory.class
           )
           connection.sslSocketFactory = sslFactory.sslSocketFactory
           connection.hostnameVerifier = sslFactory.hostnameVerifier
       }
    
  • Robin Shen changed fields 1 week ago
    Name Previous Value Current Value
    Type
    Bug
    Question
  • Alejandro Oton Garcia commented 1 week ago

    Thanks Robin, that is good to know.

  • Alejandro Oton Garcia changed state to 'Closed' 1 week ago
    Previous Value Current Value
    Open
    Closed
  • Alejandro Oton Garcia commented 1 week ago

    After adding your suggestions, I got the following error

    Error: No such property: sslSocketFactory for class: sun.net.www.protocol.https.HttpsURLConnectionImpl
    Possible solutions: SSLSocketFactory
    

    This is what my script looks like - let me know if I missed anything:

    def progetUrl = "https://proget.domain.com"
    def feedName  = "my-powershell-modules"
    
    try {
        def url = new URL("${progetUrl}/api/packages/${feedName}/latest")
    
        def connection = url.openConnection()
    
        if (connection instanceof javax.net.ssl.HttpsURLConnection) {
            def sslFactory = io.onedev.server.OneDev.getInstance(
                nl.altindag.ssl.SSLFactory.class
            )
            connection.sslSocketFactory = sslFactory.sslSocketFactory
            connection.hostnameVerifier = sslFactory.hostnameVerifier
        }
    
        def response = url.text
    
        def mapper = io.onedev.server.OneDev.getInstance(com.fasterxml.jackson.databind.ObjectMapper.class)
        def packages = mapper.readValue(response, List.class)
    
        def choices = [:]
        packages.findAll { it.name?.startsWith("MY") }.each { pkg ->
            def key = pkg.name + " (" + pkg.version + ")"
            choices.put(key, null)
        }
        return choices
    
    } catch (Exception e) {
        def errKey = "Error: " + e.message
        def choices = [:]
        choices.put(errKey, null)
        return choices
    }
    
  • Alejandro Oton Garcia changed state to 'Open' 1 week ago
    Previous Value Current Value
    Closed
    Open
  • Robin Shen commented 1 week ago

    The Groovy property syntax does not handle the acronym-style JavaBean property correctly here. Please use the explicit setter methods:

    connection.setSSLSocketFactory(sslFactory.getSslSocketFactory())
    connection.setHostnameVerifier(sslFactory.getHostnameVerifier())
    

    Also replace:

    def response = url.text
    

    with:

    def response = connection.inputStream.getText("UTF-8")
    

    url.text opens another connection, so it would not use the SSL configuration applied to connection.

  • Alejandro Oton Garcia commented 1 week ago

    Thank you Robin, that worked.

  • Alejandro Oton Garcia changed state to 'Closed' 1 week ago
    Previous Value Current Value
    Open
    Closed
1/1
Type
Question
Priority
Normal
Assignee
Labels
No labels
Issue Votes (0)
Watchers (3)
Reference
OD-2806
Please wait...
Connection lost or session expired, reload to recover
Page is in error, reload to recover