Is it possible to change Amazon S3 response header `Server`? - amazon-web-services

From AWS S3, I can see that Server is set to AmazonS3.
I'm wondering if it's possible to change that to other name?
Thanks.

Yes it is, please see these blogposts 1, 2.
https://nvisium.com/resources/blog/2017/08/10/lambda-edge-cloudfront-custom-headers.html
http://lukaszielinski.de/blog/posts/2018/07/10/aws-how-to-change-s3-response-headers/

Edit to update 2019 - please see #scrrr's answer below for a more up to date answer since the introduction of Lambda # Edge (released a year after the answer below): https://stackoverflow.com/a/51269162/6720449
As far as I've seen, it's not possible to change it intrinsically with S3. See Common Response Headers for a list of headers.
Regarding Server header it says:
The name of the server that created the response.
Type: String
Default: AmazonS3
If you try and modify an objects header in the S3 console, the only headers you can change are:
Cache-Control
Content-Disposition
Content-Type
Content-Language
Expires
Content-Encoding
Website Redirect Location
Also, you can specify custom headers if they start with x-amz-meta-.
If you need to present a different Server header, you will need to wrap access to S3 in a custom reverse proxy with header configurability - e.g. nginx on an ec2 instance.

Related

Simplest way to host a static webpage in AWS with a single case-sensitive response header

I'm trying to create a simple webpage for a ConnMan connectivity check from some embedded devices. This library is very finicky about certain things.
The body must be: <html>\n<head>\n</head>\n<body>\n</body>\n</html>\n If anything, even the trailing newline at the end is missing, it fails.
The other component is a response header of X-ConnMan-Status: online. Normally response headers are case insensitive, but ConnMan fails if case is changed at all. This is a seemingly unimportant detail, until you try to host it with AWS.
My first thought was CloudFront (CDN) as it is static content. I also would like to find a serverless solution, as I don't want to manage EC2 instances or K8s pods. I setup a CloudFront distribution with an S3 bucket as the backend to host the simple HTML response. This works great.
I then started exploring various ways of adding the response header. So far I have tried:
CloudFront Function to add the header
CloudFront Response Header Policy to add the header
Lambda#edge function to add the header
All work to add the response header, but Amazon has some crazy obsession with converting the header names to lower case and I need it to be X-ConnMan-Status not x-connman-status. Is there a simple way to host this in AWS where it won't lower case the response header name?
I eventually realized that the lower casing of the header names was due to HTTP/2: https://httpwg.org/specs/rfc7540.html#HttpHeaders
header field names MUST be converted to lowercase prior to their encoding in HTTP/2. A request or response containing uppercase header field names MUST be treated as malformed
Disabling HTTP/2 support on the CloudFront distribution, or requesting HTTP/1.1 with the client, preserves the header casing.

API Gateway configuration returns 403

I have an API Gateway configured and deployed. If I make a GET request to one of its staged endpoints, for example https://1234567890.execute-api.us-east-1.amazonaws.com/dev/doc, I get a 200 OK response.
If I take a look at the Custom Domain Names section and supplant the URL found there into my request, for example abcdefghijkl-f4cwy0d1u5.execute-api.us-east-1.amazonaws.com to make https://abcdefghijkl-f4cwy0d1u5.execute-api.us-east-1.amazonaws.com/dev/doc, I get 403 Forbidden.
Am I wrong in thinking that I should be able to make a request to the domain name - and thus use the API's Custom domain name in a CNAME record - or does the 403 indicate that a specific configuration item is missing?
you can find some response headers that come together with your 403 error here: https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-troubleshoot-403-forbidden/
this might help you to find which error you are facing!
TL;DR: When getting 403 Forbidden with API Gateway and using the Custom domain name it's important to trim the stage name because API Gateway is routing the custom name to that stage.
Using the documentation provided by #leoandreotti I was able to identify the response header:
x-amzn-ErrorType: ForbiddenException
For this, the documentation states:
Invoking a REST API that has a custom domain name using the default
execute-api endpoint - The caller uses the default execute-api
endpoint to invoke a REST API after disabling the default endpoint.
This made me think back to a header I had been recommended to use by a colleague - the Host header.
So, I added the header back into the request and got this:
x-amzn-ErrorType: MissingAuthenticationTokenException
For which the docs state:
Resource path doesn't exist - A request with no "Authorization" header
is sent to an API resource path that doesn't exist.
But the path /dev/doc absolutely does exist. Then I realised that the /dev portion is actually the stage name.
So I trimmed the /dev portion from the path and got 200 OK - then I removed the Host header and also got 200 OK!
Thanks #leoandreotti

API Gateway CORS Issue

So I have CORS enabled going through the basic setup given by AWS Gateway. However for this API I need to allow Control Origins for all requests and allow credentials.
Here is what it looks like
The issue as you may have guessed is this setup is not allowed by CORS, you can not have a wildcard for Origin and have credentials as true. Normally the work around for this is to just grab the requesting domain and add it into the Origin Header. Which is more or less what I want to do. But I don't know how to get that information and add it as a mapping value. Where does API Gateway store that information and how do i get it?
UPDATE:
I have to pass through HTTP Header Host to my Lambda Function which I should have mentioned earlier, I have tried implementing the Answer below but I cannot access the header to pass it to the Lambda function using the instructions provided. Any more assistance with this is greatly appreciated.
Okay After hours of research and finding bits of information across the internet I have a solution and hopefully it is useful for other people.
To pass an HTTP Header that is not a default value provided by AWS API Gateway and then access that data via a Lambda Function and return that data in the Response Header follow the steps below
In "Method Request" go to "HTTP Request Headers" and add your desired header to capture. ie. if we want to get the host value of the API url you can enter "Host" here. if you want to get the website host of the caller use "Origin"
In "Integration Request" go to mapping templates and create a new template if an "application/json" does not exist, if it does just update it.
This is the important part, pass the header value you set in step 1. To do that write something similar to the following in the Template Box.
{
"origin" : "$input.params().header.Origin",
"host" : "$input.params().header.Host"
}
You can also pass in any url parameters you have defined in the same JSON.
Access the data from Lambda, The integration request passed the information into the "Event" parameter if using Node as the Lambda Backend code. to retrieve the value of any header just use the following within your handler.
event.origin;
When sending back your response from Lambda to API Gateway, it is best to format the response in JSON. Something similar to this.
{
"origin" : event.origin,
"host" : event.host,
"nonHeaderOutput" : "Hello World"
}
In "Integration Response" go to "Header Mappings", if the header you need is not listed you may add it in "Method Response" and it will then appear here. For this example I used "Access-Control-Allow-Origin" and edited the Mapping Value to be integration.response.body.origin
now go to "Mapping Templates and select the content type you want to use, and then edit the template to access the non header responses by adding this to the Template Box
$input.path("$.nonHeaderOutput")
That is it now the header that was sent to the API can be used in your method Response.
Jurgen from API Gateway here.
Thanks for reporting this issue. There is currently no simple way to set it up via the "Enable CORS" feature in the console. However, we are looking into it to improve the user experience for this use case.
As a potential workaround, you could passthrough the Origin header from the client to your backend and parse / create the value for the Access-Control-Allow-Origin header there. Then you would map the Header in the Integration Response from 'integration.response.header.Access-Control-Allow-Origin' to Access-Control-Allow-Origin and return it to the client.
Hope this helps.
Best,
Jurgen

Being able to download, not just stream files, from Amazon S3

I have Amazon S3 where all of my files are stored. Currently my users can go to a link where they can stream, but not download, audio and video files. How can I set up a link through either Amazon S3 or perhaps Amazon CloudFront that will allow someone to download an MP3 file or something of that nature?
Thanks for any advice!
You must set the file's content header to something other than the media type the browser understands. For example:
Content-Disposition: attachment; filename=FILENAME.EXT
Content-Type: application/octet-stream
This used to be a big issue if you wanted to have both features (ability to display/view and ability to download) and you used to have to proxy the file download through your EC2 or other annoying ways. Now S3 has it built in:
http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html
You can override values for a set of response headers using the query
parameters listed in the following table. These response header values
are only sent on a successful request, that is, when status code 200
OK is returned. The set of headers you can override using these
parameters is a subset of the headers that Amazon S3 accepts when you
create an object. The response headers that you can override for the
GET response are Content-Type, Content-Language, Expires,
Cache-Control, Content-Disposition, and Content-Encoding. To override
these header values in the GET response, you use the request
parameters described in the following table. (linke above)

Do not propagate headers on HTTP redirects

I have files hosted on Amazon S3, and I'd like to download them after a treatment in my app. This app view requires the HTTP Authorization header to proceed. Here is the process:
Query view /file/xxx with the required Authorization request header
If the app access is granted, does some treatment
Generate a signed S3 url, and redirect to it
The fact is that the request header is also propagated on the redirect, and is in conflict with Amazon's S3 signature, I have the following error message:
Either the Signature query string parameter or the Authorization header should be specified, not both
So, is there a way to not propagate the Authorization header to S3 ?
Note that I have tested all 3xx HTTP codes. Thanks in advance.
It may depend on your client, for example see: https://code.google.com/p/go/issues/detail?id=4800
It appears curl won't carry over your Authorization header by default. But I'm dealing with a similar issue and I see that by default http components appears to carry it.
Is your S3 file path on the same domain as your /file/xxx? That would explain why the Authorization header is being sent, presumably. If you have your S3 hosted on a different domain to the app view, then I would assume the Authorization header would not be sent to the S3 domain (as HTTP Authorization is per-domain).