Autobahn|Python Twisted server that checks API key and disconnects clients - python

I want to add a simple API key check to an Autobahn Python WebSocket server. The server should check the key in the HTTP header of a client and disconnect clients that don't have the correct key.
I have figured out a solution to this, but I'm not sure it is the best solution (see below). If anyone has suggestions, I'd appreciate it.

From the API Docs for the onConnect method:
Throw autobahn.websocket.types.ConnectionDeny when you don’t want to accept the WebSocket connection request.
You can see this done on line 117 of one of the examples here.
I have tested this and it does not close the connection cleanly. However you are terminating a connection with an unauthenticated client so you should not want to go through a closing handshake.
The onClose callback takes a wasClean argument which allows you to differentiate between clean and unclean connection closures.

My solution is to check the HTTP header after the client has connected to the server and to close the connection if the client does not have a valid API key.
MY_API_KEY = u'12345'
class MyServerProtocol(WebSocketServerProtocol):
def onConnect(self, request):
print("Client connecting: {}".format(request.peer))
def onOpen(self):
# Check API Key
if 'my-api-key' not in self.http_headers or\
self.http_headers['my-api-key'] != MY_API_KEY:
# Disconnect the client
print('Missing/Invalid Key')
self.sendClose( 4000, u'Missing/Invalid Key')
# Register client
self.factory.register(self)
I found that if I close the connection inside onConnect, I get an error saying that I cannot close a connection that has not yet connected. The above solution closes cleanly on the client side, but behaves oddly on the server side. The log output is
dropping connection: None
Connection to/from tcp4:127.0.0.1:51967 was aborted locally
_connectionLost: [Failure instance: Traceback (failure with no frames): <class 'twisted.internet.error.ConnectionAborted'>: Connection was aborted locally, using.
]
WebSocket connection closed: None
Is the reason the close message is None on the server side that the server closed the connection and the client did not send back a reason? Is there a better way to do this?
Update:
I have accepted Henry Heath's answer because it seems to be the officially supported solution, even though it does not close the connection cleanly. Using autobahn.websocket.types.ConnectionDeny, the solution becomes
from autobahn.websocket.types import ConnectionDeny
MY_API_KEY = u'12345'
class MyServerProtocol(WebSocketServerProtocol):
def onConnect(self, request):
print("Client connecting: {}".format(request.peer))
# Check API Key
if 'my-api-key' not in request.headers or\
request.headers['my-api-key'] != MY_API_KEY:
# Disconnect the client
print('Missing/Invalid Key')
raise ConnectionDeny( 4000, u'Missing/Invalid Key')
def onOpen(self):
# Register client
self.factory.register(self)
Note that within onConnect, HTTP headers are accessible with request.headers whereas within onOpen, they are accessible with self.http_headers.

Related

How to create a node js API for which users can subscribe to listen to events?

I am trying to create and node.js api to which users can subscribe to get event notifications?
I created the below API and was able to call the API using python ,however its not clear to me how can folks subscribe to it?
How can folks subscribe to this API to get notification of New root build released?what do I need to change?
node.js API
app.get("/api/root_event_notification", (req, res, next) => {
console.log(req.query.params)
var events = require('events');
var eventEmitter = new events.EventEmitter();
//Create an event handler:
var myEventHandler = function () {
console.log('new_root_announced!');
res.status(200).json({
message: "New root build released!",
posts: req.query.params
});
}
import requests
python call
input_json = {'BATS':'678910','root_version':'12A12'}
url = 'http://localhost:3000/api/root_event_notification?params=%s'%input_json
response = requests.get(url)
print response.text
OUTPUT:-
{"message":"New root build released!","posts":"{'root_version': '12A12', 'BATS': '678910'}"}
You can't just postpone sending an http response for an arbitrary amount of time. Both client and server (and sometimes the hosting provider's infrastructure) will timeout the http request after some number of minutes. There are various tricks to try to keep the http connection alive, but all have limitations.
Using web technologies, the usual options for get clients getting updated server data:
http polling (client regularly polls the server). There's also a long polling adaptation version of this that attempts to improve efficiency a bit.
Websocket. Clients makes a websocket connection to the server which is a lasting, persistent connection. Then either client or server can send data/events of this connection at any time, allowing the server to efficiently send notifications to the client at any time.
Server Sent Events (SSE). This is a newer http technology that allows one-way notification from server to client using some modified http technology.
Since a server cannot typically connect directly to a client due to firewall and public IP address issues, the usual mechanism for a server to notify a client is to use either a persistent webSocket connection from client to server over which either side can then send webSocket packets or use the newer SSE (server sent events) which allows some server events to be sent to a client over a long lasting connection.
The client can also "poll" the server repeatedly, but this is not really an event notification system (and not particularly efficient or timely) as much as it is some state that the client can check.

Determine When Flask Server Is Ready For Requests

How can I determine when a flask server is ready to receive traffic?
For example, I have server A, inside a dockerfile, that sends a request to an already running server B, announcing its existence upon the container being run.
However once the already running server (B) recieves that request, it tries to send data to one of server A's routes, receiving the error: Failed to establish a new connection: [Errno 111]
Is there a flask or external resource that can send a request in the server when it is ready to receive resulting traffic?
I just pull the server state by sending a simple GET request:
#app.route('/srv_status', methods=['GET'])
def get_srv_status():
return 'OK', 200 # just means we're on air
If it replied, that means the server is ready to process the requests.
Additionally, it can serve for monitoring purposes.
PS: it's been a while after your question. It would be interesting to learn how you've solved the problem.

rabbitmq credential issue. only works with localhost

I have a question regarding rabbitmq. I am trying to set up a messaging system based off the queue name and so far everything has been good with localhost. As soon as I set some credentials for local connection, I get a timeout error. (I have lengthened the timeout time as well.) I have also gave the user administrative privileges and the guest account administrative privileges. I get this error when ran with both the consume and produce script. Port 5672 is open as well. This is all being done on a ubuntu 14.04 LTS machine and python 2.7.14. Guest and my other rabbit user are both allowed to use the default vhost too.
import pika, json
credentials = pika.credentials.PlainCredentials('guest', 'guest')
connection = pika.BlockingConnection(pika.ConnectionParameters('<ip here>',
5672, '/', credentials))
channel = connection.channel()
result = channel.queue_declare(queue='hello', durable=True)
def callback(ch, method, properties, body):
print "localhost received %r" % json.loads(body)
channel.basic_consume(callback,
queue='hello')
print 'localhost waiting for messages. To exit press CTRL+C'
channel.start_consuming()
channel.close()
Here is my error message too. Just a timeout method which would make me think that the connection is failing and this is a network problem but when I replace my ip in the credentials with 'localhost', everything works fine. Any ideas?
pika.exceptions.ConnectionClosed: Connection to <ip here>:5672 failed: timeout
The RabbitMQ team monitors the rabbitmq-users mailing list and only sometimes answers questions on StackOverflow.
You are probably running into multiple issues.
First of all, the guest user can only connect via localhost by default. This document goes into detail. FWIW that document is the first hit when site:rabbitmq.com localhost guest are used as search terms on google.
Second, timeout means that the TCP connection can't be established. Please see this document for a step-by-step guide to diagnosis.

Can I/How do I access client side cookies through a websocket connection to a twisted app?

i have a simple twisted application, with the websockets module. the application is serving a lineReceiver protocol, and i'd like to inspect the contents of incoming connections, including seeing which cookies are set on them, something like this:
class Echo(basic.lineReceiver):
def connectionMade(self):
print "Got new client!"
#print request.cookies
is it possible for me to access request (client-side) cookies from a protocol that's served over websockets? if so, how do I do it?
Here is a complete example that includes a WebSocket client and a server that set/get custom HTTP headers. Adapting this you can access cookies sent by e.g. a browser during the initial WebSocket opening handshake on server-side.
Disclosure: I am original author of Autobahn and work for Tavendo.

Twisted server TLS hanging on connection

I have a twisted webserver with TLS authentication, and it appears to hang when I connect to it over SMTP. Here is the block of twisted code to start the server:
(Note: certificateData is our private key and public key concatenated together, that appeared to be the only way to get a self signed certificate to work)
customFactory = CustomSMTPFactory(portal)
certificate = PrivateCertificate.loadPEM(certificateData)
contextFactory = certificate.options(certificate)
tlsFactory = TLSMemoryBIOFactory(contextFactory, False, customFactory)
a = service.Application("Custom Server")
internet.TCPServer(5870, tlsFactory).setServiceParent(a)
On the client, this line just hangs waiting to read data:
smtplib.SMTP('localhost',5870)
Any ideas? How do I setup TLS authentication on a twisted webserver?
Your server starts TLS from the beginning of the connection. Try smtplib.SMTP_SSL instead, so your client expects this.

Categories

Resources