Client IP when behind AWS network load balancer from Python - python-3.x

When running a socket server behind a network load balancer, with instances specified by IP, ...
server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=socket.IPPROTO_TCP)
server_sock.setblocking(False)
server_sock.bind(('', port))
server_sock.listen(socket.IPPROTO_TCP)
loop = asyncio.get_event_loop()
sock, adddress = await loop.sock_accept(server_sock)
... then...
print(adddress)
prints what appears to be a private IP of the network load balancer itself.
How can I determine the original client IP?

You can enable Proxy Protocol V2, and then write a function to parse the proxy protocol header [which gets sent ahead of incoming data]...
import asyncio
import collections
import socket
import struct
PP_V2_SIGNATURE = b'\r\n\r\n\x00\r\nQUIT\n'
PP_V2_COMMANDS = {
b'\x00': 'LOCAL',
b'\x01': 'PROXY',
}
PP_V2_VERSIONS = {
b'\x02': '2',
}
PP_V2_ADDRESS_FAMILIES = {
b'\x00': 'AF_UNSPEC',
b'\x01': 'AF_INET',
b'\x02': 'AF_INET6',
b'\x03': 'AF_UNIX',
}
PP_V2_PROTOCOLS = {
b'\x00': 'UNSPEC',
b'\x01': 'STREAM',
b'\x02': 'DGRAM',
}
PP_V2_ADDRESS_FORMATS = {
'AF_INET': '4B4BHH',
'AF_INET6': '16B16BHH',
'AF_UNIX': '108B108B',
}
ProxyProtocolV2Header = collections.namedtuple('ProxyProtocolV2Header', [
'version',
'command',
'protocol',
'address_family',
'address',
])
ProxyProtocolIpAddress = collections.namedtuple('ProxyProtocolIpAddress', [
'source_ip',
'source_port',
'dest_ip',
'dest_port',
])
async def proxy_protocol_header_recv(loop, sock):
# The header, itself has a header
header_format = '>12sccH'
header_length = 16
header_raw = await recv_num_bytes(loop, sock, header_length)
header_unpacked = struct.unpack(header_format, header_raw)
signature = header_unpacked[0]
version_and_command = header_unpacked[1][0]
protocol_and_address_family = header_unpacked[2][0]
address_length = header_unpacked[3]
if signature != PP_V2_SIGNATURE:
raise Exception('Incorrect proxy protocol signature')
version = PP_V2_VERSIONS[bytes([version_and_command >> 4])]
command = PP_V2_COMMANDS[bytes([version_and_command & 0x0f])]
protocol = PP_V2_PROTOCOLS[bytes([protocol_and_address_family & 0x0f])]
address_family = PP_V2_ADDRESS_FAMILIES[bytes([protocol_and_address_family >> 4])]
address_raw = await recv_num_bytes(loop, sock, address_length)
address_format = PP_V2_ADDRESS_FORMATS[address_family]
address = struct.unpack(address_format, address_raw[:struct.calcsize(address_format)])
return ProxyProtocolV2Header(
version=version,
command=command,
protocol=protocol,
address_family=address_family,
# For IPV6/Unix, you'll need to do something else
address=ProxyProtocolIpAddress(
source_ip='.'.join(str(part) for part in address[:4]),
source_port=address[8],
dest_ip='.'.join(str(part) for part in address[4:8]),
dest_port=address[9],
) if address_family == 'AF_INET' else None,
)
async def recv_num_bytes(loop, sock, num_bytes):
incoming_buf = bytearray(num_bytes)
incoming = memoryview(incoming_buf)
bytes_in_total = 0
while bytes_in_total != num_bytes:
bytes_just_in = await loop.sock_recv_into(sock, incoming[bytes_in_total:])
if bytes_just_in == 0:
raise Exception('Socket closed')
bytes_in_total += bytes_just_in
return incoming_buf
... which you you can use as, for example:
async def main(loop):
port = 5023
server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=socket.IPPROTO_TCP)
server_sock.setblocking(False)
server_sock.bind(('', port))
server_sock.listen(socket.IPPROTO_TCP)
while True:
print('Waiting for connection')
sock, adddress = await loop.sock_accept(server_sock)
header = await proxy_protocol_header_recv(loop, sock)
print(header.address.source_ip)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))

with instances specified by IP
...you can't.
With instances (targets) specified by IP, Network Load Balancer doesn't have the ability to hook sufficiently deeply into the network infrastructure to preserve the client IP. When you specify instances by instance ID, the client IP is automatically preserved.
You can configure a target group so that you register targets by instance ID or IP address. If you specify targets using an instance ID, the source IP addresses of the clients are preserved and provided to your applications. If you specify targets by IP address, the source IP addresses are the private IP addresses of the load balancer nodes.
https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html

AWS load balancers place the client ip into the 'X-Forwarded-For' header
so simple query it by:
class CustomHandler(http.server.SimpleHTTPRequestHandler):
...
def do_GET(self):
aws_forwarded_ip = self.headers.get('X-Forwarded-For')
...

Related

Problems about concurrent task in FastAPI

dear community
I've tried to execute async def in FastAPI app.
the workflow is crate FastAPI service to recieve from end-user requests and send it to another service such as DB Writer service.
first, I've create async def for send request with aiosonic library
Here's the code
import aiosonic
from aiosonic.timeout import Timeouts
async def db_writer_requests(arrival_input, prediction_out) :
client = aiosonic.HTTPClient()
timeouts_settings = Timeouts(sock_connect = 10,sock_read = 10)
await client.post('http://127.0.0.1:8082/api/motor/writer/test1',
headers = {'Content-Type' : 'application/json'},
json = arrival_input,
timeouts = timeouts_settings)
client.shutdown()
Here's main app
#app.post('/api/motor/test')
async def manager_api(raw_input:arrival_requests) :
depart_json = dict(raw_input)
inc_preds, model_error = await predict_requests(depart_json)
if (inc_preds == None) or (inc_preds['status_code'] != 200) :
return inc_preds if model_error == None else model_error
else :
mlid = uuid.uuid4()
pred_output = model_output(
predict_uuid = str(mlid),
net_premium = str(inc_preds["result"]["data"]["net_premium"]),
car_repair = str(inc_preds["result"]["data"]["car_repair"]),
place_repair = str(inc_preds["result"]["data"]["place_repair"]),
insurer_tier = str(inc_preds["result"]["data"]["insurer_tier"])
)
send_msg = good_response_msg(
status = 'OK',
status_code = 200,
result = data(
data = pred_output
)
)
await db_writer_requests(depart_json,dict(pred_output))
return send_msg
when I've tried to send request.
case 1 I've not to use "await", the program not send request to service and not show any response in endpoint service.
case 2 I've use await it worked normally, but if endpoint service not available the main service shown "Internal Server Error".
Thank you for Advance

Trouble getting my server to respond back to client | Python | Sockets

I'm trying to repurpose this script, I want the server to send messages to my client, and I want the client to be the one that 'reaches out' and makes the connection. As you can see, with the script I have, the client is the one reaching out but it's the one that can send the messages to the server. I've tried looking at other scripts, moving the send and handle function around, but I feel like my inexperience with sockets got me stumped. Any help would be appreicated.
Server:
import socket
import threading
HEADER = 64
PORT = 5430
SERVER = '192.110.100.189' # socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = '!END'
# defines the type of connection
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
def handle_client(conn, addr):
print(f'[NEW CONNECTIONS] {addr} connected')
connected = True
while connected:
# the header variable here acts as a protocol to give us an idea of how many bytes are being sent
msg_length = conn.recv(HEADER).decode(FORMAT)
if msg_length:
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(FORMAT)
if msg == DISCONNECT_MESSAGE:
break
print(f'{addr} | {msg}')
conn.send('msg recieved'.encode(FORMAT))
conn.close()
def start():
server.listen()
print(f'[LISTENING] Server is listening on {SERVER}')
while True:
conn, addr = server.accept()
thread = threading.Thread(target = handle_client, args = (conn, addr))
thread.start()
print(f'[ACTIVE CONNECTIONS] {threading.activeCount() - 1}')
print('[STARTNG] Server is starting')
start()
Client:
import socket
HEADER = 64
PORT = 5430
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = '!END'
SERVER = '192.110.100.189'
ADDR = (SERVER, PORT)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)
def send(msg):
message = msg.encode(FORMAT)
msg_length = len(message)
send_length = str(msg_length).encode(FORMAT)
send_length += b' ' * (HEADER - len(send_length))
client.send(send_length)
client.send(message)
while True:
enter = input('CLIENT - ')
send(enter)
if enter == DISCONNECT_MESSAGE:
break

Socket chat app Freezing when sending a message

Hi I was following a tech with Tim tutorial about sockets and I am building a chat app and I am trying to create a dictionary with the names and the IP in it. but when I connect and type my name the client-side app freezes but I don't know why so can you see if you can help me this is the code.
Server-side code
import socket
import threading
import io
HEADER = 64
PORT = 5050
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "dis"
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
Names = {}
def handle_client(conn, addr):
print(f"[NEW CONNECTION] {addr} connected.")
connected = True
while connected:
msg_length = conn.recv(HEADER).decode(FORMAT)
if msg_length:
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(FORMAT)
if msg.split(':::')[0] == 'Name':
name = msg.split(':::')[-1]
Names[addr] = name
print(Names.get(addr))
elif msg == DISCONNECT_MESSAGE:
connected = False
print(f'[{addr}] Has disconnected')
# Names.pop(addr)
else:
conn.send('message recieved'.encode(FORMAT))
print(msg)
conn.close()
def start():
server.listen()
print(f"[LISTENING] Server is listening on {SERVER}")
while True:
conn, addr = server.accept()
thread = threading.Thread(target=handle_client, args=(conn, addr))
thread.start()
print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")
print("[STARTING] server is starting...")
start()
And this is the client-side code
import socket
HEADER = 64
PORT = 5050
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "dis"
SERVER = '127.0.1.1'
print(SERVER)
Name = input('Enter your name: ')
ADDR = (SERVER, PORT)
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(ADDR)
def send(msg):
message = msg.encode(FORMAT)
msg_length = len(message)
send_length = str(msg_length).encode(FORMAT)
send_length += b' ' * (HEADER - len(send_length))
client.send(send_length)
client.send(message)
print(client.recv(2048).decode(FORMAT))
send('Name:::' + Name)
while True:
msg = input('what message do you want to send type dis to disconnect: ')
if msg != DISCONNECT_MESSAGE :
send(msg)
elif msg == DISCONNECT_MESSAGE:
send(msg)
break
elif msg == None:
print("please type a message and don't leave it a blank")
In your client code, under function send(msg) you are expecting response from server once the client sent a message, and you are using the same function for sending username as well.
In the server you have not coded to respond for username.
Hence your client is actually waiting for response from server, for the username it just sent. That's why it looks frozen.
Adding a response like Hello username in server will resolve this.
Server
import socket
import threading
import io
HEADER = 64
PORT = 5050
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "dis"
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
Names = {}
def handle_client(conn, addr):
print(f"[NEW CONNECTION] {addr} connected.")
connected = True
while connected:
msg_length = conn.recv(HEADER).decode(FORMAT)
if msg_length:
msg_length = int(msg_length)
msg = conn.recv(msg_length).decode(FORMAT)
if msg.split(':::')[0] == 'Name':
name = msg.split(':::')[-1]
Names[addr] = name
print(Names.get(addr))
conn.send(f'Hello {name}'.encode(FORMAT))
elif msg == DISCONNECT_MESSAGE:
connected = False
print(f'[{addr}] Has disconnected')
# Names.pop(addr)
else:
conn.send('message recieved'.encode(FORMAT))
print(msg)
conn.close()
def start():
server.listen()
print(f"[LISTENING] Server is listening on {SERVER}")
while True:
conn, addr = server.accept()
thread = threading.Thread(target=handle_client, args=(conn, addr))
thread.start()
print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")
print("[STARTING] server is starting...")
start()

Searching for Socket hosts and displaying their IP Addresses Python

I'm creating a local client-server game on Python using sockets where a server is hosted on the local network and the clients on the network can connect to the server if they have the server's IP address. It's not a dedicated server so it just runs on another thread on a client's computer. Currently, the client has to manually enter the IP address of the server to connect to it. I want the client to be able to search the network for avaialble servers join, but i'm not sure how to get the server to broadcast it's availability.
This is the server-side script:
totalConnections = 0
port = 5555
host=socket.gethostname()
IP = socket.gethostbyname(host) #this just fetches the computer's IP address so the server can use it
server = IP
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.bind((server, port))
except socket.error as e:
str(e)
s.listen(2)
print("Waiting for a connection, Server Started")
while True:
conn, addr = s.accept()
print("Connected to:", addr)
totalConnections += 1
start_new_thread(threaded_client, (conn, currentPlayer))
currentPlayer += 1
def threaded_client(conn, player):
conn.send(pickle.dumps(all_data[player])) # sends player data to client
While True:
conn.sendall(pickle.dumps(reply))
This is the client-side:
class Network:
def __init__(self,ip):
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server = ip
self.port = 5555
self.addr = (self.server, self.port)
self.p = self.connect()
def getP(self):
return self.p
def connect(self):
try:
self.client.connect(self.addr)
return pickle.loads(self.client.recv(2048*3))
except:
pass
def send(self, data):
try:
self.client.send(pickle.dumps(data))
return pickle.loads(self.client.recv(2048*3))
except socket.error as e:
print(e)
I found the solution using multicasting on a specific UDP port where all the clients connect to so the data can be shared amongst them : https://pymotw.com/2/socket/multicast.html

Connection error between server client OS error

Hello I am pretty much new to socket and I was trying to make a connection inside my local computer using socket.
this is the server
import socket
def server():
host = socket.gethostname() # get local machine name
port = 8080 # Make sure it's within the > 1024 $$ <65535 range
s = socket.socket()
s.bind(('192.168.56.1', port))
s.listen(1)
client_socket, adress = s.accept()
print("Connection from: " + str(adress))
while True:
data = s.recv(1024).decode('utf-8')
if not data:
breakpoint
print('From online user: ' + data)
data = data.upper()
s.send(data.encode('utf-8'))
s.close()
if __name__ == '__main__':
server()
and this is the client
import socket
def client():
host = socket.gethostname() # get local machine name
port = 8080 # Make sure it's within the > 1024 $$ <65535 range
s = socket.socket()
s.connect(("192.168.56.1", port))
message = input('-> ')
while message != 'q':
s.send(message.encode('utf-8'))
data = s.recv(1024).decode('utf-8')
print('Received from server: ' + data)
message = input('==> ')
s.close()
if __name__ == '__main__':
client()
I know there are useless lines in there but I will clean it up after I finish my connection .

Resources