Skip to content

Commit 581e8a2

Browse files
committed
Merge branch 'main' into eap
2 parents 6beb03a + f140b7e commit 581e8a2

23 files changed

+728
-59
lines changed

.github/workflows/build.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- windows-latest
2323
runs-on: ${{ matrix.platform }}
2424
steps:
25-
- uses: actions/checkout@v4.1.0
25+
- uses: actions/checkout@v4.1.1
2626

2727
- uses: actions/setup-java@v3
2828
with:
@@ -55,7 +55,7 @@ jobs:
5555
steps:
5656
# Check out current repository
5757
- name: Fetch Sources
58-
uses: actions/checkout@v4.1.0
58+
uses: actions/checkout@v4.1.1
5959

6060
# Setup Java 11 environment for the next steps
6161
- name: Setup Java
@@ -110,7 +110,7 @@ jobs:
110110

111111
# Run Qodana inspections
112112
- name: Qodana - Code Inspection
113-
uses: JetBrains/qodana-action@v2023.2.6
113+
uses: JetBrains/qodana-action@v2023.2.8
114114

115115
# Prepare plugin archive content for creating artifact
116116
- name: Prepare Plugin Artifact
@@ -139,7 +139,7 @@ jobs:
139139

140140
# Check out current repository
141141
- name: Fetch Sources
142-
uses: actions/checkout@v4.1.0
142+
uses: actions/checkout@v4.1.1
143143

144144
# Remove old release drafts by using the curl request for the available releases with draft flag
145145
- name: Remove Old Release Drafts

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515

1616
# Check out current repository
1717
- name: Fetch Sources
18-
uses: actions/checkout@v4.1.0
18+
uses: actions/checkout@v4.1.1
1919
with:
2020
ref: ${{ github.event.release.tag_name }}
2121

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44

55
## Unreleased
66

7+
## 2.9.1 - 2023-11-06
8+
9+
### Fixed
10+
11+
- Set the `CODER_HEADER_COMMAND` environment variable when executing the CLI with the setting value.
12+
13+
## 2.9.0 - 2023-10-27
14+
715
### Added
816

917
- Configuration options for mTLS.

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ version = properties("pluginVersion")
2424
dependencies {
2525
implementation("com.squareup.retrofit2:retrofit:2.9.0")
2626
// define a BOM and its version
27-
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.11.0"))
27+
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.12.0"))
2828
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
2929
implementation("com.squareup.okhttp3:okhttp")
3030
implementation("com.squareup.okhttp3:logging-interceptor")

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
pluginGroup=com.coder.gateway
44
pluginName=coder-gateway
55
# SemVer format -> https://semver.org
6-
pluginVersion=2.9.0-eap.0
6+
pluginVersion=2.9.1-eap.0
77
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
88
# for insight into build numbers and IntelliJ Platform versions.
99
pluginSinceBuild=233.6745

src/main/kotlin/com/coder/gateway/CoderGatewayConnectionProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ class CoderGatewayConnectionProvider : GatewayConnectionProvider {
140140
if (token == null) { // User aborted.
141141
throw IllegalArgumentException("Unable to connect to $deploymentURL, $TOKEN is missing")
142142
}
143-
val client = CoderRestClient(deploymentURL, token.first,null, settings)
143+
val client = CoderRestClient(deploymentURL, token.first, null, settings)
144144
return try {
145145
Pair(client, client.me().username)
146146
} catch (ex: AuthenticationResponseException) {

src/main/kotlin/com/coder/gateway/sdk/CoderCLIManager.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ class CoderCLIManager @JvmOverloads constructor(
101101
fun downloadCLI(): Boolean {
102102
val etag = getBinaryETag()
103103
val conn = remoteBinaryURL.openConnection() as HttpURLConnection
104+
if (settings.headerCommand.isNotBlank()) {
105+
val headersFromHeaderCommand = CoderRestClient.getHeaders(deploymentURL, settings.headerCommand)
106+
for ((key, value) in headersFromHeaderCommand) {
107+
conn.setRequestProperty(key, value)
108+
}
109+
}
104110
if (etag != null) {
105111
logger.info("Found existing binary at $localBinaryPath; calculated hash as $etag")
106112
conn.setRequestProperty("If-None-Match", "\"$etag\"")
@@ -364,6 +370,7 @@ class CoderCLIManager @JvmOverloads constructor(
364370
private fun exec(vararg args: String): String {
365371
val stdout = ProcessExecutor()
366372
.command(localBinaryPath.toString(), *args)
373+
.environment("CODER_HEADER_COMMAND", settings.headerCommand)
367374
.exitValues(0)
368375
.readOutput(true)
369376
.execute()

src/main/kotlin/com/coder/gateway/sdk/CoderRestClientService.kt

Lines changed: 48 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import com.google.gson.Gson
1919
import com.google.gson.GsonBuilder
2020
import com.intellij.ide.plugins.PluginManagerCore
2121
import com.intellij.openapi.components.Service
22+
import com.intellij.openapi.diagnostic.Logger
2223
import com.intellij.openapi.extensions.PluginId
2324
import com.intellij.openapi.util.SystemInfo
2425
import okhttp3.OkHttpClient
@@ -36,7 +37,6 @@ import java.net.URL
3637
import java.nio.file.Path
3738
import java.security.KeyFactory
3839
import java.security.KeyStore
39-
import java.security.PrivateKey
4040
import java.security.cert.CertificateException
4141
import java.security.cert.CertificateFactory
4242
import java.security.cert.X509Certificate
@@ -47,6 +47,7 @@ import java.util.Base64
4747
import java.util.Locale
4848
import java.util.UUID
4949
import javax.net.ssl.HostnameVerifier
50+
import javax.net.ssl.KeyManager
5051
import javax.net.ssl.KeyManagerFactory
5152
import javax.net.ssl.SNIHostName
5253
import javax.net.ssl.SSLContext
@@ -251,53 +252,56 @@ class CoderRestClient(
251252
}
252253
}
253254

254-
fun coderSocketFactory(settings: CoderSettingsState) : SSLSocketFactory {
255-
if (settings.tlsCertPath.isBlank() || settings.tlsKeyPath.isBlank()) {
256-
return SSLSocketFactory.getDefault() as SSLSocketFactory
257-
}
255+
fun SSLContextFromPEMs(certPath: String, keyPath: String, caPath: String) : SSLContext {
256+
var km: Array<KeyManager>? = null
257+
if (certPath.isNotBlank() && keyPath.isNotBlank()) {
258+
val certificateFactory = CertificateFactory.getInstance("X.509")
259+
val certInputStream = FileInputStream(expandPath(certPath))
260+
val certChain = certificateFactory.generateCertificates(certInputStream)
261+
certInputStream.close()
262+
263+
// ideally we would use something like PemReader from BouncyCastle, but
264+
// BC is used by the IDE. This makes using BC very impractical since
265+
// type casting will mismatch due to the different class loaders.
266+
val privateKeyPem = File(expandPath(keyPath)).readText()
267+
val start: Int = privateKeyPem.indexOf("-----BEGIN PRIVATE KEY-----")
268+
val end: Int = privateKeyPem.indexOf("-----END PRIVATE KEY-----", start)
269+
val pemBytes: ByteArray = Base64.getDecoder().decode(
270+
privateKeyPem.substring(start + "-----BEGIN PRIVATE KEY-----".length, end)
271+
.replace("\\s+".toRegex(), "")
272+
)
273+
274+
val privateKey = try {
275+
val kf = KeyFactory.getInstance("RSA")
276+
val keySpec = PKCS8EncodedKeySpec(pemBytes)
277+
kf.generatePrivate(keySpec)
278+
} catch (e: InvalidKeySpecException) {
279+
val kf = KeyFactory.getInstance("EC")
280+
val keySpec = PKCS8EncodedKeySpec(pemBytes)
281+
kf.generatePrivate(keySpec)
282+
}
258283

259-
val certificateFactory = CertificateFactory.getInstance("X.509")
260-
val certInputStream = FileInputStream(expandPath(settings.tlsCertPath))
261-
val certChain = certificateFactory.generateCertificates(certInputStream)
262-
certInputStream.close()
263-
264-
// ideally we would use something like PemReader from BouncyCastle, but
265-
// BC is used by the IDE. This makes using BC very impractical since
266-
// type casting will mismatch due to the different class loaders.
267-
val privateKeyPem = File(expandPath(settings.tlsKeyPath)).readText()
268-
val start: Int = privateKeyPem.indexOf("-----BEGIN PRIVATE KEY-----")
269-
val end: Int = privateKeyPem.indexOf("-----END PRIVATE KEY-----", start)
270-
val pemBytes: ByteArray = Base64.getDecoder().decode(
271-
privateKeyPem.substring(start + "-----BEGIN PRIVATE KEY-----".length, end)
272-
.replace("\\s+".toRegex(), "")
273-
)
274-
275-
var privateKey : PrivateKey
276-
try {
277-
val kf = KeyFactory.getInstance("RSA")
278-
val keySpec = PKCS8EncodedKeySpec(pemBytes)
279-
privateKey = kf.generatePrivate(keySpec)
280-
} catch (e: InvalidKeySpecException) {
281-
val kf = KeyFactory.getInstance("EC")
282-
val keySpec = PKCS8EncodedKeySpec(pemBytes)
283-
privateKey = kf.generatePrivate(keySpec)
284-
}
285-
286-
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
287-
keyStore.load(null)
288-
certChain.withIndex().forEach {
289-
keyStore.setCertificateEntry("cert${it.index}", it.value as X509Certificate)
290-
}
291-
keyStore.setKeyEntry("key", privateKey, null, certChain.toTypedArray())
284+
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
285+
keyStore.load(null)
286+
certChain.withIndex().forEach {
287+
keyStore.setCertificateEntry("cert${it.index}", it.value as X509Certificate)
288+
}
289+
keyStore.setKeyEntry("key", privateKey, null, certChain.toTypedArray())
292290

293-
val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
294-
keyManagerFactory.init(keyStore, null)
291+
val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
292+
keyManagerFactory.init(keyStore, null)
293+
km = keyManagerFactory.keyManagers
294+
}
295295

296296
val sslContext = SSLContext.getInstance("TLS")
297297

298-
val trustManagers = coderTrustManagers(settings.tlsCAPath)
299-
sslContext.init(keyManagerFactory.keyManagers, trustManagers, null)
298+
val trustManagers = coderTrustManagers(caPath)
299+
sslContext.init(km, trustManagers, null)
300+
return sslContext
301+
}
300302

303+
fun coderSocketFactory(settings: CoderSettingsState) : SSLSocketFactory {
304+
val sslContext = SSLContextFromPEMs(settings.tlsCertPath, settings.tlsKeyPath, settings.tlsCAPath)
301305
if (settings.tlsAlternateHostname.isBlank()) {
302306
return sslContext.socketFactory
303307
}
@@ -393,12 +397,11 @@ class AlternateNameSSLSocketFactory(private val delegate: SSLSocketFactory, priv
393397
}
394398

395399
class CoderHostnameVerifier(private val alternateName: String) : HostnameVerifier {
400+
val logger = Logger.getInstance(CoderRestClientService::class.java.simpleName)
396401
override fun verify(host: String, session: SSLSession): Boolean {
397402
if (alternateName.isEmpty()) {
398-
println("using default hostname verifier, alternateName is empty")
399403
return OkHostnameVerifier.verify(host, session)
400404
}
401-
println("Looking for alternate hostname: $alternateName")
402405
val certs = session.peerCertificates ?: return false
403406
for (cert in certs) {
404407
if (cert !is X509Certificate) {
@@ -411,13 +414,12 @@ class CoderHostnameVerifier(private val alternateName: String) : HostnameVerifie
411414
continue
412415
}
413416
val hostname = entry[1] as String
414-
println("Found cert hostname: $hostname")
417+
logger.debug("Found cert hostname: $hostname")
415418
if (hostname.lowercase(Locale.getDefault()) == alternateName) {
416419
return true
417420
}
418421
}
419422
}
420-
println("No matching hostname found")
421423
return false
422424
}
423425
}

src/main/resources/messages/CoderGatewayBundle.properties

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,20 +93,20 @@ gateway.connector.settings.header-command.comment=An external command that \
9393
outputs additional HTTP headers added to all requests. The command must \
9494
output each header as `key=value` on its own line. The following \
9595
environment variables will be available to the process: CODER_URL.
96-
gateway.connector.settings.tls-cert-path.title=Cert Path:
96+
gateway.connector.settings.tls-cert-path.title=Cert path:
9797
gateway.connector.settings.tls-cert-path.comment=Optionally set this to \
9898
the path of a certificate to use for TLS connections. The certificate \
9999
should be in X.509 PEM format.
100-
gateway.connector.settings.tls-key-path.title=Key Path:
100+
gateway.connector.settings.tls-key-path.title=Key path:
101101
gateway.connector.settings.tls-key-path.comment=Optionally set this to \
102102
the path of the private key that corresponds to the above cert path to use \
103103
for TLS connections. The key should be in X.509 PEM format.
104-
gateway.connector.settings.tls-ca-path.title=CA Path:
104+
gateway.connector.settings.tls-ca-path.title=CA path:
105105
gateway.connector.settings.tls-ca-path.comment=Optionally set this to \
106106
the path of a file containing certificates for an alternate certificate \
107107
authority used to verify TLS certs returned by the Coder service. \
108108
The file should be in X.509 PEM format.
109-
gateway.connector.settings.tls-alt-name.title=Alt Hostname:
109+
gateway.connector.settings.tls-alt-name.title=Alt hostname:
110110
gateway.connector.settings.tls-alt-name.comment=Optionally set this to \
111111
an alternate hostname used for verifying TLS connections. This is useful \
112112
when the hostname used to connect to the Coder service does not match the \
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICvjCCAaagAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJVEVT
3+
VC1yb290MCAXDTIzMTAzMDAyMzY0M1oYDzIxMjMxMDA2MDIzNjQzWjAcMRowGAYD
4+
VQQDDBFURVNULWludGVybWVkaWF0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
5+
AQoCggEBAMGD9oILmMRcplGkcNdSZMsBR7C2yoPtL9iRp3V2BKpiRZvQuXHSQsdc
6+
S0Tpk6vnIWQTLkCjVRawL9BoOzwK3FZQti9iXRMnHuzl0gQGZGiHJZ2P/efWaVvn
7+
cmH3Cu2oNCVePhgYAMOiipYGQPcjnQ2kUvMLldZ9+WC+EcaD+FA/kaccPX+kOxQg
8+
qQ0MnPQFfno0F8gylOac+ouKOsXya+jlctgK3dxC73/I+Cdq8xrOJ8lXOYxggleB
9+
ZRXNWWUhrzomn4rUP9wNBrQzFCGcqIS+QjlACjlyn0gPU//ZGVRZ8gZXoI8pDYuB
10+
lRyWpt970/ZPFuiyfiasAAAc8gJ3C7cCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zAN
11+
BgkqhkiG9w0BAQsFAAOCAQEAdHRiLqlYAyGNMPj6wzJt3XmwDbU5yEWor4q+GmA9
12+
fupirWXeSqKiqngDfvHlQNKgDlm10Kuk7LDVUcAP27Xnv/uFmHIUF+4g/eIjxvog
13+
RorUD2I9hi0Wyww7E8th/JfnuDX4YbIQrv1r5P4JaCoc0C2NBd1hO1Er2GdNEoXm
14+
UYoZg6/P5YQkWSLYtLPswb/Hf63DvzG94H6HnFBYlumt/5xYLrfD1Lx8099wZVdR
15+
qWXSi/tYi0HJGGUynZCvjdUu5En7eDoyWclGHz3stOUkBlz0efz01bxpiGsE/rRG
16+
Xr6qJt45N0Zktytk5TphoeDAeFB5ZHRRatZsg9CyZGoaIA==
17+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy