@@ -22,9 +22,12 @@ import com.intellij.openapi.project.Project
2222import com.intellij.openapi.util.Disposer
2323import com.intellij.openapi.util.Key
2424import com.intellij.openapi.util.SystemInfo
25+ import com.intellij.util.EnvironmentUtil
26+ import com.intellij.util.io.DigestUtil
2527import com.intellij.util.io.await
2628import com.intellij.util.net.HttpConfigurable
2729import com.intellij.util.net.JdkProxyProvider
30+ import com.intellij.util.net.ssl.CertificateManager
2831import kotlinx.coroutines.CoroutineScope
2932import kotlinx.coroutines.Deferred
3033import kotlinx.coroutines.Job
@@ -77,6 +80,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.textdocument.TextDoc
7780import software.aws.toolkits.jetbrains.services.amazonq.lsp.util.WorkspaceFolderUtil.createWorkspaceFolders
7881import software.aws.toolkits.jetbrains.services.amazonq.lsp.workspace.WorkspaceServiceHandler
7982import software.aws.toolkits.jetbrains.services.amazonq.profile.QDefaultServiceConfig
83+ import software.aws.toolkits.jetbrains.services.amazonq.profile.QEndpoints
8084import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl
8185import software.aws.toolkits.jetbrains.services.telemetry.ClientMetadata
8286import software.aws.toolkits.jetbrains.settings.LspSettings
@@ -369,21 +373,50 @@ private class AmazonQServerInstance(private val project: Project, private val cs
369373 // will cause slow service init, but maybe fine for now. will not block UI since fetch/extract will be under background progress
370374 val artifact = runBlocking { service<ArtifactManager >().fetchArtifact(project) }.toAbsolutePath()
371375
372- // more network calls
373- // make assumption that all requests will resolve to the same CA
374- // also terrible assumption that default endpoint is reachable
375- val qUri = URI (QDefaultServiceConfig .ENDPOINT )
376- val extraCaCerts = try {
377- val rtsTrustChain = TrustChainUtil .getTrustChain(qUri)
376+ // make some network calls for troubleshooting
377+ listOf (* QEndpoints .listRegionEndpoints().map { it.endpoint }.toTypedArray(), QDefaultServiceConfig .ENDPOINT ).forEach {
378+ try {
379+ val qUri = URI (it)
380+ val rtsTrustChain = TrustChainUtil .getTrustChain(qUri)
381+ val trustRoot = rtsTrustChain.last()
382+ // ATS is cross-signed against starfield certs: https://www.amazontrust.com/repository/
383+ if (listOf (" Amazon Root CA" , " Starfield Technologies" ).any { trustRoot.subjectX500Principal.name.contains(it) }) {
384+ LOG .info { " Trust chain for $it ends with public-like CA with sha256 fingerprint: ${DigestUtil .sha256Hex(trustRoot.encoded)} " }
385+ } else {
386+ LOG .info {
387+ """
388+ |Trust chain for $it transits private CA:
389+ |${buildString {
390+ rtsTrustChain.forEach { cert ->
391+ append(" Issuer: ${cert.issuerX500Principal} , " )
392+ append(" Subject: ${cert.subjectX500Principal} , " )
393+ append(" Fingerprint: ${DigestUtil .sha256Hex(cert.encoded)} \n\t " )
394+ }
395+ }}
396+ """ .trimMargin(" |" )
397+ }
398+ LOG .debug { " Full trust chain info for $it : $rtsTrustChain " }
399+ }
400+ } catch (e: Exception ) {
401+ LOG .info { " ${e.message} : Could not resolve trust chain for $it " }
402+ }
403+ }
378404
405+ val userEnvNodeCaCerts = EnvironmentUtil .getValue(" NODE_EXTRA_CA_CERTS" )
406+ // if user has NODE_EXTRA_CA_CERTS in their environment, assume they know what they're doing
407+ val extraCaCerts = if (! userEnvNodeCaCerts.isNullOrEmpty()) {
408+ LOG .info { " Skipping injection of IDE trust store, user already defines NODE_EXTRA_CA_CERTS: $userEnvNodeCaCerts " }
409+ null
410+ } else {
411+ // otherwise include everything the IDE knows about
412+ val allAcceptedIssuers = CertificateManager .getInstance().trustManager.acceptedIssuers
413+ val customIssuers = CertificateManager .getInstance().customTrustManager.acceptedIssuers
414+ LOG .info { " Injecting ${allAcceptedIssuers.size} trusted certificates (${customIssuers.size} from IDE custom manager) into NODE_EXTRA_CA_CERTS" }
379415 Files .createTempFile(" q-extra-ca" , " .pem" ).apply {
380416 writeText(
381- TrustChainUtil .certsToPem(rtsTrustChain )
417+ TrustChainUtil .certsToPem(CertificateManager .getInstance().trustManager.acceptedIssuers.toList() )
382418 )
383- }
384- } catch (e: Exception ) {
385- LOG .info(e) { " Could not resolve trust chain for $qUri , skipping NODE_EXTRA_CA_CERTS" }
386- null
419+ }.toAbsolutePath().toString()
387420 }
388421
389422 val node = if (SystemInfo .isWindows) " node.exe" else " node"
@@ -396,8 +429,13 @@ private class AmazonQServerInstance(private val project: Project, private val cs
396429 " --set-credentials-encryption-key" ,
397430 ).withEnvironment(
398431 buildMap {
399- extraCaCerts?.let { put(" NODE_EXTRA_CA_CERTS" , it.toAbsolutePath().toString()) }
432+ extraCaCerts?.let {
433+ LOG .info { " Starting Flare with NODE_EXTRA_CA_CERTS: $it " }
434+ put(" NODE_EXTRA_CA_CERTS" , it)
435+ }
400436
437+ // assume default endpoint will pick correct proxy if needed
438+ val qUri = URI (QDefaultServiceConfig .ENDPOINT )
401439 val proxy = JdkProxyProvider .getInstance().proxySelector.select(qUri)
402440 // log if only socks proxy available
403441 .firstOrNull { it.type() == Proxy .Type .HTTP }
0 commit comments