Serving multiple apps on different sub-domains with Lighthtpd - django

I am trying to serve different subdomains with different applications, all using the same Lighthtpd server as a front server. All applications serving sub-domains are also running on the same server as Lighthtpd.
I have setup DNS records as follows (both are A records)
mydomain.com xx.xx.xx.xx
sub.mydomain.com xx.xx.xx.xx
xx.xx.xx.xx is same for both records.
Relevant parts of my Lighttpd configuration file as as follows
$HTTP["host"] =~ "sub.mydomain.com" {
fastcgi.server = (
"/ideas.fcgi" => (
"main" => (
"host" => "127.0.0.1",
"port" => "9030",
)
),
)
url.rewrite-once = (
"^(/.*)$" => "/ideas.fcgi$1",
)
}
$HTTP["host"] =~ "mydomain.com" {
proxy.balance = "round-robin" proxy.server = ( "/" =>
( ( "host" => "127.0.0.1", "port" => 9010 ) ) )
}
The process running on port 9010 is a Java web app and the process running on 9030 is a django webapp running as an fcgi process started with the following command
./manage.py runfcgi method=threaded host=127.0.0.1 port=9030
My problem is - in my browser both the url's, mydomain.com and sub.mydomain.com get the same web application (the app intended for mydomain.com).
It seems that the Lighttpd setting for sub.mydomain.com is either being ignored or overriden by the setting for mydomain.com. I even tried reversing the physical location of both settings in the configuration file, but that did not help either.
I am sure this configuration should be possible. Am I missing something ?

The line $HTTP["host"] =~ "mydomain.com" is doing a regular expression match, so it matches the Django subdomain as well as the domain. If you use a string equals match as below, then the Django subdomain will not match.
$HTTP["host"] == "mydomain.com"
There might be a better way to do this (e.g. a command to skip all further virtual hosts once you've matched the first one), but I'm afraid my lighttpd knowledge is limited.

Related

Automatically certbot renew wildcard certificates on NameCheap - port 53 problem?

I'm trying to get an AWS/Lightsail Debian server automatically renewing certificates with certbot. My DNS is with Namecheap.
I'm follow the steps on https://blog.bryanroessler.com/2019-02-09-automatic-certbot-namecheap-acme-dns/ and https://blog.bryanroessler.com/2019-02-09-automatic-certbot-namecheap-acme-dns/. I keep getting a no-permission error.
I run:
sudo certbot certonly -d "*.example.com" --agree-tos --manual-public-ip-logging-ok --server https://acme-v02.api.letsencrypt.org/directory --preferred-challenges dns --manual --manual-auth-hook /etc/letsencrypt/acme-dns-auth.py --debug-challenges
I see:
Failed authorization procedure. example.com (dns-01): urn:ietf:params:acme:error:unauthorized :: The client lacks sufficient authorization :: No TXT record found at _acme-challenge.example.com
It says I need to open port 53. I followed Amazon's Lightsail instructions. Neither iptables nor ufw seems to be installed. When I nmap my machine, I don't see 53. I actually installed ufw for lack of a good idea, to no avail.
My /etc/acme-dns/config.cfg is as follows:
#/etc/acme-dns/config.cfg
[general]
# DNS interface
listen = ":53"
protocol = "udp"
# domain name to serve the requests off of
domain = "acme.example.com"
# zone name server
nsname = "ns1.acme.example.com"
# admin email address, where # is substituted with .
nsadmin = "example.example.com"
# predefined records served in addition to the TXT
records = [
"acme.example.com. A <public ip>",
"ns1.acme.example.com. A <public ip>",
"acme.example.com. NS ns1.acme.example.com.",
]
debug = false
[database]
engine = "sqlite3"
connection = "/var/lib/acme-dns/acme-dns.db"
[api]
api_domain = ""
ip = "127.0.0.1"
disable_registration = false
autocert_port = "80"
port = "8082"
tls = "none"
corsorigins = [
"*"
]
use_header = false
header_name = "X-Forwarded-For"
[logconfig]
loglevel = "debug"
logtype = "stdout"
logformat = "text"
For the listen value, I also tried 127.0.0.1:53 and :53
The settings portion of /etc/letsencrypt/acme-dns-auth.py:
# URL to acme-dns instance
ACMEDNS_URL = "http://127.0.0.1:8082"
# Path for acme-dns credential storage
STORAGE_PATH = "/etc/letsencrypt/acmedns.json"
# Whitelist for address ranges to allow the updates from
# Example: ALLOW_FROM = ["192.168.10.0/24", "::1/128"]
ALLOW_FROM = []
# Force re-registration. Overwrites the already existing acme-dns accounts.
FORCE_REGISTER = False
Thanks for any help you can provide.
If you don't wish to maintain your own acme DNS server, I built and use this script to automatically renew NameCheap wildcard certs with certbot. I hope it helps:
https://github.com/scribe777/letsencrypt-namecheap-dns-auth

Django 502 bad gateway with Nginx and Uwsgi

I can normally open up my web in the front several hours after deployment,but later , it occurred 502 bad gateway ,it is so wired, my web uses Django and Nginx and Uwsgi, i do research a lot on google,but failed with nothing
Here is my configuration:
1.Nginx configuration
# mysite_nginx.conf
upstream django {
server 127.0.0.1:8004; # for a web port socket (we'll use this first)
}
server {
listen 80;
server_name www.example.com # substitute your machine's IP address or FQDN
charset utf-8;
client_max_body_size 75M; # adjust to taste
location /media {
alias /home/blender_font_project/django_file/Blender_website/media;
}
location /static {
alias /home/blender_font_project/django_file/Blender_website/static;
}
location / {
uwsgi_pass 127.0.0.1:8003;
include /etc/nginx/uwsgi_params;
}
}
2.uwsgi configuration
# mysite_uwsgi.ini file
[uwsgi]
chdir = /home/blender_font_project/django_file/Blender_website
module = djangoTest5.wsgi
master = true
processes = 10
socket = :8003
vacuum = true
harakiri=60
daemonize=/home/blender_font_project/uwsgi_file/real3dfont_logfile
3.this is my Nginx error log
231 connect() failed (111: Connection refused) while connecting to upstream
BTW , i have set Django to DEBUG Ture and i can access my resource by www.example.com/static/example.jpg,but the web page shows 502
I really dont know why , thanks if you offer any help!
(...After million years struggle and strive,with inspiration from a super hero in comment named #Atul Mishra , i finally figure it out...)
It is the matter Django itself,i forget to download mysql module in View , i would have expect a Django error html if it is the django problem, but no , so i mistakenly attribute it to Nginx or Uwsgi
But the wired thing is that Django should have report the error , but no ! what an irresponsible dude!!
so , 1.remember to add Django error log function ,it saves your life , and
2.test Django with runserver before Nginx enter the stage even when comet is striking the earth!!

Can't connect SFTP(AWS EC2) with QuotaGuard Static IP

I am using QuotaGuard Static Addon on Heroku to access the SFTP server(AWS EC2) which has whitelisted IP.
I have tried to connect with a private key file.
This is my code.
def connect
puts "started"
Net::SSH.start(ENV["HOST"], ENV["USER"],
{
:key_data => [ ENV["FTP_KEY"] ],
:keys => [],
:keys_only => true,
:verbose => :debug,
:proxy => proxy
}
) do |ssh|
ssh.sftp.connect do |sftp|
sftp.dir.foreach("/") do |entry|
puts entry.longname
end
end
end
puts "done"
end
def quotaguard
URI(ENV["QUOTAGUARDSTATIC_URL"])
end
def proxy
Net::SSH::Proxy::HTTP.new(quotaguard.host,quotaguard.port, :user => quotaguard.user,:password=> quotaguard.password)
end
But it is failed to connect with this error.
WARN: Net::SSH::Proxy::ConnectError: {:version=>"HTTP/1.1", :code=>502, :reason=>"Bad Gateway", :headers=>{}, :body=>nil}
HOST, USER, FTP_KEY, and QUOTAGUARDSTATIC_URL are Heroku Env variables.
My thought:
I think to connect AWS EC2 using the proxy, maybe some settings need to be configured to allow proxy on AWS EC2.
But not sure.
There was the wrong Security Group on AWS EC2.
I updated and I can connect now.

Get the host IP from my django app container

There is a feature on our web app that we need to send the IP of our server but I always send 172.17.0.2 because that is the value of request.META['REMOTE_ADDR'] which is usually 127.0.0.1 when using django in localhost and which I assume is the TCP address of our NGINX container which where the request is coming from. How will I send the IP of my docker host instead?
Containers:
Nginx
Django with gunicorn
PostgreSQL
Redis
Pass it as an environment variable to your container when you create it. Then, read that environment variable in your Django code.
You can do it with option -e HOST_IP=$(/sbin/ip route | awk '/default/ { print $3 }') in docker run command.
In docker-compose.yml, you could do something like this.
django:
environment:
- HOST_IP=$(/sbin/ip route | awk '/default/ { print $3 }')
Try this:
import subprocess
host = subprocess.check_output(['bash', '-c', "/sbin/ip route|awk '/default/ { print $3 }'"]).decode('utf-8').strip()

Django CSRF validation fails on POST requests : referer checking failed - no Referer

My Django website is in HTTPS. When I am trying to POST data to the website from a script I get this error : "referer checking failed - no Referer". It seems to be a CSRF issue but I do not know how to solve it.
Example :
import requests
r = requests.post('https://mywebsite/mypage', data = {'key':'value'})
print r.text
gives me this output :
[...]
<p>Reason given for failure:</p>
<pre>
Referer checking failed - no Referer.
</pre>
<p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when
<a
href="https://docs.djangoproject.com/en/1.8/ref/csrf/">Django's
CSRF mechanism</a> has not been used correctly. For POST forms, you need to
ensure:</p>
<ul>
<li>Your browser is accepting cookies.</li>
<li>The view function passes a <code>request</code> to the template's <code>render</code>
method.</li>
<li>In the template, there is a <code>{% csrf_token
%}</code> template tag inside each POST form that
targets an internal URL.</li>
<li>If you are not using <code>CsrfViewMiddleware</code>, then you must use
<code>csrf_protect</code> on any views that use the <code>csrf_token</code>
template tag, as well as those that accept the POST data.</li>
</ul>
[...]
Do I need to pass a referer to my headers before sending the POST data - which would not be convenient ? Or should I disable CSRF for this page ?
Thanks
AFAIK, This is the purpose of CSRF, to avoid posting data from unknown strange sources. You need csrf token to post this which django generates dynamically.
Upgrading Django might fix the missing Referer error.
As of Django 4.0 (release notes), the backend will first check the Origin header before falling back to the Referer header (source):
CsrfViewMiddleware verifies the Origin header, if provided by the browser, against the current host and the CSRF_TRUSTED_ORIGINS setting. This provides protection against cross-subdomain attacks.
In addition, for HTTPS requests, if the Origin header isn’t provided, CsrfViewMiddleware performs strict referer checking. This means that even if a subdomain can set or modify cookies on your domain, it can’t force a user to post to your application since that request won’t come from your own exact domain.
It's possible you have a reverse proxy running, for example an nginx proxy_pass to 127.0.0.1:8000?
In this case, Django expects the Cross-Site Forgery Protection tokens to match hostname 127.0.0.1, but they will be coming from a normal domain (for example example.com).
Expected Source
Actual Source
http://127.0.0.1
https://example.com
HTTP reverse proxy (example.com:80 -> localhost:3000) is a common way to use nginx with NodeJS applications, but it doesn't work well with Django
Client-Facing URL
Server Proxy URL
https://example.com
http://127.0.0.1:3000
It is better to run Django through a Unix socket rather than a port (example.com:80 -> <socket>). You can do this with Gunicorn:
Client-Facing URL
Server Proxy URL
https://example.com
unix:/run/example.com.sock
Here's how to do this with Django, Gunicorn, and nginx:
Let's say you've got a Django project root, which contains a system folder (the one where settings.py and wsgi.py are):
export DJANGO_PROJECT_PATH=/path/to/django/root
export DJANGO_SETTING_FOLDER=system
First, make sure you have Gunicorn installed and that you are using a virtual environment:
cd $DJANGO_PROJECT_PATH
source .venv/bin/activate # <- Use a virtual environment
pip3 install gunicorn # <- install Gunicorn in the venv
Run Gunicorn. This will start the Django project similar to running python3 manage.py runserver, except that you can listen for requests on a Unix socket:
$DJANGO_PROJECT_PATH/.venv/bin/gunicorn \
--workers=3 \
--access-logfile - \
--bind unix:/run/example.com.sock \ # <- Socket
--chdir=$DJANGO_PROJECT_PATH/ \
$DJANGO_SETTING_FOLDER.wsgi:application
Then create an HTTP proxy using nginx that passes HTTP requests from clients through the gunicon-created socket:
/etc/nginx/sites-enabled/example.com:
server {
listen 80;
listen [::]:80;
server_name example.com;
# serve static files directly through nginx
location /static/ {
autoindex off;
root /path/to/django/root;
}
# serve user-uploaded files directly through nginx
location /media/ {
autoindex off;
root /path/to/django/root;
}
# You can do fun stuff like aliasing files from other folders
location /robots.txt {
alias /path/to/django/root/static/robots.txt;
}
# here is the proxy magic
location / {
include proxy_params;
proxy_pass http://unix:/run/example.com.sock; # <- the socket!
}
}
Make sure to restart nginx:
sudo service restart nginx
After all this, your csrf tokens should match the domain name of your site and you'll be able to log in and submit forms.