Unable to configure nginx as mail proxy - email

I need to use nginx as a mail proxy. I am completely new to nginx and need some help with the configuration.
Here is what I did:
First I built a service that mocks the authentication services described here: http://wiki.nginx.org/NginxMailCoreModule. For example,
curl -v -H "Host:auth.server.hostname" -H "Auth-Method:plain" -H "Auth-User:user" -H "Auth-pass:123" -H "Auth-Protocol:imap" -H "Auth-Login-Attempt:1" -H "Client-IP: 192.168.1.1" http://localhost:8080/authorize
returns the following response header:
< HTTP/1.1 200 OK
< Content-Type: text/html;charset=ISO-8859-1
< Auth-Status: OK
< Auth-Server: 192.168.1.10
< Auth-Port: 110
Second I installed nginx on my mac after installing macports:
$ sudo port -d selfupdate
$ sudo port install nginx
Third I created an nginx.conf with the following:
worker_processes 1;
error_log /var/log/nginx/error.log info;
mail {
server_name <my mail server here>;
auth_http http://localhost:8080/authorize;
pop3_auth plain apop cram-md5;
pop3_capabilities "LAST" "TOP" "USER" "PIPELINING" "UIDL";
xclient off;
server {
listen 110;
protocol pop3;
proxy on;
proxy_pass_error_message on;
}
}
Here is what I got running nginx:
$ nginx -V
nginx version: nginx/1.2.4
configure arguments: --prefix=/opt/local --with-cc-opt='-I/opt/local/include -O2' --with-ld-opt=-L/opt/local/lib --conf-path=/opt/local/etc/nginx/nginx.conf --error-log-path=/opt/local/var/log/nginx/error.log --http-log-path=/opt/local/var/log/nginx/access.log --pid-path=/opt/local/var/run/nginx/nginx.pid --lock-path=/opt/local/var/run/nginx/nginx.lock --http-client-body-temp-path=/opt/local/var/run/nginx/client_body_temp --http-proxy-temp-path=/opt/local/var/run/nginx/proxy_temp --http-fastcgi-temp-path=/opt/local/var/run/nginx/fastcgi_temp --http-uwsgi-temp-path=/opt/local/var/run/nginx/uwsgi_temp --with-ipv6
$ nginx
nginx: [emerg] unknown directive "mail" in /opt/local/etc/nginx/nginx.conf:6
The only mention of that error on the web brings up a discussion in Russian...
My questions:
Why am I getting this unknow directive?
Does my config look correct at first sight or am I missing some key component for the mail proxy to work using the authentication approach described here: http://wiki.nginx.org/NginxMailCoreModule?

I got the mail proxy working so I will answer my own questions for future reference:
nginx doesn't install support for mail by default
The following is needed for nginx to process the mail directive:
$ sudo port edit nginx
==> add --with-mail at the end of the config parameters
Then (re)install nginx
In the config I included, I was missing the events:
events {
worker_connections 1024;
}
An important clarification that got me stuck for a while: the authentication service (specified with auth_http) needs to return the mail server expressed as an IP address, not a host name.
Obviously for nginx to proxy on both inbound and outbound traffic, the smtp listener needs to be added. Similar approach as with the pop3 configuration. In my case, I used port 2525, so I had
server {
listen 2525;
protocol smtp;
}

Related

haproxy : http frontend to https backend

This is the exact same question as http request to https request using haproxy
However, the accepted answer does not work for me and I dont understand why
haproxy.cfg:
global
daemon
maxconn 15
defaults
mode tcp
balance first
frontend google
bind *:10005
default_backend google-url
backend google-url
server xxx google.com:443 ssl verify none
when I call curl --location --request GET 'http://localhost:10005', I receive a response that comes from google but with a 404 status
The requested URL / was not found on this server. That’s all we know.
I tried both mode tcp and mode http, same result
If I activate the logs with
mode http
bind *:10005
default_backend google-url
option httplog
log stdout format raw local0
I have this
127.0.0.1:52588 [16/Jun/2022:08:24:49.976] google google-url/xxx 0/0/49/20/69 404 1884 - - ---- 2/2/0/0/0 0/0 "GET / HTTP/1.1"
127.0.0.1:52588 [16/Jun/2022:08:24:49.938] google google/<NOSRV> -1/-1/-1/-1/1038 400 0 - - CR-- 2/2/0/0/0 0/0 "<BADREQ>"
In case this has some impact, I'm running haproxy in kubernetes and then I "port-forward" 10005 (but this does not seem to be the issue because the logs demonstrate that haproxy is correctly receiving the request and using the correct backend...)
Your curent HAProxy configuration will accept your request:
curl --location --request GET 'http://localhost:10005'
(corresponds to the first log entry)
and proxy it to Google as:
curl --location -H 'Host: localhost' --request GET 'https://www.google.com/'
(note the Host header implied; I bet this is not what you'd expect).
Google will respond with 404 and HAProxy will log the BADREQ.
This happens because HAProxy can't infer that when client request's Host header is localhost it should re-write it to google.com (or better: www.google.com) simply because it proxies to a host with that name.
You need to configure:
backend google-url
server xxx google.com:443 ssl verify none
http-request set-header host www.google.com

HAProxy frontend rule matching order

I have a haproxy configuration as follows. (haproxy 1.7) We want to catch all OPTIONS request and respond directly to them instead of routing the requests to backends (which have basic auth enabled).
This was working fine when we developed it but now it seems to not be matching the rules in order (not sure what we have/haven't done which has caused this):
global
log 127.0.0.1 local1
tune.ssl.default-dh-param 2048
lua-load /etc/haproxy/cors.lua
stats socket /var/run/haproxy.sock mode 400
# Default certificate and key directories
ca-base /etc/ssl/private
crt-base /etc/ssl/private
# User lists used to enforce HTTP Basic Authentication
userlist ul_100123-2ovt9rsu
user app1 password $6$lCjf6VnWhI$kcjmpWdV.odeYf4psUhcVKs49ZtPk3MDhg5wtLNUx658A3EWdDHJQqs9xCD1d.7zG05M2nwOxdkC6o/MSpifv0
userlist ul_100123-9uvsclqr
user app1 password $6$DlcLoDMMu$wDm3O0W1eiQuk8gI.GmpzI1.jbBf.UYQ.KM73nHa1tGZJNfzkDpVnLUhh7v7C9yPHB1oo0cRrFnfOdeyAf/eU1
# Front-end for public services which have SSL termination at the router.
frontend term
bind *:443 accept-proxy ssl no-sslv3 crt router/fred-external.pem crt router/fred-external.ace.pem crt router
reqadd X-Forwarded-Proto:\ https
rspidel ^(Server|X-Powered-By):
option forwardfor
mode http
http-request use-service lua.cors-response if METH_OPTIONS { req.hdr(origin) -m found }
acl host_match_100123-2ovt9rsu ssl_fc_sni -i 2ovt9rsu.fredurl.com
use_backend b_term_100123-2ovt9rsu if host_match_100123-2ovt9rsu
......
If I curl -X OPTIONS to 2ovt9rsu.fredurl.com it matches the 2nd rule and forwards me to the b_term_100123-2ovt9rsu backend which then fails as I haven't provided auth creds.
If I curl -X OPTIONS to Anything.fredurl.com it matches the first http-request and responds with the cors response as expected.
Why does the 2ovt9rsu.fredurl.com not match the first http-request rule and then return the cors-response?
In the logs we can see
Nov 7 18:24:09 localhost haproxy[37302]: 94.45.23.22:49853 [07/Nov/2017:18:24:09.807] term~ b_term_100123-2ovt9rsu/<lua.cors-response> -1/-1/-1/-1/73 401 249 - - PR-- 0/0/0/0/3 0/0 "OPTIONS / HTTP/1.1"
when the request gets forwarded to the backend
http-request gets executed before use_backend, the config looks good to me, have you set origin header when you curl ?

Nginx can't access a uWSGI unix socket on CentOS 7

I have configured uWSGI to serve my Django app on a unix socket, and Nginx as a proxy to this socket. The server is running CentOS 7. I think I have configured Nginx so that it has permission to read and write to uWSGI's socket, but I'm still getting a permission denied error. Why can't Nginx access the uWSGI socket on CentOS 7?
[uwsgi]
socket=/socket/uwsgi.sock
virtualenv=/home/site/virtsite/
chdir=/home/site/wsgitest/
module=wsgitest.wsgi:application
vhost = true
master=True
workers=8
chmod-socket=666
pidfile=/home/site/wsgitest/uwsgi-master.pid
max-requests=5000
chown-socket=nginx:nginx
uid = nginx
gid = nginx
listen.owner = nginx
listen.group = nginx
server {
listen 80;
location / {
uwsgi_pass unix:///home/site/wsgitest/uwsgi.sock;
include uwsgi_params;
}
}
uwsgi --ini uwsgi.ini (as root)
ls -l /home/site/wsgitest/uwsgi.sock
srwxrwxrwx. 1 nginx nginx 0 Oct 13 10:05 uwsgi.sock
2014/10/12 19:01:44 [crit] 19365#0: *10 connect() to unix:///socket/uwsgi.sock failed (13: Permission denied) while connecting to upstream, client: 2.191.102.217, server: , request: "GET / HTTP/1.1", upstream: "uwsgi://unix:///socket/uwsgi.sock:", host: "179.227.126.222"
The Nginx and uWSGI configurations are correct. The problem is that SELinux denied Nginx access to the socket. This results in a generic access denied error in Nginx's log. The important messages are actually in SELinux's audit log.
# show the new rules to be generated
grep nginx /var/log/audit/audit.log | audit2allow
# show the full rules to be applied
grep nginx /var/log/audit/audit.log | audit2allow -m nginx
# generate the rules to be applied
grep nginx /var/log/audit/audit.log | audit2allow -M nginx
# apply the rules
semodule -i nginx.pp
You may need to generate the rules multiple times, trying to access the site after each pass, since the first SELinux error might not be the only one that can be generated. Always inspect the policy that audit2allow suggests creating.
These steps were taken from this blog post which contains more details about how to investigate and what output you'll get.
Configure your uwsgi.ini with uid and gid user.
#uwsgi.ini
uid = nginx
gid = nginx
Regards,
I wished I could comment :(
Everything looks fine from here except unix socket path
unix:///socket/uwsgi.sock failed (2: No such file or directory)
Docs says it has just one slash
uwsgi_pass unix:/tmp/uwsgi.socket;

Nginx and Flask-socketio Websockets: Alive but not Messaging?

I've been having a bit of trouble getting Nginx to play nicely with the Python Flask-socketio library (which is based on gevent). Currently, since we're actively developing, I'm trying to get Nginx to just work as a proxy. For sending pages, I can get this to work, either by directly running the flask-socketio app, or by running through gunicorn. One hitch: the websocket messaging does not seem to work. The pages are successfully hosted and displayed. However, when I try to use the websockets, they do not work. They are alive enough that the websocket thinks it is connected, but they will not send a message. If I remove the Nginx proxy, they do work. Firefox gives me this error when I try to send a message:
Firefox can't establish a connection to the server at ws:///socket.io/1/websocket/.
Where web address is where the server is located and the unique id is just a bunch of randomish digits. It seems to be doing enough to keep the connection live (e.g., the client thinks it is connected), but can't send a message over the websocket. I have to think that the issue has to do with some part of the proxy, but am having mighty trouble debugging what the issue might be (in part because this is my first go-round with both Flask-socketIO and nginx). The configuration file I am using for nginx is:
user <user name>; ## This is set to the user name for the remote SSH session
worker_processes 5;
events {
worker_connections 1024; ## Default: 1024
}
http {
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] $status '
'"$request" $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
sendfile on;
server_names_hash_bucket_size 128; # this seems to be required for some vhosts
server {
listen 80;
server_name _;
location / {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
}
I made the config file as an amalgam of a general example and a websocket specific one, but trying to fiddle with it has not solved the issue. Also, I am using the werkzeug Proxy_Fix call on my Flask app.wsgi_app when I use it in wsgi mode. I've tried it with and without that, to no avail, however. If anyone has some insight, I will be all ears/eyes.
I managed to fix this. The issues were not specific to flask-socketio, but they were specific to Ubuntu, NginX, and gevent-socketio. Two significant issues were present:
Ubuntu 12.04 has a truly ancient version of nginx (1.1.19 vs 1.6.x for stable versions). Why? Who knows. What we do know is that this version does not support websockets in any useful way, as 1.3.13 is about the earliest you should be using.
By default, gevent-socketio expects your sockets to be at the location /socket.io . You can upgrade the whole HTTP connection, but I had some trouble getting that to work properly (especially after I threw SSL into the mix).
I fixed #1, but in fiddling with it I purged by nginx and apt-get installed... the default version of nginx on Ubuntu. Then, I was mysteriously confused as to why things worked even worse than before. Many .conf files valiantly lost their lives in this battle.
If trying to debug websockets in this configuration, I would recommend the following steps:
Check your nginx version via 'nginx -v'. If it is anything less than 1.4, upgrade it.
Check your nginx.conf settings. You need to make sure the connection upgrades.
Check that your server IP and port match your nginx.conf reverse proxy.
Check that your client (e.g., socketio.js) connects to the right location and port, with the right protocol.
Check your blocked ports. I was on EC2, so you have to manually open 80 (HTTP) and 443 (SSL/HTTPS).
Having just checked all of these things, there are takeaways.
Upgrading to the latest stable nginx version on Ubuntu (full ref) can be done by:
sudo apt-get install python-software-properties
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:nginx/stable
sudo apt-get update
sudo apt-get install nginx
In systems like Windows, you can use an installer and will be less likely to get a bad version.
Many config files for this can be confusing, since nginx officially added sockets in about 2013, making earlier workaround configs obsolete. Existing config files don't tend to cover all the bases for nginx, gevent-socketio, and SSL together, but have them all separately (Nginx Tutorial, Gevent-socketio, Node.js with SSL). A config file for nginx 1.6 with flask-socketio (which wraps gevent-socketio) and SSL is:
user <user account, probably optional>;
worker_processes 2;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
sendfile on;
# tcp_nopush on;
keepalive_timeout 3;
# tcp_nodelay on;
# gzip on;
client_max_body_size 20m;
index index.html;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
# Listen on 80 and 443
listen 80 default;
listen 443 ssl; (only needed if you want SSL/HTTPS)
server_name <your server name here, optional unless you use SSL>;
# SSL Certificate (only needed if you want SSL/HTTPS)
ssl_certificate <file location for your unified .crt file>;
ssl_certificate_key <file location for your .key file>;
# Optional: Redirect all non-SSL traffic to SSL. (if you want ONLY SSL/HTTPS)
# if ($ssl_protocol = "") {
# rewrite ^ https://$host$request_uri? permanent;
# }
# Split off basic traffic to backends
location / {
proxy_pass http://localhost:8081; # 127.0.0.1 is preferred, actually.
proxy_redirect off;
}
location /socket.io {
proxy_pass http://127.0.0.1:8081/socket.io; # 127.0.0.1 is preferred, actually.
proxy_redirect off;
proxy_buffering off; # Optional
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
}
Checking that your Flask-socketio is using the right port is easy. This is sufficient to work with the above:
from flask import Flask, render_template, session, request, abort
import flask.ext.socketio
FLASK_CORE_APP = Flask(__name__)
FLASK_CORE_APP.config['SECRET_KEY'] = '12345' # Luggage combination
SOCKET_IO_CORE = flask.ext.socketio.SocketIO(FLASK_CORE_APP)
#FLASK_CORE_APP.route('/')
def index():
return render_template('index.html')
#SOCKET_IO_CORE.on('message')
def receive_message(message):
return "Echo: %s"%(message,)
SOCKET_IO_CORE.run(FLASK_CORE_APP, host=127.0.0.1, port=8081)
For a client such as socketio.js, connecting should be easy. For example:
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/0.9.16/socket.io.min.js"></script>
<script type="text/javascript">
var url = window.location.protocol + document.domain + ':' + location.port,
socket = io.connect(url);
socket.on('message', alert);
io.emit("message", "Test")
</script>
Opening ports is really more of a server-fault or a superuser issue, since it will depend a lot on your firewall. For Amazon EC2, see here.
If trying all of this does not work, cry. Then return to the top of the list. Because you might just have accidentally reinstalled an older version of nginx.

How to use non-standard ports?

I'm trying to get moov running on port 8080 but am getting the error:
$ curl -s -i http://mlocal.nytimes.com:8080/
HTTP/1.0 534 Internal Server Error
Connection: close
Content-Type: text/plain;
Content-Length: 69
Host header 'mlocal.nytimes.com:8080' did not match project rewriters
I'm starting the server with:
$ sudo moov server -p=8080 --auto-hosts
(It appears to work fine on port 80.)
There's an additional step you need to take when you manually specify a port to run on.
Go into the project files and open config.json
Append :8080 to the domain names specified like this:
"$.nytimes.com:8080 => www.nytimes.com",
etc..