Django/Python http.server access 404s via proxy, works locally - python

I've previously had the issue that I couldn't pinpoint the culprit in my Django app, causing all requests to 404. See the previous Stackoverflow question.
Since then I've tried to narrow down the issue and therefore started a new, VERY basic, Django app.
All the project consists of is:
$ django-admin startproject tempor
I've added the test directory and imported the test function
$ vi tempor/tempor/urls.py
from django.conf.urls import url
from django.contrib import admin
from tempor.views import test
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^test/$', test),
]
The views.py and the test function
$ vi tempor/tempor/views.py
from django.http import HttpResponse
def test(request):
return HttpResponse("OKAY")
Then I migrated the project - as suggested by Django:
$ python manage.py check
$ python manage.py migrate
Now I run the server:
$ python3 manage.py runserver 0.0.0.0:8282
Access from the server locally:
$ curl localhost:8282/test/
OKAY
$ curl <server-public-IP>:8282/test/
OKAY
Access from an external system (via proxy)
$ curl <server-public-IP>:8282/test/
<!DOCTYPE html>
<html lang="en">
[...]
<h1>Page not found <span>(404)</span></h1>
[...]
</html>
In the settings.py I have:
ALLOWED_HOSTS = ['*']
If I don't use '*', the external system is informed accordingly, as debugging is on:
$ curl <server-public-IP>:8282/test/
[...]
DisallowedHost at http://<server-public-IP>:8282/test/
[...]
This also occurs, if I try the same with a simple Python HTTP server - which the Django admin.py basically uses.
echo "OKAY" > /tmp/test/index.html
cd /tmp/test/
python -m http.server 8282
[...]
Local access:
$ curl localhost:8282/index.html
OKAY
$ curl <server-public-IP>:8282/index.html
OKAY
Remote access:
$ curl <server-public-IP>:8282/index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
[...]
<p>Error code explanation: HTTPStatus.NOT_FOUND - Nothing matches the given URI.</p>
[...]
</html>
http.server log:
Serving HTTP on 0.0.0.0 port 8282 ...
<server-public-ip> - - [<timestamp>] "GET /index.html HTTP/1.1" 200 -
<proxy-ip> - - [<timestamp>] code 404, message File not found
<proxy-ip> - - [<timestamp>] "GET http://<server-public-ip>:8282/index.html HTTP/1.1" 404 -
For the moment, the proxy is a simple SSH port forwarding - which will later be replaces by an NGINX.
edit: The very same applies if I run uwsgi for the Django app:
$ uwsgi --http :8282 --module tempor.wsgi
Local accesses work - external requests 404.
Why do requests via a proxy 404 for me and how can I fix this issue?

Solved, but I don't understand why.
The issue was the direct access to the Python server using SSH port forwarding. An intermediate Nginx resolved the problem.
See my ServerFault question for further details.
For the server setup see the ServerFault question.
Client setup boils down to:
# SSH port forwarding, port 80 for Nginx access, port 8282 for direct Python webserver access
ssh -L 8181:<farawayhost-ip>:<80 or 8282> <sshuser>#<remotehost-ip>
# on a second terminal
export http_proxy=127.0.0.1:8181
curl <farawayhost-ip>

Related

Configuring python flask app in HTTPS mode in OpenShift [duplicate]

This question already has answers here:
Are a WSGI server and HTTP server required to serve a Flask app?
(3 answers)
Closed 1 year ago.
This post was edited and submitted for review 1 year ago and failed to reopen the post:
Original close reason(s) were not resolved
I am trying to run a python flask app in https mode in openshift 4.7. I have my python flask app listening at port 8080 and this port is exposed through dockerFile configuration as shown below. I have redirected the https requests through the configuration in Openshift console by doing Service Port Mapping for https port 443 to pod port 8080. You can configure this by going to Openshift Console > Project > Services > Service Details and select your app. But still I can't access the service in https mode. When I try to access in https mode I get the error The application is currently not serving requests at this endpoint. It may not have been started or is still starting.
When I do normal deployment through oc cli and do not do any ssl configuration through openshift console, the app works fine in http mode. Please advise on how to run this in https mode
my app.py code is below
from flask import Flask, jsonify, request, redirect
app = Flask(__name__)
#app.route('/<string:name>/')
def helloName(name):
print(request)
return "Hello " + name
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8080, debug = True)
my Dockerfile content is as below and I am exposing port 8080 from it
# set base image
FROM registry.redhat.io/rhel8/python-38
# copy the dependencies file to the working directory
COPY requirements.txt .
# Configure PIP with local Nexus
ENV PIP_TRUSTED-HOST=nexus.company.com:8443
ENV PIP_INDEX=https://nexus.company.com:8443/repository/pypi-group/pypi
ENV PIP_INDEX-URL=https://nexus.company.com:8443/repository/pypi-group/simple
ENV PIP_NO-CACHE-DIR=false
ENV PIP_TIMEOUT=600
RUN touch ~/.netrc && \
echo "machine nexus.company.com" >> ~/.netrc && \
echo "login ${NEXUS_USER}" >> ~/.netrc && \
echo "password ${NEXUS_TOKEN}" >> ~/.netrc
# install dependencies
RUN pip install -U pip \
pip install -r requirements.txt
# copy the content of the local src directory to the working directory
COPY src/ .
EXPOSE 8080
# command to run on container start
CMD [ "python3", "./app.py" ]
Requirements.txt has only Flask
Two things here:
With host="0.0.0.0" Flask listens to the IP of the machine it is running on. I think changing this to 127.0.0.1 should be good.
app.run(host='127.0.0.1', port=8080, debug = True)
Probably even more important, the .run() method from Flask is only for development, for production you should use something like gunicorn or something.
link: https://gunicorn.org/
Cheers, T

Django OAuth Toolkit - Introspection Request: "Authentication credentials were not provided."

Good morning or afternoon even good evening!
I have been trying to achieve the separating resource server from the auth server using OAuth Toolkit with Django and I got stuck.
Tryed:
First, I have already tried the following:
Follow the tutorial with this tutorial and it works when it comes to serving projects with python manage.py runserver.
The whole structure is that I use Postmen as client and request to resource server and check the authenticated user with auth server so there is introspection process between resource and auth server.
Isuss:
As I mentioned, the whole idea works only when I serve project with python manage.py runserver. When deployed projects in Docker-Compose using Nginx and Gunicorn to serve projects, headache has come.
This was the final error - Max retries exceeded with url: /o/introspect/
When I tracked back to the root - Introspection: Failed POST to localhost:8000/o/introspect/ in token lookup
This is error in the client app - "Authentication credentials were not provided."
I found this issue is happened when the access token is expired or revoked and the system try to get a new access token to resource server from auth server.
Somehow, the introspection process is failed by for me an unknown reason!
Anybody hit this wall before?
Edit: (Thu Mar 4, 2021)
I found another reason that can more related to the exact issue!
As the docker compose create services that each service serves one container consisting of image of the project(Django). Therefore, each project is isolated from each other!
This results in A project can be harder to request to B project as the port for B project cannot be reach in the A project.
A potential solution may be using the Nginx server proxy name (which is gonna be the same as the name of each service in docker compose) to make a request.
I am still trying to handle this! If anyone can help that would be really appreciate!
Edit: (Thu Mar 4, 2021 5:07PM Taiwan) Problem Sovled
Solution is demoed!
Before you READ: This solution is to handle projects using Django OAuth Toolkit deployed with Docker-Compose which is occurred the Failed Introspection Request issue
So first, let me demo you the docker compose structure:
version: "3.4"
x-service-volumes: &service-volumes
- ./:/usr/proj/:rw,cached
services:
ShopDjangoBN_Nginx:
image: ${DJ_NGINX_IMAGE}
ports:
- 8001:8001
volumes: *service-volumes
environment:
- NGINX_SHOP_HOST=${NGINX_SHOP_HOST}
depends_on:
- "ShopDjangoBN"
ShopDjangoBN:
image: ${SHOP_DJANGO_IMAGE}
command: gunicorn -w 2 -b 0.0.0.0:8001 main.wsgi:application
volumes: *service-volumes
depends_on:
- "ShopDjangoBN_Migrate"
expose:
- 8001
ShopDjangoBN_CollectStatic:
image: ${SHOP_DJANGO_IMAGE}
command: python manage.py collectstatic --noinput
volumes: *service-volumes
ShopDjangoBN_Migrate:
image: ${SHOP_DJANGO_IMAGE}
command: python manage.py migrate
volumes: *service-volumes
OAuthDjangoBN_Nginx:
image: ${DJ_NGINX_IMAGE}
ports:
- 8000:8000
volumes: *service-volumes
environment:
- NGINX_OAUTH_HOST=${NGINX_OAUTH_HOST}
depends_on:
- "OAuthDjangoBN"
OAuthDjangoBN:
image: ${O_AUTH_DJANGO_IMAGE}
command: gunicorn -w 2 -b 0.0.0.0:8000 main.wsgi:application
volumes: *service-volumes
depends_on:
- "OAuthDjangoBN_Migrate"
expose:
- 8000
OAuthDjangoBN_CollectStatic:
image: ${O_AUTH_DJANGO_IMAGE}
command: python manage.py collectstatic --noinput
volumes: *service-volumes
OAuthDjangoBN_Migrate:
image: ${O_AUTH_DJANGO_IMAGE}
command: python manage.py migrate
volumes: *service-volumes
volumes:
auth-data:
shop-data:
static-volume:
media-volume:
There are two Nginx server services that handle Django's network ShopDjangoBN_Nginx and OAuthDjangoBN_Nginx in the docker-compose yml file! Generally speaking, if we serve projects without docker-compose and nginx, you wouldn't meet this issue. However you would meet this issue when it comes to the deployment using docker tech, I assume.
To set up the idea of Separate Server, You need to follow this tutorial and you will need to complete the following code in Django settings file for the Resource server project:
OAUTH2_PROVIDER = {
...
'RESOURCE_SERVER_INTROSPECTION_URL': 'https://example.org/o/introspect/',
'RESOURCE_SERVER_AUTH_TOKEN': '3yUqsWtwKYKHnfivFcJu', # OR this but not both:
#'RESOURCE_SERVER_INTROSPECTION_CREDENTIALS' ('rs_client_id','rs_client_secret'),
...
}
The key here is the 'RESOURCE_SERVER_INTROSPECTION_URL' variable! This variable is used to request the Introspection Request from Resource to Auth server and therefore, this url is pretty recommended to be set correctly and it has to be the introspection endpoint in the Auth Server.
Next, if you still remember, the OAuthDjangoBN_Nginx, which handles any request for Auth Server, is a reverse proxy service! And technically speaking, the OAuthDjangoBN_Nginx is gonna be our host for the Auth Server. So... the introspection url in Resource server Django settings file will be like:
'RESOURCE_SERVER_INTROSPECTION_URL': 'https://OAuthDjangoBN_Nginx:<port>/o/introspect/'
And the nginx.conf
upstream OAuthDjangoBN {
server OAuthDjangoBN:8000;
}
server {
listen 8000;
location / {
proxy_pass http://OAuthDjangoBN;
# proxy_set_header Host $NGINX_SHOP_HOST;
proxy_set_header Host "localhost:8000";
proxy_redirect off;
}
location /static/ {
alias /usr/proj/OAuthDjangoBN/static/;
}
}
This proxy_set_header is better to be set with env variable, I found some solutions on the internet so wouldn't be a problem. It is also important to be set up as the reverse host name is gonna be OAuthDjangoBN_Nginx: which will be not recognised as a valid host name and therefore, set it up!
proxy_set_header Host "localhost:8000";
Alright, I think this idea can be a solution if someone has met or will meet the same issue. I also believe this can be still a confusion. Just let me know if you hit the wall!

How do you execute a server-side python script using http.server?

I have a collection of python scripts, that I would like to be able to execute with a button press, from a web browser.
Currently, I run python -m http.server 8000 to start a server on port 8000. It serves up html pages well, but that's about all it does. Is it possible to have it execute a python script (via ajax) and return the output, instead of just returning the full text of the .py file.
Additionally, if not, is there a simple (as in only 1 or 2 files) way to make this work? I'm looking for the equivalent of PHP -s, but for python.
For completeness, this is my html
<h1>Hello World</h1>
<button>
Click me!
</button>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.3.1.js"> </script>
<script>
$('button').click(function(){
$.get('/gui/run_bash.py');
});
</script>
Add --cgi to your command line.
python -m http.server --cgi 8000
Then place your python scripts in ./cgi-bin and mark them as executable.
$ mkdir cgi-bin
$ cp hello.py cgi-bin/hello.py
$ chmod +x cgi-bin/hello.py
You may need to slightly modify your python scripts to support the CGI protocol.
Here is the server running:
$ cat cgi-bin/hello.py
#! /usr/bin/env python3
print("Content-Type: application/json")
print()
print('{"hello": "world"}')
radams#wombat:/tmp/z/h$ python -m http.server --cgi
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
127.0.0.1 - - [20/Mar/2018 18:04:16] "GET /cgi-bin/hello.py HTTP/1.1" 200 -
Reference: https://docs.python.org/3/library/http.server.html#http.server.CGIHTTPRequestHandler
http.server merely serves static files, it does not do any serverside processing or execute any code when you hit a python file. If you want to run some python code, you'll have to write an application to do that. Flask is a Python web framework that is probably well-suited to this task.
Your flask application might look something like this for executing scripts...
import subprocess
from flask import Flask
app = Flask(__name__)
SCRIPTS_ROOT = '/path/to/script_dir'
#app.route('/run/<script_name>')
def run_script(script_name):
fp = os.path.join(SCRIPTS_ROOT, script_name)
try:
output = subprocess.check_output(['python', fp])
except subprocess.CalledProcessError as call:
output = call.output # if exit code was non-zero
return output.encode('utf-8') # or your system encoding
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8000)
And of course, I should include an obligatory warning 'having a webserver execute commands like this is insecure', etc, etc. Check out the Flask quickstart for more details.

Django manage.py runserver fails to respond

I'm running a vagrant box on Mac OS X. The VM is running Ubuntu 12.04, with Python 2.7 and Django 1.4.5. When I start up manage.py, I call it like this:
./manage.py runserver 0.0.0.0:8000
And if I visit http://127.0.0.1:8000 from within the VM, the text browsers I've tried report that the HTTP request has been sent and then wait for a response until the request times out. No response ever comes.
I can telnet to the port like this:
telnet 127.0.0.1 8000
And enter random gibberish, which manage.py reports as the following:
127.0.0.1 - - [05/Aug/2014 17:06:26] code 400, message Bad request syntax ('asdfasdfadsfasd')
127.0.0.1 - - [05/Aug/2014 17:06:26] "asdfasdfadsfasd" 400 -
So manage.py is listening on that port. But a standard HTTP request generates no response from manage.py, either in the console or in the browser.
I've tried using different ports which hasn't had any effect. Does anyone have any ideas?
UPDATE
Some additional curl output.
Executing 'curl -v http://127.0.0.1:8000' returns
'* About to connect() to 127.0.0.1 port 8000 (#0)
* Trying 127.0.0.1... connected
GET / HTTP/1.1
User-Agent: curl/7.22.0 (i686-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
Host: 127.0.0.1:8000
Accept: /
'
Executing 'curl -v http://somefakedomain' results in
'* getaddrinfo(3) failed for somefakedomain:80
* Couldn't resolve host 'somefakedomain'
* Closing connection #0
curl: (6) Couldn't resolve host somefakedomain'
Okay, so to reiterate my last post.
There was a call to a Django service that was failing on application startup. No error was thrown, instead it was absorbed by Sentry. Those who were already using the VM on their local machines had worked around the issue.
The issue was identified by importing ipdb and calling its set_trace() function. From the console, I stepped through the application, testing likely variables and return values until it refused to continue. This narrowed it down to the misbehaving service and its unthrown error.
The code has been updated with proper try/catch blocks and the error is now handled gracefully.
So to summarise: Not a malfunctioning VM, but a problem with code.

How to execute python script on the BaseHTTPSERVER created by python?

I have simply created a python server with :
python -m SimpleHTTPServer
I had a .htaccess (I don't know if it is usefull with python server)
with:
AddHandler cgi-script .py
Options +ExecCGI
Now I am writing a simple python script :
#!/usr/bin/python
import cgitb
cgitb.enable()
print 'Content-type: text/html'
print '''
<html>
<head>
<title>My website</title>
</head>
<body>
<p>Here I am</p>
</body>
</html>
'''
I make test.py (name of my script) an executed file with:
chmod +x test.py
I am launching in firefox with this addres: (http : //) 0.0.0.0:8000/test.py
Problem, the script is not executed... I see the code in the web page...
And server error is:
localhost - - [25/Oct/2012 10:47:12] "GET / HTTP/1.1" 200 -
localhost - - [25/Oct/2012 10:47:13] code 404, message File not found
localhost - - [25/Oct/2012 10:47:13] "GET /favicon.ico HTTP/1.1" 404 -
How can I manage the execution of python code simply? Is it possible to write in a python server to execute the python script like with something like that:
import BaseHTTPServer
import CGIHTTPServer
httpd = BaseHTTPServer.HTTPServer(\
('localhost', 8123), \
CGIHTTPServer.CGIHTTPRequestHandler)
###  here some code to say, hey please execute python script on the webserver... ;-)
httpd.serve_forever()
Or something else...
You are on the right track with CGIHTTPRequestHandler, as .htaccess files mean nothing to the the built-in http server. There is a CGIHTTPRequestHandler.cgi_directories variable that specifies the directories under which an executable file is considered a cgi script (here is the check itself). You should consider moving test.py to a cgi-bin or htbin directory and use the following script:
cgiserver.py:
#!/usr/bin/env python3
from http.server import CGIHTTPRequestHandler, HTTPServer
handler = CGIHTTPRequestHandler
handler.cgi_directories = ['/cgi-bin', '/htbin'] # this is the default
server = HTTPServer(('localhost', 8123), handler)
server.serve_forever()
cgi-bin/test.py:
#!/usr/bin/env python3
print('Content-type: text/html\n')
print('<title>Hello World</title>')
You should end up with:
|- cgiserver.py
|- cgi-bin/
` test.py
Run with python3 cgiserver.py and send requests to localhost:8123/cgi-bin/test.py. Cheers.
Have you tried using Flask? It's a lightweight server library that makes this really easy.
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello_world():
return '<title>Hello World</title>'
if __name__ == '__main__':
app.run(debug=True)
The return value, in this case <title>Hello World</title>, is rendered has HTML. You can also use HTML template files for more complex pages.
Here's a good, short, youtube tutorial that explains it better.
You can use a simpler approach and use the --cgi option launching the python3 version of http server:
python3 -m http.server --cgi
as pointed out by the command:
python3 -m http.server --help

Categories