Meanings of parameters when generating pre-signed URL using boto S3 - python

I am using boto to create pre-signed URLs to permission users to upload directly to S3.
I know that we can use generate_url method (available both for Connection and Bucket classes) for this, but it's not clear to me what some of the available parameters such as headers, response_headers, and force_http specifically mean in that method.
I guess that headers are the headers in the request to the URL generation? And response_headers are the ones that will be in the response when the file is downloaded?
As for force_http, which connection is it for? Is it the connection between my application and aws, or the connection between the uploading client and aws?

force_http
will force non SSL http connection (e.g. not https). This is for the resulting URL
headers (e.g. generate_url(headers='X-Forward-For: 1.2.3.4')
will set the headers for the request, maybe passing a aws token for auth or something
response_headers
will override the default response headers from the S3 object, maybe you want to have a header in the response that your receiving client will interpret.
Details can be seen here: http://boto.readthedocs.org/en/latest/ref/gs.html?highlight=generate_url#boto.gs.key.Key.generate_url

Related

Python - Requests Library - How to ensure HTTPS requests

This is probably a dumb question, but I just want to make sure with the below.
I am currently using the requests library in python. I am using this to call an external API hosted on Azure cloud.
If I use the requests library from a virtual machine, and the requests library sends to URL: https://api-management-example/run, does that mean my communication to this API, as well as the entire payload I send through is secure? I have seen in my Python site-packages in my virtual environment, there is a cacert.pem file. Do I need to update that at all? Do I need to do anything else on my end to ensure the communication is secure, or the fact that I am calling the HTTPS URL means it is secure?
Any information/guidance would be much appreciated.
Thanks,
A HTTPS is secure with valid signed certificate. Some people use self signed certificate to maintain HTTPS. In requests library, you explicitly verify your certificate. If you have self-signed HTTPS then, you need to pass the certificate to cross verify with your local certificate.
verify = True
import requests
response = requests.get("https://api-management-example/run", verify=True)
Self Signed Certificate
import requests
response = requests.get("https://api-management-example/run", verify="/path/to/local/certificate/file/")
Post requests are more secure because they can carry data in an encrypted form as a message body. Whereas GET requests append the parameters in the URL, which is also visible in the browser history, SSL/TLS and HTTPS connections encrypt the GET parameters as well. If you are not using HTTPs or SSL/TSL connections, then POST requests are the preference for security.
A dictionary object can be used to send the data, as a key-value pair, as a second parameter to the post method.
The HTTPS protocol is safe provided you have a valid SSL certificate on your API. If you want to be extra safe, you can implement end-to-end encryption/cryptography. Basically converting your so called plaintext, and converting it to scrambled text, called ciphertext.
You can explicitly enable verification in requests library:
import requests
session = requests.Session()
session.verify = True
session.post(url='https://api-management-example/run', data={'bar':'baz'})
This is enabled by default. you can also verify the certificate per request:
requests.get('https://github.com', verify='/path/to/certfile')
Or per session:
s = requests.Session()
s.verify = '/path/to/certfile'
Read the docs.

using kubernetes secrets in SSLContext

I am doing POC to check if we can connect to an API, for that I use the below code.
# Define the client certificate settings for https connection
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
print(type(context))
context.load_cert_chain(certfile=CERT, keyfile=KEY, password=PW)
# Create a connection to submit HTTP requests
connection = http.client.HTTPSConnection(host, port=443, context=context)
# Use connection to submit a HTTP POST request
connection.request(method="GET", url=request_url, headers=request_headers)
# Print the HTTP response from the IOT service endpoint
response = connection.getresponse()
print(response.status, response.reason)
data = response.read()
print(data)
these two variables (CERT and KEY), i get the secrets via kubernetes files and convert them to strings. Is there alternate ways to load the downloaded secrets into context object instead of using load_cert_chain method (since this one needs files). I know this is not ideal, but since I am doing only POC I just want to see if this is doable.

boto3: How to interract with DigitalOcean S3 Spaces when CDN is enabled

I'm working with DigitalOcean Spaces (S3 storage protocol) which has enabled CDN.
Any file on s3 can be accessed via direct URL in the given form:
https://my-bucket.fra1.digitaloceanspaces.com/<file_key>
If CDN is enabled, the file can be accessed via additional CDN URL:
https://my-bucket.fra1.cdn.digitaloceanspaces.com/<file_key>
where fra1 is a region_name.
When I'm using boto3 SDK for Python, the file URL is the following (generated by boto3):
https://fra1.digitaloceanspaces.com/my-bucket/<file_key>
# just note that bucket name is no more a domain part!
This format also works fine.
But, if CDN is enabled - file url causes an error:
EndpointConnectionError: Could not connect to the endpoint URL: https://fra1.cdn.digitaloceanspaces.com/my-bucket/<file_key>
assuming the endpoint_url was changed from
default_endpoint=https://fra1.digitaloceanspaces.com
to
default_endpoint=https://fra1.cdn.digitaloceanspaces.com
How to connect to CDN with proper URL without getting an error?
And why boto3 uses different URL format? Is any workaround can be applied in this case?
code:
s3_client = boto3.client('s3',
region_name=s3_configs['default_region'],
endpoint_url=s3_configs['default_endpoint'],
aws_access_key_id=s3_configs['bucket_access_key'],
aws_secret_access_key=s3_configs['bucket_secret_key'])
s3_client.download_file(bucket_name,key,local_filepath)
boto3 guide for DigitalOcean Spaces.
Here is what I've also tried but It didn't work:
Generate presigned url's
UPDATE
Based on #Amit Singh's answer:
As I mentioned before, I've already tried this trick with presigned URLs.
I've got Urls like this
https://fra1.digitaloceanspaces.com/<my-bucket>/interiors/uploaded/images/07IRgHJ2PFhVqVrJDCIpzhghqe4TwK1cSSUXaC4T.jpeg?<presigned-url-params>
The bucket name appears after endpoint. I had to move It to domain-level manually:
https://<my-bucket>.fra1.cdn.digitaloceanspaces.com/interiors/uploaded/images/07IRgHJ2PFhVqVrJDCIpzhghqe4TwK1cSSUXaC4T.jpeg?<presigned-url-params>
With this URL I can now connect to Digital ocean, but another arror occures:
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
<Code>SignatureDoesNotMatch</Code>
<RequestId>tx00000000000008dfdbc88-006005347c-604235a-fra1a</RequestId>
<HostId>604235a-fra1a-fra1</HostId>
</Error>
As a workaround I've tired to use signature s3v4:
s3_client = boto3.client('s3',
region_name=configs['default_region'],
endpoint_url=configs['default_endpoint'],
aws_access_key_id=configs['bucket_access_key'],
aws_secret_access_key=configs['bucket_secret_key'],
config= boto3.session.Config(signature_version='s3v4'))
but It still fails.
boto3 is a client library for Amazon S3 and not Digital Ocean Spaces. So, boto3 will not recognize the CDN URL fra1.cdn.digitaloceanspaces.com since it is provided by Digital Ocean and the URL with CDN is not one of the supported URI patterns. I don't fully understand how CDNs work internally, so my guess is there might be challenges with implementing this redirection to correct URL.
Now that that's clear, let's see how we can get a pre-signed CDN URL. Suppose, your CDN URL is https://fra1.cdn.digitaloceanspaces.com and your space name is my-space. We want to get a pre-signed URL for an object my-example-object stored in the space.
import os
import boto3
from botocore.client import Config
# Initialize the client
session = boto3.session.Session()
client = session.client('s3',
region_name='fra1',
endpoint_url='https://fra1.digitaloceanspaces.com', # Remove `.cdn` from the URL
aws_access_key_id=os.getenv('SPACES_KEY'),
aws_secret_access_key=os.getenv('SPACES_SECRET'),
config=Config(s3={'addressing_style': 'virtual'}))
# Get a presigned URL for object
url = client.generate_presigned_url(ClientMethod='get_object',
Params={'Bucket': 'my-space',
'Key': 'my-example-object'},
ExpiresIn=300)
print(url)
The pre-signed URL will look something like :
https://my-space.fra1.digitaloceanspaces.com/my-example-object?AWSAccessKeyId=EXAMPLE7UQOTHDTF3GK4&Content-Type=text&Expires=1580419378&Signature=YIXPlynk4BALXE6fH7vqbnwjSEw%3D
Add the cdn in between either manually or programmatically, in case you need to so that your final URL will become:
https://my-space.fra1.cdn.digitaloceanspaces.com/my-example-object?AWSAccessKeyId=EXAMPLE7UQOTHDTF3GK4&Content-Type=text&Expires=1580419378&Signature=YIXPlynk4BALXE6fH7vqbnwjSEw%3D
This is your CDN URL.
Based on #Amit Singh's answer, I've made an additional research of this issue.
Answers that helped me were found here and here.
To make boto3 presigned URLs work, I've made the following update to client and generate_presigned_url() params.
s3_client = boto3.client('s3',
region_name=configs['default_region'],
endpoint_url=configs['default_endpoint'],
aws_access_key_id=configs['bucket_access_key'],
aws_secret_access_key=configs['bucket_secret_key'],
config=boto3.session.Config(signature_version='s3v4', retries={
'max_attempts': 10,
'mode': 'standard'
},
s3={'addressing_style': "virtual"}, ))
...
response = s3_client.generate_presigned_url('get_object',
Params={'Bucket': bucket_name,
'Key': object_name},
ExpiresIn=3600,
HttpMethod=None
)
After that, .cdn domain part shoud be added after region name.

WSO2 API POST Python Web Service - Empty or No Payload to server

I am not able to send request payload to my POST service from WSO2.
On rest console, my service is working.
From WSO2 server I am able to do curl to my server with successful response.
here is my API configuration
Payload to send:
{"query":"Hi I am a POST query parameter"}
My server is receiving {} as request payload. It expect RAW body in JSON (as above) in payload. I have tried all combinations for Parameter Type, but still not able to send payload to my server from WSO2.
How can I do this?
EDIT 1
I have tried all possible ways of sending data including following.
Am I doing something wrong here???
and
From both I get error that my payload is empty or incorrect!!
Edit 2
I am able to connect with Java based services but not with Python based services.
Do I need any special settings on my python server?
enable wirelogs and check following
payload is coming into the API manager (swagger -> AM )
Payload is going out from api manager (AM -> backend)
Also check the request headers coming in and going out and compare them with the stuff from curl request (successful request)
I am using Flask and I am afraid Flask can not deal with this issue currently.
I could reproduce this issue, the message send to back-end correctly, but Python only handle message until timeout.
Python Flask cannot receive post request from WSO2
The work-round may be using Java or Python get method.
I solved this problem by using apache to proxy this request.
I think this is related with wsgi.
Processing chunked encoded HTTP POST requests in python (or generic CGI under apache)
ProxyPass / http://localhost:8001/
ProxyPassReverse / http://localhost:8001/

How do I know what's the realm and uri of a site

I want to use python's urllib2 with authentication and I need the realm and uri of a url. How do I get it?
thanks
When you make a request for a resource that requires authentication, the server will respond with a 401 status code, and a header that contains the realm:
WWW-Authenticate: Basic realm="the realm"
The URI is the URL you're trying to access.

Categories