Skip to content

Commit 0599062

Browse files
authored
security: require mixed inbound auth whenever exposed to LAN (#4)
The mixed inbound binds to 0.0.0.0 when allowAccess is enabled (in both VPN and Proxy modes), but authentication was only applied in VPN mode and was dropped entirely when appendHttpProxy was active on Android Q+. That left an unauthenticated open proxy reachable from the LAN. - mixedInboundNeedsAuth now requires auth whenever allowAccess is set, regardless of service mode or appendHttpProxy. - Loopback-only behavior is unchanged (appendHttpProxy still works). - When allowAccess and appendHttpProxy are both on (Q+), skip VpnService.setHttpProxy, since Android's system HTTP proxy cannot supply credentials; log a note explaining why.
1 parent b5458cc commit 0599062

2 files changed

Lines changed: 22 additions & 3 deletions

File tree

app/src/main/java/io/nekohasekai/sagernet/bg/VpnService.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,18 @@ class VpnService : BaseVpnService(),
189189
}
190190

191191
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && DataStore.appendHttpProxy) {
192-
builder.setHttpProxy(ProxyInfo.buildDirectProxy(LOCALHOST, DataStore.mixedPort))
192+
if (DataStore.allowAccess) {
193+
// When LAN access is enabled the mixed inbound requires authentication
194+
// (see DataStore.mixedInboundNeedsAuth). Android's system HTTP proxy
195+
// cannot supply credentials, so registering it here would just fail.
196+
// Skip it instead of weakening inbound auth and exposing an open LAN proxy.
197+
Logs.w(
198+
"Append HTTP proxy was skipped because LAN access requires proxy " +
199+
"authentication. Disable Allow LAN access to use system HTTP proxy."
200+
)
201+
} else {
202+
builder.setHttpProxy(ProxyInfo.buildDirectProxy(LOCALHOST, DataStore.mixedPort))
203+
}
193204
}
194205

195206
metered = DataStore.meteredNetwork

app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,16 @@ object DataStore : OnPreferenceDataStoreChangeListener {
153153
set(value) = saveLocalPort(Key.MIXED_PORT, value)
154154

155155
val mixedInboundNeedsAuth: Boolean
156-
get() = serviceMode == Key.MODE_VPN &&
157-
!(appendHttpProxy && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
156+
get() =
157+
// When the inbound is exposed to the LAN (bind 0.0.0.0 via allowAccess),
158+
// authentication is mandatory so the proxy is never an open relay. This
159+
// holds in both VPN and Proxy service modes and overrides appendHttpProxy.
160+
allowAccess ||
161+
// Loopback-only inbound: keep the prior behavior. In VPN mode the
162+
// inbound is authenticated, except when appendHttpProxy is active on
163+
// Android Q+ (the system HTTP proxy cannot supply credentials).
164+
(serviceMode == Key.MODE_VPN &&
165+
!(appendHttpProxy && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q))
158166

159167
val mixedInboundUser: String get() = if (mixedInboundAuthed) Key.MIXED_USERNAME else ""
160168
val mixedInboundPass: String get() = if (mixedInboundAuthed) mixedSecret else ""

0 commit comments

Comments
 (0)