Skip to content

Commit 0ae1752

Browse files
authored
feat: apply salt to ip hashing (#13749)
1 parent 8751450 commit 0ae1752

File tree

6 files changed

+39
-13
lines changed

6 files changed

+39
-13
lines changed

dev/environment

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ PATH=/opt/warehouse/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:
33

44
WAREHOUSE_ENV=development
55
WAREHOUSE_TOKEN=insecuretoken
6+
WAREHOUSE_IP_SALT="insecure himalayan pink salt"
67

78
AWS_ACCESS_KEY_ID=foo
89
AWS_SECRET_ACCESS_KEY=foo

tests/conftest.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ def remote_addr_hashed():
114114
return "6694f83c9f476da31f5df6bcc520034e7e57d421d247b9d34f49edbfc84a764c"
115115

116116

117+
@pytest.fixture
118+
def remote_addr_salted():
119+
"""
120+
Output of `hashlib.sha256((remote_addr + "pepa").encode("utf8")).hexdigest()`
121+
"""
122+
return "a69a49383d81404e4b1df297c7baa28e1cd6c4ee1495ed5d0ab165a63a147763"
123+
124+
117125
@pytest.fixture
118126
def jinja():
119127
dir_name = os.path.join(os.path.dirname(warehouse.__file__))
@@ -273,6 +281,7 @@ def app_config(database):
273281
settings = {
274282
"warehouse.prevent_esi": True,
275283
"warehouse.token": "insecure token",
284+
"warehouse.ip_salt": "insecure salt",
276285
"camo.url": "http://localhost:9000/",
277286
"camo.key": "insecure key",
278287
"celery.broker_url": "amqp://",

tests/unit/test_config.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ class FakeRegistry(dict):
195195
def __init__(self):
196196
self.settings = {
197197
"warehouse.token": "insecure token",
198+
"warehouse.ip_salt": "insecure salt",
198199
"warehouse.env": environment,
199200
"camo.url": "http://camo.example.com/",
200201
"pyramid.reload_assets": False,
@@ -310,7 +311,9 @@ def __init__(self):
310311
assert result is configurator_obj
311312
assert configurator_obj.set_root_factory.calls == [pretend.call(config.RootFactory)]
312313
assert configurator_obj.add_wsgi_middleware.calls == [
313-
pretend.call(ProxyFixer, token="insecure token", num_proxies=1),
314+
pretend.call(
315+
ProxyFixer, token="insecure token", ip_salt="insecure salt", num_proxies=1
316+
),
314317
pretend.call(VhmRootRemover),
315318
]
316319
assert configurator_obj.include.calls == (

tests/unit/utils/test_wsgi.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ def test_skips_headers(self):
3434
}
3535
start_response = pretend.stub()
3636

37-
resp = wsgi.ProxyFixer(app, token="1234")(environ, start_response)
37+
resp = wsgi.ProxyFixer(app, token="1234", ip_salt="pepa")(
38+
environ, start_response
39+
)
3840

3941
assert resp is response
4042
assert app.calls == [pretend.call({}, start_response)]
@@ -53,7 +55,9 @@ def test_accepts_warehouse_headers(self):
5355
}
5456
start_response = pretend.stub()
5557

56-
resp = wsgi.ProxyFixer(app, token="1234")(environ, start_response)
58+
resp = wsgi.ProxyFixer(app, token="1234", ip_salt="pepa")(
59+
environ, start_response
60+
)
5761

5862
assert resp is response
5963
assert app.calls == [
@@ -76,12 +80,14 @@ def test_missing_headers(self):
7680
environ = {"HTTP_WAREHOUSE_TOKEN": "1234"}
7781
start_response = pretend.stub()
7882

79-
resp = wsgi.ProxyFixer(app, token="1234")(environ, start_response)
83+
resp = wsgi.ProxyFixer(app, token="1234", ip_salt="pepa")(
84+
environ, start_response
85+
)
8086

8187
assert resp is response
8288
assert app.calls == [pretend.call({}, start_response)]
8389

84-
def test_accepts_x_forwarded_headers(self, remote_addr_hashed):
90+
def test_accepts_x_forwarded_headers(self, remote_addr_salted):
8591
response = pretend.stub()
8692
app = pretend.call_recorder(lambda e, s: response)
8793

@@ -93,15 +99,15 @@ def test_accepts_x_forwarded_headers(self, remote_addr_hashed):
9399
}
94100
start_response = pretend.stub()
95101

96-
resp = wsgi.ProxyFixer(app, token=None)(environ, start_response)
102+
resp = wsgi.ProxyFixer(app, token=None, ip_salt="pepa")(environ, start_response)
97103

98104
assert resp is response
99105
assert app.calls == [
100106
pretend.call(
101107
{
102108
"HTTP_SOME_OTHER_HEADER": "woop",
103109
"REMOTE_ADDR": "1.2.3.4",
104-
"REMOTE_ADDR_HASHED": remote_addr_hashed,
110+
"REMOTE_ADDR_HASHED": remote_addr_salted,
105111
"HTTP_HOST": "example.com",
106112
"wsgi.url_scheme": "http",
107113
},
@@ -116,14 +122,16 @@ def test_skips_x_forwarded_when_not_enough(self):
116122
environ = {"HTTP_X_FORWARDED_FOR": "1.2.3.4", "HTTP_SOME_OTHER_HEADER": "woop"}
117123
start_response = pretend.stub()
118124

119-
resp = wsgi.ProxyFixer(app, token=None, num_proxies=2)(environ, start_response)
125+
resp = wsgi.ProxyFixer(app, token=None, ip_salt=None, num_proxies=2)(
126+
environ, start_response
127+
)
120128

121129
assert resp is response
122130
assert app.calls == [
123131
pretend.call({"HTTP_SOME_OTHER_HEADER": "woop"}, start_response)
124132
]
125133

126-
def test_selects_right_x_forwarded_value(self, remote_addr_hashed):
134+
def test_selects_right_x_forwarded_value(self, remote_addr_salted):
127135
response = pretend.stub()
128136
app = pretend.call_recorder(lambda e, s: response)
129137

@@ -135,15 +143,17 @@ def test_selects_right_x_forwarded_value(self, remote_addr_hashed):
135143
}
136144
start_response = pretend.stub()
137145

138-
resp = wsgi.ProxyFixer(app, token=None, num_proxies=2)(environ, start_response)
146+
resp = wsgi.ProxyFixer(app, token=None, ip_salt="pepa", num_proxies=2)(
147+
environ, start_response
148+
)
139149

140150
assert resp is response
141151
assert app.calls == [
142152
pretend.call(
143153
{
144154
"HTTP_SOME_OTHER_HEADER": "woop",
145155
"REMOTE_ADDR": "1.2.3.4",
146-
"REMOTE_ADDR_HASHED": remote_addr_hashed,
156+
"REMOTE_ADDR_HASHED": remote_addr_salted,
147157
"HTTP_HOST": "example.com",
148158
"wsgi.url_scheme": "http",
149159
},

warehouse/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ def configure(settings=None):
153153

154154
# Pull in default configuration from the environment.
155155
maybe_set(settings, "warehouse.token", "WAREHOUSE_TOKEN")
156+
maybe_set(settings, "warehouse.ip_salt", "WAREHOUSE_IP_SALT")
156157
maybe_set(settings, "warehouse.num_proxies", "WAREHOUSE_NUM_PROXIES", int)
157158
maybe_set(settings, "warehouse.domain", "WAREHOUSE_DOMAIN")
158159
maybe_set(settings, "forklift.domain", "FORKLIFT_DOMAIN")
@@ -671,6 +672,7 @@ def configure(settings=None):
671672
config.add_wsgi_middleware(
672673
ProxyFixer,
673674
token=config.registry.settings["warehouse.token"],
675+
ip_salt=config.registry.settings["warehouse.ip_salt"],
674676
num_proxies=config.registry.settings.get("warehouse.num_proxies", 1),
675677
)
676678

warehouse/utils/wsgi.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ def _forwarded_value(values, num_proxies):
4141

4242

4343
class ProxyFixer:
44-
def __init__(self, app, token, num_proxies=1):
44+
def __init__(self, app, token, ip_salt: str, num_proxies=1):
4545
self.app = app
4646
self.token = token
47+
self.ip_salt = ip_salt
4748
self.num_proxies = num_proxies
4849

4950
def __call__(self, environ, start_response):
@@ -73,7 +74,7 @@ def __call__(self, environ, start_response):
7374
environ.get("HTTP_X_FORWARDED_FOR", ""), self.num_proxies
7475
)
7576
remote_addr_hashed = (
76-
hashlib.sha256(remote_addr.encode("utf8")).hexdigest()
77+
hashlib.sha256((remote_addr + self.ip_salt).encode("utf8")).hexdigest()
7778
if remote_addr
7879
else ""
7980
)

0 commit comments

Comments
 (0)