Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitb7b609d

Browse files
authored
fix: relaxed SNI hostname resolution (#579)
* fix: relaxed SNI hostname resolutionWhen establishing TLS connections, SNI resolution may fail if the configured altHostname contains `_` orany other characters not allowed by domain name standards (i.e. letters, digits and hyphens).This change introduces a relaxed SNI resolution strategy which ignores the LDH rules completely.Because this change goes hand in hand with auth. via certificates, I was able to reproduce the issueonly via UTs. At this point the official Coder releases supports only auth. via API keys.-fixes#577* chore: next version 2.22.3* chore: remove leftover debug liness
1 parentf1a40c0 commitb7b609d

File tree

5 files changed

+495
-5
lines changed

5 files changed

+495
-5
lines changed

‎CHANGELOG.md‎

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

55
##Unreleased
66

7+
###Fixed
8+
9+
- relaxed SNI hostname resolution
10+
711
##2.22.2 - 2025-09-08
812

913
###Fixed

‎gradle.properties‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pluginGroup=com.coder.gateway
55
artifactName=coder-gateway
66
pluginName=Coder
77
# SemVer format -> https://semver.org
8-
pluginVersion=2.22.2
8+
pluginVersion=2.22.3
99
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
1010
# for insight into build numbers and IntelliJ Platform versions.
1111
pluginSinceBuild=243.26574

‎src/main/kotlin/com/coder/gateway/util/TLS.kt‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import okhttp3.internal.tls.OkHostnameVerifier
55
importorg.slf4j.LoggerFactory
66
importjava.io.File
77
importjava.io.FileInputStream
8+
importjava.net.IDN
89
importjava.net.InetAddress
910
importjava.net.Socket
11+
importjava.nio.charset.StandardCharsets
1012
importjava.security.KeyFactory
1113
importjava.security.KeyStore
1214
importjava.security.cert.CertificateException
@@ -19,11 +21,12 @@ import java.util.Locale
1921
importjavax.net.ssl.HostnameVerifier
2022
importjavax.net.ssl.KeyManager
2123
importjavax.net.ssl.KeyManagerFactory
22-
importjavax.net.ssl.SNIHostName
24+
importjavax.net.ssl.SNIServerName
2325
importjavax.net.ssl.SSLContext
2426
importjavax.net.ssl.SSLSession
2527
importjavax.net.ssl.SSLSocket
2628
importjavax.net.ssl.SSLSocketFactory
29+
importjavax.net.ssl.StandardConstants
2730
importjavax.net.ssl.TrustManager
2831
importjavax.net.ssl.TrustManagerFactory
2932
importjavax.net.ssl.X509TrustManager
@@ -112,7 +115,8 @@ fun coderTrustManagers(tlsCAPath: String): Array<TrustManager> {
112115
return trustManagerFactory.trustManagers.map {MergedSystemTrustManger(itasX509TrustManager) }.toTypedArray()
113116
}
114117

115-
classAlternateNameSSLSocketFactory(privatevaldelegate:SSLSocketFactory, privatevalalternateName:String) : SSLSocketFactory() {
118+
classAlternateNameSSLSocketFactory(privatevaldelegate:SSLSocketFactory, privatevalalternateName:String) :
119+
SSLSocketFactory() {
116120
overridefungetDefaultCipherSuites():Array<String>= delegate.defaultCipherSuites
117121

118122
overridefungetSupportedCipherSuites():Array<String>= delegate.supportedCipherSuites
@@ -176,11 +180,17 @@ class AlternateNameSSLSocketFactory(private val delegate: SSLSocketFactory, priv
176180

177181
privatefuncustomizeSocket(socket:SSLSocket) {
178182
val params= socket.sslParameters
179-
params.serverNames=listOf(SNIHostName(alternateName))
183+
184+
params.serverNames=listOf(RelaxedSNIHostname(alternateName))
180185
socket.sslParameters= params
181186
}
182187
}
183188

189+
privateclassRelaxedSNIHostname(hostname:String) : SNIServerName(
190+
StandardConstants.SNI_HOST_NAME,
191+
IDN.toASCII(hostname, 0).toByteArray(StandardCharsets.UTF_8)
192+
)
193+
184194
classCoderHostnameVerifier(privatevalalternateName:String) : HostnameVerifier {
185195
privateval logger=LoggerFactory.getLogger(javaClass)
186196

@@ -244,5 +254,6 @@ class MergedSystemTrustManger(private val otherTrustManager: X509TrustManager) :
244254
}
245255
}
246256

247-
overridefungetAcceptedIssuers():Array<X509Certificate>= otherTrustManager.acceptedIssuers+ systemTrustManager.acceptedIssuers
257+
overridefungetAcceptedIssuers():Array<X509Certificate>=
258+
otherTrustManager.acceptedIssuers+ systemTrustManager.acceptedIssuers
248259
}
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
packagecom.coder.gateway.util
2+
3+
importio.mockk.Runs
4+
importio.mockk.every
5+
importio.mockk.just
6+
importio.mockk.mockk
7+
importio.mockk.verify
8+
importjava.net.InetAddress
9+
importjava.net.Socket
10+
importjavax.net.ssl.SSLParameters
11+
importjavax.net.ssl.SSLSocket
12+
importjavax.net.ssl.SSLSocketFactory
13+
importkotlin.test.Test
14+
importkotlin.test.assertEquals
15+
importkotlin.test.assertNotNull
16+
importkotlin.test.assertSame
17+
18+
19+
classAlternateNameSSLSocketFactoryTest {
20+
21+
@Test
22+
fun`createSocket with no parameters should customize socket with alternate name`() {
23+
// Given
24+
val mockFactory= mockk<SSLSocketFactory>()
25+
val mockSocket= mockk<SSLSocket>(relaxed=true)
26+
val mockParams= mockk<SSLParameters>(relaxed=true)
27+
28+
every { mockFactory.createSocket() } returns mockSocket
29+
every { mockSocket.sslParameters } returns mockParams
30+
every { mockSocket.sslParameters= any() } justRuns
31+
32+
val alternateFactory=AlternateNameSSLSocketFactory(mockFactory,"alternate.example.com")
33+
34+
// When
35+
val result= alternateFactory.createSocket()
36+
37+
// Then
38+
verify { mockSocket.sslParameters= any() }
39+
assertSame(mockSocket, result)
40+
}
41+
42+
@Test
43+
fun`createSocket with host and port should customize socket with alternate name`() {
44+
// Given
45+
val mockFactory= mockk<SSLSocketFactory>()
46+
val mockSocket= mockk<SSLSocket>(relaxed=true)
47+
val mockParams= mockk<SSLParameters>(relaxed=true)
48+
49+
every { mockFactory.createSocket("original.com",443) } returns mockSocket
50+
every { mockSocket.sslParameters } returns mockParams
51+
every { mockSocket.sslParameters= any() } justRuns
52+
53+
val alternateFactory=AlternateNameSSLSocketFactory(mockFactory,"alternate.example.com")
54+
55+
// When
56+
val result= alternateFactory.createSocket("original.com",443)
57+
58+
// Then
59+
verify { mockSocket.sslParameters= any() }
60+
assertSame(mockSocket, result)
61+
}
62+
63+
@Test
64+
fun`createSocket with host port and local address should customize socket`() {
65+
// Given
66+
val mockFactory= mockk<SSLSocketFactory>()
67+
val mockSocket= mockk<SSLSocket>(relaxed=true)
68+
val mockParams= mockk<SSLParameters>(relaxed=true)
69+
val localHost= mockk<InetAddress>()
70+
71+
every { mockFactory.createSocket("original.com",443, localHost,8080) } returns mockSocket
72+
every { mockSocket.sslParameters } returns mockParams
73+
every { mockSocket.sslParameters= any() } justRuns
74+
75+
val alternateFactory=AlternateNameSSLSocketFactory(mockFactory,"alternate.example.com")
76+
77+
// When
78+
val result= alternateFactory.createSocket("original.com",443, localHost,8080)
79+
80+
// Then
81+
verify { mockSocket.sslParameters= any() }
82+
assertSame(mockSocket, result)
83+
}
84+
85+
@Test
86+
fun`createSocket with InetAddress should customize socket with alternate name`() {
87+
// Given
88+
val mockFactory= mockk<SSLSocketFactory>()
89+
val mockSocket= mockk<SSLSocket>(relaxed=true)
90+
val mockParams= mockk<SSLParameters>(relaxed=true)
91+
val address= mockk<InetAddress>()
92+
93+
every { mockFactory.createSocket(address,443) } returns mockSocket
94+
every { mockSocket.sslParameters } returns mockParams
95+
every { mockSocket.sslParameters= any() } justRuns
96+
97+
val alternateFactory=AlternateNameSSLSocketFactory(mockFactory,"alternate.example.com")
98+
99+
// When
100+
val result= alternateFactory.createSocket(address,443)
101+
102+
// Then
103+
verify { mockSocket.sslParameters= any() }
104+
assertSame(mockSocket, result)
105+
}
106+
107+
@Test
108+
fun`createSocket with InetAddress and local address should customize socket`() {
109+
// Given
110+
val mockFactory= mockk<SSLSocketFactory>()
111+
val mockSocket= mockk<SSLSocket>(relaxed=true)
112+
val mockParams= mockk<SSLParameters>(relaxed=true)
113+
val address= mockk<InetAddress>()
114+
val localAddress= mockk<InetAddress>()
115+
116+
every { mockFactory.createSocket(address,443, localAddress,8080) } returns mockSocket
117+
every { mockSocket.sslParameters } returns mockParams
118+
every { mockSocket.sslParameters= any() } justRuns
119+
120+
val alternateFactory=AlternateNameSSLSocketFactory(mockFactory,"alternate.example.com")
121+
122+
// When
123+
val result= alternateFactory.createSocket(address,443, localAddress,8080)
124+
125+
// Then
126+
verify { mockSocket.sslParameters= any() }
127+
assertSame(mockSocket, result)
128+
}
129+
130+
@Test
131+
fun`createSocket with existing socket should customize socket with alternate name`() {
132+
// Given
133+
val mockFactory= mockk<SSLSocketFactory>()
134+
val mockSSLSocket= mockk<SSLSocket>(relaxed=true)
135+
val mockParams= mockk<SSLParameters>(relaxed=true)
136+
val existingSocket= mockk<Socket>()
137+
138+
every { mockFactory.createSocket(existingSocket,"original.com",443,true) } returns mockSSLSocket
139+
every { mockSSLSocket.sslParameters } returns mockParams
140+
every { mockSSLSocket.sslParameters= any() } justRuns
141+
142+
val alternateFactory=AlternateNameSSLSocketFactory(mockFactory,"alternate.example.com")
143+
144+
// When
145+
val result= alternateFactory.createSocket(existingSocket,"original.com",443,true)
146+
147+
// Then
148+
verify { mockSSLSocket.sslParameters= any() }
149+
assertSame(mockSSLSocket, result)
150+
}
151+
152+
@Test
153+
fun`customizeSocket should set SNI hostname to alternate name for valid hostname`() {
154+
// Given
155+
val mockFactory= mockk<SSLSocketFactory>()
156+
val mockSocket= mockk<SSLSocket>(relaxed=true)
157+
val mockParams= mockk<SSLParameters>(relaxed=true)
158+
159+
every { mockFactory.createSocket() } returns mockSocket
160+
every { mockSocket.sslParameters } returns mockParams
161+
every { mockSocket.sslParameters= any() } justRuns
162+
163+
val alternateFactory=AlternateNameSSLSocketFactory(mockFactory,"valid-hostname.example.com")
164+
165+
// When & Then - This should work without throwing an exception
166+
assertNotNull(alternateFactory.createSocket())
167+
verify { mockSocket.sslParameters= any() }
168+
}
169+
170+
@Test
171+
fun`customizeSocket should NOT throw IllegalArgumentException for hostname with underscore`() {
172+
// Given
173+
val mockFactory= mockk<SSLSocketFactory>()
174+
val mockSocket= mockk<SSLSocket>(relaxed=true)
175+
val mockParams= mockk<SSLParameters>(relaxed=true)
176+
177+
every { mockFactory.createSocket() } returns mockSocket
178+
every { mockSocket.sslParameters } returns mockParams
179+
every { mockSocket.sslParameters= any() } justRuns
180+
181+
val alternateFactory=AlternateNameSSLSocketFactory(mockFactory,"non_compliant_hostname.example.com")
182+
183+
// When & Then - This should work without throwing an exception
184+
assertNotNull(alternateFactory.createSocket())
185+
verify { mockSocket.sslParameters= any() }
186+
assertEquals(0, mockSocket.sslParameters.serverNames.size)
187+
}
188+
189+
@Test
190+
fun`createSocket should work with valid international domain names`() {
191+
// Given
192+
val mockFactory= mockk<SSLSocketFactory>()
193+
val mockSocket= mockk<SSLSocket>(relaxed=true)
194+
val mockParams= mockk<SSLParameters>(relaxed=true)
195+
196+
every { mockFactory.createSocket() } returns mockSocket
197+
every { mockSocket.sslParameters } returns mockParams
198+
every { mockSocket.sslParameters= any() } justRuns
199+
200+
val alternateFactory=AlternateNameSSLSocketFactory(mockFactory,"test-server.example.com")
201+
202+
// When & Then - This should work as hyphens are valid
203+
assertNotNull(alternateFactory.createSocket())
204+
verify { mockSocket.sslParameters= any() }
205+
}
206+
207+
privatefuncreateMockSSLSocketFactory():SSLSocketFactory {
208+
val mockFactory= mockk<SSLSocketFactory>()
209+
val mockSocket= mockk<SSLSocket>(relaxed=true)
210+
val mockParams= mockk<SSLParameters>(relaxed=true)
211+
212+
// Setup default behavior
213+
every { mockFactory.defaultCipherSuites } returns arrayOf("TLS_AES_256_GCM_SHA384")
214+
every { mockFactory.supportedCipherSuites } returns arrayOf("TLS_AES_256_GCM_SHA384","TLS_AES_128_GCM_SHA256")
215+
216+
// Make all createSocket methods return our mock socket
217+
every { mockFactory.createSocket() } returns mockSocket
218+
every { mockFactory.createSocket(any<String>(), any<Int>()) } returns mockSocket
219+
every { mockFactory.createSocket(any<String>(), any<Int>(), any<InetAddress>(), any<Int>()) } returns mockSocket
220+
every { mockFactory.createSocket(any<InetAddress>(), any<Int>()) } returns mockSocket
221+
every {
222+
mockFactory.createSocket(
223+
any<InetAddress>(),
224+
any<Int>(),
225+
any<InetAddress>(),
226+
any<Int>()
227+
)
228+
} returns mockSocket
229+
every { mockFactory.createSocket(any<Socket>(), any<String>(), any<Int>(), any<Boolean>()) } returns mockSocket
230+
231+
// Setup SSL parameters
232+
every { mockSocket.sslParameters } returns mockParams
233+
every { mockSocket.sslParameters= any() } justRuns
234+
235+
return mockFactory
236+
}
237+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp