-
-
Notifications
You must be signed in to change notification settings - Fork 865
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom domain and query string auth #165
Comments
Technically the comments in settings imply this "just use this domain plus the path", but I agree that this is an issue that needs to be addressed. |
Are folks just not using auth with custom domains then? |
Probably. I'm running two different s3 locations for static and media files and Django settings are rarely well documented, so I'm not really sure what settings are being used for what. I think it might be possible to adjust MEDIA_URL or STATIC_URL without setting AWS_S3_CUSTOM_DOMAIN and have it work. I had to set STATIC_URL for django.contrib.staticfiles, but I went with the default domain for other media instead of messing with MEDIA_URL. |
Hi! |
If a |
@vchrisb Exactly the boto backend you are pointing to doesn't make it possible to use signed URLs with custom domain, although it is something possible (see for instance http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned-policy.html#private-content-creating-signed-url-canned-policy-procedure, paragraph 2 with custom domain examples) |
This was my hacky solution below: """
Note that below for media file objects I requested they not have a custom domain.
This is because they need to be signed in order to be viewed.
Django-Storages only supports signing on files not set with a custom domain
"""
class StaticStorage(S3Boto3Storage):
default_acl = None # don't want any ACL applied ever.
location = settings.AWS_STATIC_LOCATION
class PublicMediaStorage(S3Boto3Storage):
location = settings.AWS_PUBLIC_MEDIA_LOCATION
file_overwrite = False # use case important
default_acl = None # don't want any ACL applied ever.
custom_domain = "" |
Depending what people are looking for (this is an old issue), the fix in #587 supports RSA signing as required in the document @tchaumeny linked to. |
I think #839 will close this. |
I'm having a problem related to this: I'm trying to load Amazon S3 assets over IPv6. Per their documentation, it's a simple matter of making the request to a different url: I was hoping to achieve this by simply tweaking this line in
But unfortunately it ignores the custom domain for private files. The fix in #587 (documented in #900) is specific to Cloudfront, which I'm not using. Edit
Code in contextIncludes following steps from this blog post. class PrivateMediaStorage(S3Boto3Storage):
location = settings.AWS_PRIVATE_MEDIA_LOCATION
default_acl = 'private'
file_overwrite = False
custom_domain = False
"""
Overriding the old `url` function to find & replace a dual-stack endpoint in the URL
"""
def url(self, name, parameters=None, expire=None):
# Preserve the trailing slash after normalizing the path.
name = self._normalize_name(self._clean_name(name))
if self.custom_domain:
return "{}//{}/{}".format(self.url_protocol,
self.custom_domain, filepath_to_uri(name))
if expire is None:
expire = self.querystring_expire
params = parameters.copy() if parameters else {}
params['Bucket'] = self.bucket.name
params['Key'] = self._encode_name(name)
url = self.bucket.meta.client.generate_presigned_url('get_object', Params=params,
ExpiresIn=expire)
url = url.replace("s3.amazonaws.com", "s3.dualstack.us-east-1.amazonaws.com")
if self.querystring_auth:
return url
return self._strip_signing_parameters(url) |
So that's definitely an interesting use case, and your solution of subclassing and overriding and replacing seems like it should work fine for the short term. It does seem like @cpoetter 's #839 would be close to what you need, but it would need to be adjusted to fix merge conflicts. This also points out that there are not enough configuration parameters and the change made in #885 although needed to be done, probably should have created an additional parameter (or maybe they had this same problem here, but they were working around it in a different way(?)). The states I see are:
Unfortunately as written now the cloudfront auth is only accessible on the custom domain path (since you'll be specifying a domain that makes sense), but the query string auth is not enabled on that path, and re-enabling it will require making sure you can easily understand why the code is using the different states above (since it's not just the two states of query string or not in the other code path) |
@AllenEllis Thanks for the code snippet. I built on it to make it a bit more forward compatible: from storages.backends.s3boto3 import S3Boto3Storage
from django.conf import settings
class PrivateMediaStorage(S3Boto3Storage):
"""Extend S3 with signed URLs for custom domains."""
custom_domain = False
def url(self, name, parameters=None, expire=None, http_method=None):
"""Replace internal domain with custom domain for signed URLs."""
url = super().url(name, parameters, expire, http_method)
custom_url = url.replace(
settings.AWS_S3_ENDPOINT_URL,
f"{settings.AWS_S3_URL_PROTOCOL}//{settings.AWS_S3_CUSTOM_DOMAIN}",
)
return custom_url |
Thanks @moritz89 for the code. custom_domain = False
def url(self, name, parameters=None, expire=None, http_method=None):
url = super().url(name, parameters, expire, http_method)
custom_url = url.replace(
settings.AWS_S3_ENDPOINT_URL,
f"https://",
)
return custom_url |
I've got a slightly improved version that I ended up using as a mixin. class CustomDomainFixedUpS3Boto3Storage(S3Boto3Storage):
# work around for url + custom domains not working for pre-signed urls.
# see: https://github.com/jschneier/django-storages/issues/165#issuecomment-810166563
# adapted to preserve the inputs we would expect to use if this were fixed upstream.
x_custom_domain = None
x_url_protocol = "https:"
def url(self, name, parameters=None, expire=None, http_method=None):
"""Replace internal domain with custom domain for signed URLs."""
url = super().url(name, parameters, expire, http_method)
if self.x_custom_domain:
return url.replace(
self.endpoint_url,
f"{self.x_url_protocol}//{self.x_custom_domain}",
)
return url |
I was going crazy trying to understand why the solutions provided by #165 (comment) and #165 (comment) weren't working for me. Turns out, setting your <Error>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your key and signing method.
</Message>
<Key>path/to/file</Key>
<BucketName>bucket</BucketName>
<Resource>/bucket/path/to/file</Resource>
<RequestId>1234567890</RequestId>
<HostId>abcdefghi123456789</HostId>
</Error> Since the current Boto3 version (1.34.31 as of this comment) sets the default My use case was running a Django app and a MinIO service in a Docker Compose project, and having them share a network. I could reference the MinIO service from the app container like Something to take into consideration is that the signature versions are not backwards compatible so be careful about url endpoints if making this change for legacy projects. The Signature Version 2 has been marked off as legacy by AWS, and some regions no longer support it. Since I only needed to solve this issue for a local project, it didn't feel wrong using the legacy version though! |
Given this is over 8 years old, I assume this will never be fixed. |
@exp0nge Please don't lose hope. There are 8 years of patience here... re-open and prioritize it please. |
@exp0nge I'd keep it open and lead it to a maintainer to close if they don't think it's something they want on their roadmap. I maintain a number of projects. I don't have time to get to every feature request, but I find open feature requests can be good tasks for new contributors or Google Summer of Code. I prefer they're left open to inspire people who want to get involved and do something that people want. It's OSS so patience is the game if you're not in a position to write the PR yourself. |
I definitely understand you very well, and I realize that most of the time AWS is used, but it is critical to have this feature to be on an equal footing with other providers and make Django the choice for them. Auth is a must, especially for private buckets. On the other hand, I see that there are a lot of pending PRs, if you want me to help, I would be happy to review the PRs if you assign me as a maintainer. |
I see that the query string auth feature does not work when a custom domain is provided. At least that's what it seems like from the source. Is this intentional? Is there a workaround to allow both features to work?
The text was updated successfully, but these errors were encountered: