Putting a shell or a desktop in your Django app
Published June 27, 2021
This video features Maari Tamm at DjangoCon US 2021 in Online.
How to make your Django app speak SSH or RDP with Guacamole, ASGI and Daphne — giving anyone a console or a desktop right in their browser!
This talk was presented at: https://2021.djangocon.us/talks/get-interactive-putting-a-shell-or-a-in/
LINKS:
Follow Maari Tamm 👇
On Twitter: https://twitter.com/tammmri
Follow DjangCon US 👇
https://twitter.com/djangocon
Follow DEFNA 👇
https://twitter.com/defnado
https://www.defna.org/
Video production by the speaker and DjangoCon US 2021 Volunteers.
Interactive training labs can provide learners with a browser-based terminal or full desktop connected to cloud-hosted machines, making it possible to practise technologies such as Kubernetes, Ceph, OpenStack, and Terraform without access to expensive physical hardware. The speaker explains how Apache Guacamole translates SSH or RDP traffic into a browser-friendly stream, and shows how to replace Guacamole’s Java servlet client with a Python/Django implementation using PyGuacamole, Django Channels, WebSockets, and Daphne. The Django consumer retrieves connection details from the database, forwards data in both directions, supports read-only sessions, and runs alongside a conventional Django application behind Nginx.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
Hi, I am Mari and I will be talking to you about how to get interactive, how to put a shell or a desktop right into your Django app. Okay, so what does that look like exactly? Let me show you what I mean. So I work in City Network in the education team, and what my team does is that we run these learning platforms One of which is the City Cloud Academy that you're seeing on your screen right now. Our platforms are based on OpenEX, which is a free open source learning management system that anyone can run. It also happens to be a big set of Django applications itself, but this is not what I will be focusing on today. Open edX also has its own annual conferences and there are plenty of talks out there to catch up on if you're interested.
Okay, so let's look at the City Cloud Academy a bit more. So we run this learning platform to teach people complex technologies like Ceph, Kubernetes, OpenStack, Terraform, that sort of thing. And I hope that you agree with us that most people learn best by doing. So you don't want to just read about all those things or look at someone else do them. You of course want to try out everything yourself. And because all these technologies are inherently distributed, you want to learn them on distributed systems as well. You want to learn them on real platforms, like a lab that your company might have available in hardware. But a hardware lab is expensive and it's probably not very easy to get access to that sort of resource. And sometimes that can be outright impossible.
So in comes cloud technology. As it happens, I work for a cloud company and that does come in handy for providing interactive labs as we do, but this is also not what I will be focusing on today. Okay, as I promised, let me show you what I mean by bringing a shell into your browser. So here we open up a page on the learning management system DLMS. And it contains this window, an interactive terminal, that it is an entry point to your own little lab. So in this case, what the learner is doing, they drop into the terminal, they look around a little bit, and then they decide that they're about to develop and compile something. So they start by installing the Build Essential Meta package. And without the learning knowing it, once they first hit this page, the LMS made an API call to the cloud platform on their behalf
and spun up a stack, a lab environment for them. In this case it's just one Ubuntu machine, but it could be five, it could be ten, it could be three that each runs a number of containers, whatever. And then the learner was presented with this terminal right there in their browser. And as you can see, this terminal is of course fully interactive. And as you can see in the next slap we're progressing to here, that window doesn't necessarily need to be a terminal. It can be a full-blown desktop as well. And what you see here is just a demonstration that yes, this thing is a fully interactive platform. For example, we can open up LibreOffers Writer and write some text in it. Now, I'm not here today to talk about all the details of how the interaction
with this cloud platform works, or how the lab stack spin up, or how they go to sleep when you don't use them, or how they magically wake up if you come back to them because that has already been done by my colleagues and I will share the links in the end of this talk Okay, so why am I here today and what is this talk about? Well this talk is about zooming in on precisely how this entry point to your only lab works How exactly can you make a fully interactive terminal or a desktop window pop up in someone's browser using Django of course? So the technology that this all revolves around is Apache Guacamola. And I'm going to do a bit of a refresher on
guacamola terms and technology here. So if you're not familiar with it, this should be enough to give you a general overview. If you're already familiar with Apache Coacamola, as I said, this is just a bit of a refresher. So from here on out I'm going to assume two things. One, that you are a learner on our platform and you've opened up a page that contains a lab. That lab has now successfully spun up a random box somewhere in the cloud as you just saw. So there's something for us to work with. And uh two, we are able to connect uh to an IP address that has been exposed on that box and two TCP ports. One for secure shell that's usually port 22, and uh one for RTP, that's usually port 3389. We could also be using VNC,
but the same principle applies, so I'll just stick with SSH and RP for now Okay, so the first thing we'll look at is the Guacamole server, GuacT. This is a server binary that's written in C, so it's very fast and efficient And it connects to upstream SSH or RDP services using protocol plugins. So to the services running on your box, the SSH and RDP daemon This QuackT server acts as an SSH or RTP client. And I'm mentioning this because the terms can get a bit confusing here as you'll see shortly. So just stay with me. The Quack D server's job is to take all these various upstream protocols and translate them into a univide
event stream using what's called the Quacomola protocol. The idea is that whatever comes down the pipe, whether it's SSH, RDP, V and C or whatever, it's translated into one event stream that's always the Coacamola protocol. Okay, but your browser of course doesn't speak the Coacamola protocol. It speaks HTTP and WebSockets. So we need another translation engine. And here's where the naming gets more confusing. Because in Gokomora's documentation, this thing is called the Gokamura client Although it's very much a server to your browser, but the naming is correct in the sense that it really is the client part of the communication between two endpoint speaking Guacamola
protocol, the server being GuacT. So the Coacomola client is a thing that speaks the Coacamola protocol on one end to the QuagDis server and WebSockets on the other end to your browser. And then to complete the picture, you've got some JavaScript on the browser that is responsible for rendering what it gets from the WebSocket stream. So, this is the general architecture we're talking about. Keep this picture in mind, I'll come back to it a few more times during this talk Now the Coacamola client, which those of you who know Coacamola will be most familiar with, is a Java Surlet application. And most of the time what people do is they deploy CoacT and deploy the Coacamola Servet
application. And what they get is a nice remote desktop manager. And this bit here is an admittedly really old version of Guacamola being showcased in a little video that you can find on Guacamola's website. And you can see how you can connect from that remote desktop manager to some Windows host and Linux host and anything that speaks one of the protocols that Quack T can understand. Now, if you don't need the standard Coca-Mola remote desktop manager and you want to incorporate the Coca-Mola into your own application, the developers give you a nice how-to for how to build your own. Again using a Java service stack.
Okay, uh but now why if you don't want to take the Java detour For example, what if you already have some data in Django models that you can nicely access in the Django ORM that you somehow want to make accessible to Guacamola? As a simple example, you may be generating one-time SSH keys for connecting to your labs. You create these keys when you spin up a lab and you delete them when you throw away the lab. So every key is always for one lab only and you never reuse those keys. So you want to store that data in Django somewhere, like two file fields pointing to the private and public key files. So when you tear down the lab environment, you'll throw the keys away.
But while the lab is alive, you want to grab that data from your database and use it to create a Guacamola connection. So what if you could bypass the Java servit bits altogether? Maybe you don't want to add a Java runtime environment and servit container to your deployment chain. Maybe you're a pure Python shop and you simply don't have Java development capacity in your team, or maybe you just really really like using Django. So, what if you could write a Coacamola client in Python using Django that has access to everything that's in your data model and that you can just plug into your Django deployment pipeline? Well as it turns out you can do exactly that.
So enter a pure Python Guacamola client Py Guacamola. Now uh what the heck is that? Well uh as we just saw to replace the Java servlet we need a client that can talk to the Coacamola server Quack D on one end and to the browser on the other end So PyCoacamola is a Python library that gives us the client for communicating with QuacT. And we're going to look at how to use it in just a bit, but before we do, we still need to connect a few dots. We also need the browser to talk to this client. And our goal as you know is to get a terminal or a desktop window to the browser and we want to be able to interact with it. So we need to connect to this client in a way that allows bi-directional
communication and for that we'll need to use WebSockets. And so as all of you probably know to handle any other protocols aside from HTTP in a Django project, we'll need to use channels. Which is built on ASCII , asynchronous server gateway interface that supports multiple protocols. And the basic unit of Channel's code is a consumer And this is actu uh exactly what we're still missing. We need to build a consumer to our application. Alright, so let's look at the code. Let's start with the view. Our project that brings the terminal or the desktop window to your browser is called the Hastexo XBlock. And it is a plugin
to OpenEdX edX platform. So the view will be rendered as part of what we call the unit page. So the view will be here, the student view in hastexo. py And just to show you really quick, this is where we render the main template and load the related JavaScript files. The JavaScript part actually stays the same. It works exactly as it would with the Java service, so we don't need to look into that. Now let's move on to the most important part: the consumer. py file. Since we are using WebSockets here and this communication runs in an asynchronous manner, we are implementing the async WebSocket Consumer from channels. And we are calling it the Guacamola WebSocket Consumer.
So two things we need to define here, a client and a task. So this is where PyCocamola comes in. On WebSocket Connect, we are going to import the client from PyCoacamola and initialize it. For our target stack, what we want to connect to, we get the information from the database. So using database sync to async from channels, we can get the stack. As you can see here in our get stack method. Now back to the Coca-Mola client. We need to provide the host name and port for Quacti and we are going to call handshake. So let's go over the parameters for this. We need to provide the protocol for the connection with the target. In our case, this is either SSH or RDP.
And we need to pass the host name, port, username, password, and private key to the target. We can also customize the size of the window we draw to the browser by passing the width and height parameters here in pixels. And we also have some additional customization options here, as you can see, color scheme, font name and font size. Now there are more options that you can find in the Guacamole documentation So now when we are connected to the client, we are going to create a task, open the communication and accept the connection. When we open the connection, we start receiving data from the Coacamola client and if we get something, we send it to the WebSocket.
And everything we receive from the WebSocket here we send to the Guacamola client. For example, what one will type in the terminal window in the browser will be passed to the Coacamola client here as a key event. So we have a use case where we want to display a terminal window in a read-only mode. So this is what you're seeing here. When switched on, we just block uh we'll we ignore it to be more exact. All mouse and key events. And the last thing we do here is cancel the task and close the client connection when the WebSocket gets disconnected. And that's all we need for the consumer. It's less than 100 lines of code. What we also need to do is define the application in routing.
py and point the WebSocket to our Guacamole WebSocket consumer. And in settings. py, amongst other things, we point to our ASCII application, which is the Hastexo Coca-Cola client routing application. And note that the channels also be added to the installed apps. Okay, so now we've gone through the code, but how do we run it? Well, there's an official uh ASCII HTTP WebSocket server called Daphne, which is uh maintained by the Channels project We really have no reason not to use it and look for other options which there are. But for us we are using Daphne. But in order to scale the number of processes, we are running it with supervisor D
And there is a great example for this in the channels documentation. I'll just show you that real quick. As you can see, we have an example setup description that gives us the supervisor configuration file. And this is what we used as a reference. Now finally, how does this all work together? You may have noticed that we run one application, OpenAdex LMS, that is all classic HTTP and Django and GUInicorn and WSGI Together with another, the Guacamola client that runs with the WebSockets, Async, Django, Channel, Safney, and ASCII. And what we do here is something that's very common in OpenEDX
all over the place, which is to run everything behind Nginx. OpenEX has a bunch of separate services besides the LMS, such as the OpenEX Studio, a course authoring tool, and a certificate verification service and several others So to unify things like SSL certificate management and even having one IP address for all services, we just throw everything behind Nginx. So to sum it all up, let's follow the track of how an RTP or SSH session ends up in an interactive browser window one more time. So the RDP traffic originates with the upstream server anywhere in the world
and QuacT receives that traffic. Translates it into a generic event stream and encodes it in the Coacamola protocol. This happens in a C binary. Now a Coacamola client running on the same server as QuacD takes that event stream and translates it into a WebSocket stream. This is an async Django app running in Daphne with ASCII using PyCokamola. Now Nginx proxies the WebSocket stream into a URL part hierarchy. shared with other OpenEdaccess services, themselves synchronous Django apps running in GUnicorn. Your browser received the mixed HTTP WebSocket content received from Nginx.
And one of the things your browser receives is the statically served Coca-Mole Web Client JavaScript library. And your browser takes that library and uses it to interpret the WebSocket stream and displays it in your browser window. Now you hit a key or click in the window on your browser. A JavaScript event listener notices the keystrike or pointer click and sends a WebSocket request up to Engine Nix. Now, Nginx proxies that event to the ASCII listener exposed by Daphne, and the listener takes the WebSocket event and translates it into a Coca-Mola protocol event And then uh Pay Guacamola passes the event to Guacti. Now Guacti translates it into an RDP event.
Quacti's RTP client library sends the RTP event onto the upstream server. The server processes that event sends an update back down the wire. And that's how you put a desktop or a terminal session in any browser with Django. And I'll finish up with some links and references to the building blocks of the architecture discussed in this talk And big thanks to all the contributors and developers out there. I'll start with the Pai Guacamole That is a MIT licensed Coacamola client library and we use it heavily in the Hastexo Xbox, which is a plugin to OpenEDX. And OpenEDX
and the Xbox are both licensed under the Afera GPL. And of course there's Apache Coca-Mola itself, of which we use the server component. And naturally that is Apache licensed. Also, I want to say thanks to Florian Haas who encouraged me to give this presentation and also kindly provided the architecture slides. And finally, you can find the slides for this talk in my GitHub page under the Creative Commons license. And that's all from me. Thank you very much for listening.
`guacd` connects to the upstream SSH or RDP service and translates its traffic into the Guacamole protocol. A Guacamole client then converts that stream to WebSockets for the browser, whose JavaScript client renders the session and sends user input back through the same chain.
Discussed at 5:14Use Apache Guacamole’s `guacd` server with the pure-Python PyGuacamole client, Django Channels, and WebSockets. PyGuacamole connects Django to Guacamole, while the browser uses Guacamole’s JavaScript client to render and interact with the terminal or desktop.
Discussed at 9:52Create an asynchronous Channels WebSocket consumer that initializes a PyGuacamole client, retrieves the target stack and credentials from Django models, performs the handshake, and forwards data in both directions between Guacamole and the WebSocket. It should also handle read-only mode and close the task and client when the socket disconnects.
Discussed at 12:12The handshake needs the Guacamole server host and port, the target protocol, and the target host, port, username, password, and private key. Window dimensions and display settings such as colors and font can also be supplied.
Discussed at 12:58Run the asynchronous Django application with Daphne, scaling its processes with Supervisor. The regular Django HTTP application can run under Gunicorn, and Nginx can proxy both applications and unify them behind the same public services and IP address.
Discussed at 14:33Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 14, 2026