how to get remote_ip from socket in phoenix-framework? - sockets

How to get remote_ip from socket in phoenixframework? I can get it from conn in View, but not in Channel.
Many thanks for help!

Copy of the answer provided here: https://elixirforum.com/t/phoenix-socket-channels-security-ip-identification/1463/3 (all the credit goes to https://elixirforum.com/u/arjan)
Phoenix 1.4 update:
Since Phoenix 1.4, you can get connection information from the underlying transport. What kind of information you get is transport dependent, but with the WebSocket transport it is possible to retrieve the peer info (ip address) and a list of x- headers (for x-forwarded-for resolving).
Configure your socket like this in your endpoint.ex:
socket("/socket", MyApp.Web.UserSocket,
websocket: [connect_info: [:peer_data, :x_headers]],
longpoll: [connect_info: [:peer_data, :x_headers]]
)
And then your UserSocket module must expose a connect/3 function like this:
def connect(_params, socket, connect_info) do
{:ok, socket}
end
On connect, the connect_info parameter now contains info from the transport:
info: %{
peer_data: %{address: {127, 0, 0, 1}, port: 52372, ssl_cert: nil},
x_headers: []
}
UPDATE
If your Phoenix app is not handling traffic directly and receives it from reverse proxy like nginx then peer_data will have nginx IP address, not the client's.
To fix this you can tell nginx (or whatever proxy you use) to pass original IP in the headers and then read it later.
So your phoenix location should look something like this:
location /phoenix/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://phoenix/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
and your socket code should have this:
defp get_ip_address(%{x_headers: headers_list}) do
header = Enum.find(headers_list, fn {key, _val} -> key == "x-real-ip" end)
case header do
nil ->
nil
{_key, value} ->
value
_ ->
nil
end
end
defp get_ip_address(_) do
nil
end
and change connect to something like this
def connect(params, socket, connect_info) do
socket = assign(socket, :ip_address, get_ip_address(connect_info))
{:ok, socket}
end

The answer right now is: you can't. You can't access the connection in channels because channels are transport agnostic. Open up an issue in Phoenix detailing your user case so the Phoenix team can act on it.

Good news! as of LiveView 0.17.7 it's available out of the box:
see https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html
to summarize:
in endpoint.ex find the socket definition and add
socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [:peer_data, session: #session_options]]
in the socket mount() function
def mount(_params, _session, socket) do
peer_data = get_connect_info(socket, :peer_data)
{:ok, socket}
end
note: it's only available on mount() and terminate()

Related

Is it possible to configure socket connection(not web) in Nginx reverse proxy server

I have a CLI client and server written in Go Currently, they are communicating via a socket connection and it's a server streaming connection Now I want to have an Nginx proxy between these two
Is it possible to configure the normal socket connection in Nginx? How do that, and what all code changes & configuration changes I need to do
There's not much on the internet on this on socket connection in Nginx I was wondering if it's possible or not
//my client code:
func getStreammessages() {
connection, err := net.Dial("tcp", "127.0.0.1:8888")
_, err = connection.Write([]byte(sendIDtoServertoGetStream))
for {
mLen, err := connection.Read(buffer)
//some logic to print the message stream
}
}
//my server code:
func StartStreamServer() {
server, err := net.Listen("tcp", "127.0.0.2:9988")
defer server.Close()
for {
connection, err := server.Accept()
go registerClient(connection)
}
}
func registerClient(connection net.Conn) {
buffer := make([]byte, 1024)
mLen, err := connection.Read(buffer)
var sendIDtoServertoGetStream message
err = json.Unmarshal(buffer[:mLen], &sendIDtoServertoGetStream)
}
//strem to client from message queue
func StreamMessageToCliCLient(connection net.Conn) {
_, err = connection.Write(messageString)
}
Have anyone done this before
currently, I am doing this in my Nginx (nginx.conf file) which is running in the same VM as my server
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream wsbackend {
server 127.0.0.2:9988;
}
server {
listen 8888;
location /{
auth_basic off;
proxy_ssl off;
proxy_http_version 1.1;
proxy_set_header upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_pass wsbackend;
}
location /v1/config {
proxy_pass http://$CONFIG_MGMT_HOST:$CONFIG_MGMT_PORT/v1/config;
}
}
}
I want to open Port 8888
Internally proxy pass to my server, with this setup, currently getting 400 error in log, Can you pls help Thank you
Just change your proxy_pass wsbackend; with proxy_pass http://wsbackend;

ingress-nginx config use custom variables

I have a nginx conf like below
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80 default_server;
access_log off;
return 200 'Hello, World! - nginx\n';
}
server {
listen 80;
server_name ~^(dev-)?(?<app>[^.]+)\.mysite\.com$;
access_log off;
location / {
resolver 127.0.0.11;
proxy_set_header Host $host;
proxy_pass http://${app}-web;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
I expected that redirecting
dev-blog.mysite.com into service blog-web
dev-market.mysite.com into service market-web
and so on
Is there any way to implement this in k8s ingress-nginx?
No, you would make a separate Ingress object for each (or one huge one, but that's less common). Usually this is semi-automated through either Helm charts or custom controllers.

Nginx https reverse proxy infinite loop

this is my site-available nginx configuration for flask application
server {
listen 80;
server_name _;
access_log /var/log/nginx/nginx_access.log;
error_log /var/log/nginx/nginx_error.log;
rewrite ^ https://$http_host$request_uri? permanent;
}
server {
listen 443;
server_name _;
access_log /var/log/nginx/nginx_access.log;
error_log /var/log/nginx/nginx_error.log;
ssl on;
ssl_certificate /etc/nginx/ssl/<redacted>.pem;
ssl_certificate_key /etc/nginx/ssl/<redacted>.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
I have gone through the questions
Nginx configuration leads to endless redirect loop
and nginx redirect loop with ssl. I seem to have the configuration specified in them already.
EDIT
Flask application is running via gunicorn/supervisord
Supervisor config.conf
[program:config]
command=/usr/local/bin/gunicorn run:app --config /etc/gunicorn/gunicorn.conf --preload
directory=/srv/<application>
autostart=true
autorestart=true
startretries=10
stderr_logfile = /var/log/supervisord/<application>-stderr.log
stdout_logfile = /var/log/supervisord/<application>-stdout.log
user=root
Gunicorn gunicorn.conf
bind = '0.0.0.0:5000'
backlog = 2048
workers = 3
worker_class = 'sync'
worker_connections = 1000
timeout = 30
keepalive = 2
accesslog='/var/log/gunicorn/gunicorn_access.log'
errorlog='/var/log/gunicorn/gunicorn_error.log'
pidfile = '/tmp/gunicorn.pid'
loglevel = 'debug'
Flask Application
run.py
from app import app
from app import views
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
view.py
from app import app, session
from flask import render_template, json, jsonify
import datetime
#app.route("/hello/")
def render_templates():
return render_template("display.html")
(... other code ..)
NB: I have an ELB in front of the flask application. 80 and 443 ports are open.
Input: https://example.com/hello/ Output: Redirected Loop
Any help will be appreciated.Thanks in advance.
I did figure out the issue.
The nginx configuration should have been
server {
listen 80;
server_name _;
access_log /var/log/nginx/nginx_access.log;
error_log /var/log/nginx/nginx_error.log;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
As ELB does an unloading of HTTPS encryption to HTTP request , my previous configuration was redirecting all my HTTP requests into HTTPS.

Mongodb REST API returns no results

I'm using the builtin MongoDB "simple REST interface" to try to tie together my MongoDB collection with a simple javascript frontend without writing an API myself. I have the REST server up and running, as proven by running the following inside my production server:
$> http get "http://localhost:28017/mydb/mycoll/?limit=1"
HTTP/1.0 200 OK
Connection: close
Content-Length: 437
Content-Type: text/plain;charset=utf-8
x-action:
x-ns: mydb.mycoll
{
"offset" : 0,
"rows": [
{ <redacted data> }
],
"total_rows" : 1 ,
"query" : {} ,
"millis" : 0
}
The Mongodb REST server does not by, by default, bind to an interface which is publicly accessible, so I'm proxying localhost:28017 to the outside world using Nginx, with the following configuration:
server {
listen 80;
server_name api.myapp.com;
rewrite ^ https://$server_name$request_uri? permanent;
}
server {
listen 443 default_server;
server_name api.myapp.com;
ssl on;
ssl_certificate /etc/ssl/myapp.crt;
ssl_certificate_key /etc/ssl/myapp.key;
access_log /logs/nginx_access.log;
error_log /logs/nginx_error.log;
location /mongo {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://127.0.0.1:28017;
break;
}
}
}
I reload nginx and try the same query I ran inside the machine hosting Mongo from a machine outside that network instead, and get:
$> http get "https://api.myapp.com/mongo/mydb/mycoll/?limit=1"
HTTP/1.1 200 OK
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/plain;charset=utf-8
Date: Fri, 27 Feb 2015 17:06:40 GMT
Server: nginx/1.4.6 (Ubuntu)
Transfer-Encoding: chunked
Vary: Accept-Encoding
x-action:
x-ns: mongo.mydb.mycoll
{
"offset" : 0,
"rows": [
],
"total_rows" : 0 ,
"query" : {} ,
"millis" : 0
}
Essentially, it is hitting the Mongo REST server (making it through the nginx proxy), but it is not returning any results for the same query that I ran on localhost.
The only lead I have so far is that the x-ns header on the two responses is different - like the Mongo REST server is not picking up the fact that Nginx is instructing it to lie behind the https://api.myapp.com/mongo route and is instead thinking I'm trying to access the database mongo.
Thanks for your help.
Solved my own issue. The problem was as I guessed at the end of my question: the Nginx config was proxying requests to the Mongo API server with the /mongo/ path intact, resulting in queries being run on the (nonexistent) database mongo.
I fixed the problem by adding the following line to the /mongo/ location in the Nginx configuration file:
rewrite /mongo/(.+) /$1 break;
so that it reads, in full:
location /mongo/ {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
rewrite /mongo/(.+) /$1 break;
proxy_pass http://127.0.0.1:28017;
break;
}
}
Thanks to anyone who gave this question a look.

Nginx: Redirect Conditional on Server Name and Sub Domain for Short URLs

I want to redirect conditionally based on the server name, but where I redirect to also depends on the subdomain. So for example, here is my basic config
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443;
server_name company.com compa.ny;
ssl on;
ssl_client_certificate /etc/ssl/certs/godaddy_CA.crt;
ssl_certificate /etc/ssl/certs/wildcard.company.com.crt;
ssl_certificate_key /etc/ssl/private/wildcard.company.com.key;
ssl_prefer_server_ciphers on;
root /var/www/company;
access_log /var/log/nginx/nginx.access.log;
error_log /var/log/nginx/nginx.error.log;
client_max_body_size 8M;
location ^~ /application {
proxy_set_header HOST $http_host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://127.0.0.1:8080;
}
}
I want to have something that looks for the short url host "compa.ny" and redirects to "company.com/shortUrldRedirector" and I also want to include the subdomain, so in dev or qa this will work correctly:
https://compa.ny/abc123 -> https://company.com/shortUrldRedirector/abc123
and
https://dev.compa.ny/abc123 -> https://dev.company.com/shortUrldRedirector/abc123
I see there is a $server_name config variable, but how do I accomplish the above redirects respecting the subdomain?
I would use map construction like this:
map $http_host $long_domain {
default company.com;
dev.compa.ny dev.company.com;
compa.ny company.com;
}
server {
...
return 301 https://$long_domain/shortUrldRedirector$request_uri;
}