feat: add per-listener SOCKS5 server authentication (RFC 1929)
Per-listener username/password auth via `auth:` config key. When set, clients must negotiate method 0x02 and pass RFC 1929 subnegotiation; no-auth (0x00) is rejected to prevent downgrade. Listeners without `auth` keep current no-auth behavior. Includes auth_failures metric, API integration (/status auth flag, /config auth_users count without exposing passwords), config parsing with YAML int coercion, integration tests (success, failure, method rejection, no-auth unchanged), and documentation updates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -593,6 +593,61 @@ class TestListenerPoolCompat:
|
||||
assert lc.pool_hops == 0
|
||||
|
||||
|
||||
class TestAuthConfig:
|
||||
"""Test auth field in listener config."""
|
||||
|
||||
def test_auth_from_yaml(self, tmp_path):
|
||||
cfg_file = tmp_path / "test.yaml"
|
||||
cfg_file.write_text(
|
||||
"listeners:\n"
|
||||
" - listen: 1080\n"
|
||||
" auth:\n"
|
||||
" alice: s3cret\n"
|
||||
" bob: hunter2\n"
|
||||
)
|
||||
c = load_config(cfg_file)
|
||||
assert c.listeners[0].auth == {"alice": "s3cret", "bob": "hunter2"}
|
||||
|
||||
def test_auth_empty_default(self):
|
||||
lc = ListenerConfig()
|
||||
assert lc.auth == {}
|
||||
|
||||
def test_auth_absent_from_yaml(self, tmp_path):
|
||||
cfg_file = tmp_path / "test.yaml"
|
||||
cfg_file.write_text(
|
||||
"listeners:\n"
|
||||
" - listen: 1080\n"
|
||||
)
|
||||
c = load_config(cfg_file)
|
||||
assert c.listeners[0].auth == {}
|
||||
|
||||
def test_auth_numeric_password(self, tmp_path):
|
||||
"""YAML parses `admin: 12345` as int; must be coerced to str."""
|
||||
cfg_file = tmp_path / "test.yaml"
|
||||
cfg_file.write_text(
|
||||
"listeners:\n"
|
||||
" - listen: 1080\n"
|
||||
" auth:\n"
|
||||
" admin: 12345\n"
|
||||
)
|
||||
c = load_config(cfg_file)
|
||||
assert c.listeners[0].auth == {"admin": "12345"}
|
||||
|
||||
def test_auth_mixed_listeners(self, tmp_path):
|
||||
"""One listener with auth, one without."""
|
||||
cfg_file = tmp_path / "test.yaml"
|
||||
cfg_file.write_text(
|
||||
"listeners:\n"
|
||||
" - listen: 1080\n"
|
||||
" auth:\n"
|
||||
" alice: pass\n"
|
||||
" - listen: 1081\n"
|
||||
)
|
||||
c = load_config(cfg_file)
|
||||
assert c.listeners[0].auth == {"alice": "pass"}
|
||||
assert c.listeners[1].auth == {}
|
||||
|
||||
|
||||
class TestBypassConfig:
|
||||
"""Test bypass rules in listener config."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user