Skip to content

Commit

Permalink
Add content signature to GMP response (#2179)
Browse files Browse the repository at this point in the history
* Add content signature to GMP response

* Add content signature to GMP response

* Allow distinct autograph url for gmp requests

* Allow for per-product autograph servers
  • Loading branch information
gbrownmozilla authored Sep 14, 2021
1 parent 9d8b100 commit 5380772
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 20 deletions.
4 changes: 3 additions & 1 deletion src/auslib/web/public/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from auslib.blobs.base import createBlob
from auslib.global_state import dbo
from auslib.services import releases
from auslib.web.public.helpers import AUS, get_aus_metadata_headers, with_transaction
from auslib.web.public.helpers import AUS, get_aus_metadata_headers, get_content_signature_headers, with_transaction

try:
from urllib import unquote
Expand Down Expand Up @@ -230,6 +230,8 @@ def get_update_blob(transaction, **url):
response = make_response(xml)
response.headers["Cache-Control"] = app.cacheControl
response.headers.extend(get_aus_metadata_headers(eval_metadata))
if query["product"] in app.config.get("CONTENT_SIGNATURE_PRODUCTS", []):
response.headers.extend(get_content_signature_headers(xml, query["product"]))
response.mimetype = "text/xml"
return response

Expand Down
30 changes: 29 additions & 1 deletion src/auslib/web/public/helpers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import logging
from functools import wraps

from flask import current_app as app

from auslib.AUS import AUS
from auslib.global_state import dbo
from auslib.global_state import cache, dbo
from auslib.util.autograph import make_hash, sign_hash

log = logging.getLogger(__name__)


def with_transaction(f):
Expand All @@ -21,4 +27,26 @@ def get_aus_metadata_headers(eval_metadata):
return headers


def get_content_signature_headers(content, product):
headers = {}
if product:
product += "_"
if app.config.get("AUTOGRAPH_%sURL" % product):
hash_ = make_hash(content)

def sign():
return sign_hash(
app.config["AUTOGRAPH_%sURL" % product],
app.config["AUTOGRAPH_%sKEYID" % product],
app.config["AUTOGRAPH_%sUSERNAME" % product],
app.config["AUTOGRAPH_%sPASSWORD" % product],
hash_,
)

signature, x5u = cache.get("content_signatures", hash_, sign)
headers = {"Content-Signature": f"x5u={x5u}; p384ecdsa={signature}"}
log.debug("Added header: %s" % headers)
return headers


AUS = AUS()
15 changes: 2 additions & 13 deletions src/auslib/web/public/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
from flask import current_app as app

from auslib.AUS import FORCE_FALLBACK_MAPPING, FORCE_MAIN_MAPPING
from auslib.global_state import cache
from auslib.util.autograph import make_hash, sign_hash
from auslib.web.public.helpers import AUS, get_aus_metadata_headers, with_transaction
from auslib.web.public.helpers import AUS, get_aus_metadata_headers, get_content_signature_headers, with_transaction


@with_transaction
Expand All @@ -21,15 +19,6 @@ def get_update(transaction, **parameters):

response = json.dumps(release.getResponse(parameters, app.config["ALLOWLISTED_DOMAINS"]))

if app.config.get("AUTOGRAPH_URL"):
hash_ = make_hash(response)

def sign():
return sign_hash(
app.config["AUTOGRAPH_URL"], app.config["AUTOGRAPH_KEYID"], app.config["AUTOGRAPH_USERNAME"], app.config["AUTOGRAPH_PASSWORD"], hash_
)

signature, x5u = cache.get("content_signatures", hash_, sign)
headers["Content-Signature"] = f"x5u={x5u}; p384ecdsa={signature}"
headers.update(get_content_signature_headers(response, ""))

return Response(response=response, status=200, headers=headers, mimetype="application/json")
25 changes: 25 additions & 0 deletions tests/web/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def setup(self, insert_release, firefox_54_0_1_build1, firefox_56_0_build1, supe
"ftp.mozilla.org": ("SystemAddons",),
}
app.config["VERSION_FILE"] = self.version_file
app.config["CONTENT_SIGNATURE_PRODUCTS"] = ["gmp"]
with open(self.version_file, "w+") as f:
f.write(
"""
Expand Down Expand Up @@ -895,6 +896,21 @@ def setup(self, insert_release, firefox_54_0_1_build1, firefox_56_0_build1, supe
os.remove(self.version_file)


@pytest.fixture(scope="function")
def mock_autograph(monkeypatch):
monkeypatch.setitem(app.config, "AUTOGRAPH_gmp_URL", "fake")
monkeypatch.setitem(app.config, "AUTOGRAPH_gmp_KEYID", "fake")
monkeypatch.setitem(app.config, "AUTOGRAPH_gmp_USERNAME", "fake")
monkeypatch.setitem(app.config, "AUTOGRAPH_gmp_PASSWORD", "fake")

def mockreturn(*args):
return ("abcdef", "https://this.is/a.x5u")

import auslib.web.public.helpers

monkeypatch.setattr(auslib.web.public.helpers, "sign_hash", mockreturn)


class ClientTest(ClientTestBase):
def testGetHeaderArchitectureWindows(self):
self.assertEqual(client_api.getHeaderArchitecture("WINNT_x86-msvc", "Firefox Intel Windows"), "Intel")
Expand Down Expand Up @@ -1270,6 +1286,15 @@ def testDeprecatedEsrVersionStyleGetsUpdates(self):
""",
)

def testGMPResponseWithoutSigning(self):
ret = self.client.get("/update/4/gmp/1.0/1/p/l/a/a/a/a/1/update.xml")
assert "Content-Signature" not in ret.headers

@pytest.mark.usefixtures("mock_autograph")
def testGMPResponseWithSigning(self):
ret = self.client.get("/update/4/gmp/1.0/1/p/l/a/a/a/a/1/update.xml")
assert ret.headers["Content-Signature"] == "x5u=https://this.is/a.x5u; p384ecdsa=abcdef"

def testGetWithResponseProducts(self):
ret = self.client.get("/update/4/gmp/1.0/1/p/l/a/a/a/a/1/update.xml")
self.assertUpdateEqual(
Expand Down
4 changes: 2 additions & 2 deletions tests/web/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ def mock_autograph(monkeypatch):
def mockreturn(*args):
return ("abcdef", "https://this.is/a.x5u")

import auslib.web.public.json
import auslib.web.public.helpers

monkeypatch.setattr(auslib.web.public.json, "sign_hash", mockreturn)
monkeypatch.setattr(auslib.web.public.helpers, "sign_hash", mockreturn)


@pytest.fixture(scope="module")
Expand Down
17 changes: 14 additions & 3 deletions uwsgi/public.wsgi
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,20 @@ if os.environ.get("AUTOGRAPH_URL"):
application.config["AUTOGRAPH_PASSWORD"] = os.environ["AUTOGRAPH_PASSWORD"]

# Autograph responses
# When we start signing things other than Guardian responses we'll need to increase the size of this cache.
# If additional types of responses require signing, consider increasing the size of this cache.
# We cache for one day to make sure we resign once per day, because the signatures eventually expire.
cache.make_cache("content_signatures", 50, 86400)

cache.make_cache("content_signatures", 200, 86400)

if os.environ.get("AUTOGRAPH_GMP_URL"):
application.config["AUTOGRAPH_GMP_URL"] = os.environ["AUTOGRAPH_GMP_URL"]
application.config["AUTOGRAPH_GMP_KEYID"] = os.environ["AUTOGRAPH_GMP_KEYID"]
application.config["AUTOGRAPH_GMP_USERNAME"] = os.environ["AUTOGRAPH_GMP_USERNAME"]
application.config["AUTOGRAPH_GMP_PASSWORD"] = os.environ["AUTOGRAPH_GMP_PASSWORD"]
elif "AUTOGRAPH_URL" in application.config:
application.config["AUTOGRAPH_GMP_URL"] = application.config["AUTOGRAPH_URL"]
application.config["AUTOGRAPH_GMP_KEYID"] = application.config["AUTOGRAPH_KEYID"]
application.config["AUTOGRAPH_GMP_USERNAME"] = application.config["AUTOGRAPH_USERNAME"]
application.config["AUTOGRAPH_GMP_PASSWORD"] = application.config["AUTOGRAPH_PASSWORD"]

cache.make_cache("blob", 500, 3600)
cache.make_cache("releases", 500, 3600)
Expand Down Expand Up @@ -83,6 +93,7 @@ application.config["SPECIAL_FORCE_HOSTS"] = SPECIAL_FORCE_HOSTS
# about the current code (version number, commit hash), but doesn't exist in
# the repo itself
application.config["VERSION_FILE"] = "/app/version.json"
application.config["CONTENT_SIGNATURE_PRODUCTS"] = ["GMP"]

if os.environ.get("SENTRY_DSN"):
sentry_sdk.init(os.environ["SENTRY_DSN"], integrations=[FlaskIntegration(), LoggingIntegration()])
Expand Down

0 comments on commit 5380772

Please sign in to comment.