mirror of
https://github.com/quay/quay.git
synced 2026-01-26 06:21:37 +03:00
This adds the optimization in CloudFlare where if a request is from the primary region then instead of redirecting to the CDN, we return the S3 URL to save egress cost
120 lines
4.3 KiB
Python
120 lines
4.3 KiB
Python
import base64
|
|
import logging
|
|
import urllib.parse
|
|
from datetime import datetime, timedelta
|
|
from functools import lru_cache
|
|
|
|
from cryptography.hazmat.backends import default_backend
|
|
from cryptography.hazmat.primitives import hashes, serialization
|
|
from cryptography.hazmat.primitives.asymmetric import padding
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
from storage.cloud import S3Storage, is_in_network_request
|
|
|
|
|
|
class CloudFlareS3Storage(S3Storage):
|
|
"""
|
|
CloudFlare CDN backed by S3 storage
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
context,
|
|
cloudflare_domain,
|
|
cloudflare_privatekey_filename,
|
|
storage_path,
|
|
s3_bucket,
|
|
s3_region,
|
|
*args,
|
|
**kwargs,
|
|
):
|
|
super(CloudFlareS3Storage, self).__init__(
|
|
context, storage_path, s3_bucket, s3_region=s3_region, *args, **kwargs
|
|
)
|
|
|
|
self.context = context
|
|
self.cloudflare_domain = cloudflare_domain
|
|
self.cloudflare_privatekey = self._load_private_key(cloudflare_privatekey_filename)
|
|
self.region = s3_region
|
|
|
|
def get_direct_download_url(
|
|
self, path, request_ip=None, expires_in=60, requires_cors=False, head=False, **kwargs
|
|
):
|
|
# If CloudFlare could not be loaded, fall back to normal S3.
|
|
s3_presigned_url = super(CloudFlareS3Storage, self).get_direct_download_url(
|
|
path, request_ip, expires_in, requires_cors, head
|
|
)
|
|
logger.debug(f"s3 presigned_url: {s3_presigned_url}")
|
|
if self.cloudflare_privatekey is None or request_ip is None:
|
|
return s3_presigned_url
|
|
|
|
logger.debug('Got direct download request for path "%s" with IP "%s"', path, request_ip)
|
|
|
|
if is_in_network_request(self._context, request_ip, self.region):
|
|
logger.debug("Request is from within the network, returning S3 URL")
|
|
return s3_presigned_url
|
|
|
|
s3_url_parsed = urllib.parse.urlparse(s3_presigned_url)
|
|
|
|
cf_url_parsed = s3_url_parsed._replace(netloc=self.cloudflare_domain)
|
|
|
|
expire_date = datetime.now() + timedelta(seconds=expires_in)
|
|
signed_url = self._cf_sign_url(cf_url_parsed, date_less_than=expire_date, **kwargs)
|
|
logger.debug(
|
|
'Returning CloudFlare URL for path "%s" with IP "%s": %s',
|
|
path,
|
|
request_ip,
|
|
signed_url,
|
|
)
|
|
return signed_url
|
|
|
|
def _cf_sign_url(self, cf_url_parsed, date_less_than, **kwargs):
|
|
expiry_ts = date_less_than.timestamp()
|
|
sign_data = "%s@%d" % (cf_url_parsed.path, expiry_ts)
|
|
signature = self.cloudflare_privatekey.sign(
|
|
sign_data.encode("utf8"), padding.PKCS1v15(), hashes.SHA256()
|
|
)
|
|
signature_b64 = base64.b64encode(signature)
|
|
|
|
return self._build_signed_url(cf_url_parsed, signature_b64, date_less_than, **kwargs)
|
|
|
|
def _build_signed_url(self, cf_url_parsed, signature, expiry_date, **kwargs):
|
|
query = cf_url_parsed.query
|
|
url_dict = dict(urllib.parse.parse_qsl(query))
|
|
params = {
|
|
"cf_sign": signature,
|
|
"cf_expiry": "%d" % expiry_date.timestamp(),
|
|
"region": self.region,
|
|
}
|
|
# Additional params for usage calculation. They are removed
|
|
# from the URL before sending to S3 by the CloudFlare worker
|
|
if kwargs.get("namespace"):
|
|
params["namespace"] = kwargs.get("namespace")
|
|
if kwargs.get("username"):
|
|
params["username"] = kwargs.get("username")
|
|
if kwargs.get("repo_name"):
|
|
params["repo_name"] = kwargs.get("repo_name")
|
|
|
|
url_dict.update(params)
|
|
url_new_query = urllib.parse.urlencode(url_dict)
|
|
url_parse = cf_url_parsed._replace(query=url_new_query)
|
|
return urllib.parse.urlunparse(url_parse)
|
|
|
|
@lru_cache(maxsize=1)
|
|
def _load_private_key(self, cloudfront_privatekey_filename):
|
|
"""
|
|
Returns the private key, loaded from the config provider, used to sign direct download URLs
|
|
to CloudFront.
|
|
"""
|
|
if self._context.config_provider is None:
|
|
return None
|
|
|
|
with self._context.config_provider.get_volume_file(
|
|
cloudfront_privatekey_filename,
|
|
mode="rb",
|
|
) as key_file:
|
|
return serialization.load_pem_private_key(
|
|
key_file.read(), password=None, backend=default_backend()
|
|
)
|