Privilege-separated installation of many Django applications on the same host

This video is from Django Day Copenhagen 2023 in Copenhagen, Denmark.

Privilege-separated installation of many Django applications on the same host
0:31:48
Published October 8, 2023
195 views

"Privilege-separated installation of many Django applications on the same host" at Django Day Copenhagen 2023. Talk description at: https://2023.djangoday.dk/talks/rm/

Summary

Running many Django applications on one OpenBSD host can be made safer by giving each application its own Unix user, service configuration, and listening port, rather than running everything under one account. A reverse proxy can terminate TLS and route requests by hostname, while PF limits which users and services can connect to each other or the network. Separate users also provide practical ways to manage package installation, shared database access, and narrowly scoped collaboration between applications; the speaker argued this is a useful alternative to relying on Docker for security, though containers or virtual machines may still suit other needs.

Key takeaways

  • Give each Django application its own Unix user so a compromise is less likely to expose other applications or personal data.
  • Run each application on a distinct local port and configure services to start and be monitored through OpenBSD’s rc system.
  • Use relayd as a TLS-terminating reverse proxy that routes incoming requests to the right application based on the hostname.
  • Use PF to allow only necessary connections, blocking application users from reaching other services or the internet by default.
  • Install packages under the relevant application user, and use file permissions or narrowly defined doas rules when applications need limited access to shared resources.
  • The speaker said Docker is not automatically a security boundary; privilege separation and careful configuration matter, and virtual machines may be appropriate for stronger isolation.

Summarised automatically from the transcript.

Transcript

5,091 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:00

Speaker 1: Thank you.

0:03

Speaker 2: And we'll start by giving him a very big applause.

0:17

Speaker 1: Simplicity is a great virtue, but it requires hard work to achieve it and education to appreciate it. And to make matters worse, complexity sells better. I'm not paid to be here, so I get to talk about simple software, mostly OpenBSD. Maybe a lot of you work at companies and you use big complex software. Unfortunately, you're not using OpenBSD, but you can benefit from many of the ideas in the talk. Also, I can help you if you hire me, but you have to hire me because it sells better. But today there's a special promotion. You can ask me questions about other softwares related to what I'm saying. Um maybe the software using, and I'll do my best to answer. And uh yeah, let's go. We're talking about Django. You've probably run this.

1:03

Speaker 1: Raise your hand if you run this. Great. Yes. Okay. Um and then you you do that and then you can make a web request to here. But yes, um maybe you want to run something else in production. Anyone want to say why you should run this in production instead of the other one in production? Raise your hand. No one no no one wants to say? Yes. Yes, they say not to do it because yeah, like security

1:48

Speaker 1: and um they also use a lot of memory and security again. So you do this one. Um you could use a lot of other servers, but uh it actually does uh please raise your hand if you if you really have no idea about this because um because then I do need to explain a little bit about it. Okay. Um but you know about this one, right? Okay. It does the same thing, but this one's better. Um and safer and all that stuff. It does the same thing in the sense you get still get the address at uh you still get the website hosted like this. Okay. Um But now instead of using an example project, let's use Bornhack because that's a Django application, in fact, and I'll take some more examples from it.

2:33

Speaker 1: Anyone know what Bornhack is? Who wants to tell us what Bornhack is? Hacker event, yeah. You get a candy too. Oh, I'm not very good at that. Okay. Yeah. Uh yeah. Okay. And then it's at that address again. Now we're gonna run like a hundred com a hundred Django applications on the same host. We could run them all as my main user RM where I also do like email and web browsing. Who wants to say well that's a bad idea? Or maybe you're scared of these candies now. You don't want to get confused with the command. Yeah, uh getting confused with the command, yeah, that's that's a good one.

3:20

Speaker 1: Uh in general, if um That's actually my main concern, my main fear that I'm gonna mess something up. But you have a hundred command a hundred uh programs running on the same user, they all have access to different sensitive information, and maybe there's some problem with one of them and can mess up one of the others. So I want to run them as separate users. So I'm going to create a user for Bornhack with this command. And then I'm going to run the same command as before, but I'm going to run it as Bornhack user. So now when you exploit my Bornhack application, You only get access to whatever Bornhack app a user has. You don't get access to my email. You don't get access to like my other Django application. Um did that sort of make sense? Okay. So I run it as the Bornhack user, but it still is the same uh web address.

4:05

Speaker 1: So I still access it by the same address in my HTTP client. But but there's a problem with this. We're gonna run hundred Django applications in the same box. See a problem. Anyone want to tell me the problem? Yes, the port. We can't have all hundreds of them use d the same uh port. Um so oh yeah, do you want a candy? Oops, sorry. Um yeah, so we set the port like this in Unicorn. And if you have another server, it'll have some other option for it. And then we, it's almost the same um address. And now we have a hundred of these um unicorn instances perhaps.

4:52

Speaker 1: Let's start them all at boot and uh we can do that by putting the command in this file, uh this file, etcrc. dbornhack. And this is the same command, just broken into parts, so that the rc. subber framework from OpenVSD will know that when I run this command, rcc rc control enable bornhack, it'll know to start Bornhack unboot. And also it'll know that if I put a command like this in my cron tab, every hour it'll tell me if Bornhack crashed and if any of my other Django applications crashed. And I still access it by the same address as we've been talking about. So now another problem. I don't like this address. I think instead of being http cones slash slash numbers, it should look like this.

5:40

Speaker 1: HTTPS colon slash slash some nice name. And there are a lot of ways you could do this. Um the way I'm going to show you is with reverse proxy. And and I've been talking showing I'm going to show a lot of configuration files. In case those configuration files don't make sense, you can just ignore it and figure. Like this is the general structure we're going for. There's a web browser that's going to make a web request to the relay on the normal TLS port, and then the relay is going to make the web request to the server that we've been talking about the whole talk so far. And now I'm going to show you the configuration file. Well, scary configuration file. Let's just look at these lines. This line says we're going to listen to everything uh IP4, we could also do IP6

6:28

Speaker 1: on um TLS port. This one says if the host header matches Bornhack. example. com, we're gonna forward it to Bornhack, where Bornhack is the local host port 2003. That's how we do one Django application with the reverse proxy thing. To do several, we put them all in the same file. So now depending on the host header, we forward to a different place. So now we have what we just said. The remote web browser goes to the relay daemon and then it goes back to whatever Django application is. So it could be the bar Django application on port 2002 or the Bornhack on port 2003 or something else. I still didn't there's still one little thing for TLS. We need to have the hosts configured on our client.

7:14

Speaker 1: There's I actually uh put too much things in the abstract, so I'm gonna skip over how to do this really. This is the sloppy way to do it, or this is the I don't know. This is the way I actually do it, but this is the easy way to do it. Um for internal stuff this works Uh but anyway, if you do those commands, then you can access it by the name. And the the TLS verification will work properly. Uh did that sort of did did this uh part about relay make uh sense? Okay. And and you know, like you can stop me if something doesn't make sense. Uh or during the talk, even ask about other softwares you might want to do it with. Yeah, so we did a like it's say we have a bunch of different uh Django applications running unicorn or some other

7:59

Speaker 1: thing on the same host with different ports and then we have the relay to get everything from our main port to the I think we should have a firewall. Anyone want to say why we should have a firewall? Yeah? Security reasons. Yes. Any more specific? Or you just want to check off the box for the security Yes, I don't specify what uh all this like. I don't want all my computers my server to be open. Candy? Oh. Okay. So I'm uh so the way we're gonna do this is I want to allow this type of request. I want the remote web browser to go to the relay daemon and the relay daemon to go to the unicorn.

8:46

Speaker 1: But I don't want to allow the Bornhack user, so the Bornhack web application, to access some other Django application running on the same server. I also don't want um Is this just the reverse? Yeah, I I don't want the reverse either. Okay. And I also don't want the Bornhack user to be able to get to the relay daemon because then it can get to all the other applications that are on the server. I also don't want the Bornhack user to see the DNS server, um, because it doesn't need the DNS server. Um and you can also exfiltrate data that way, but It principle is just you don't need the DNS server. I also don't want the Bornhack user to unfortunately see Django Day website because it doesn't need to. It doesn't need to go to the internet.

9:32

Speaker 1: So the the concept is I want the born hack user to have no internet access, and I want nothing to be able to access the born hack user except for the few places where I need it. And one place where I need it is um well, we'll get to that. Uh so the only thing I want to allow is this one. And we're going to do it with the one firewall that I know how to use, PF. And it works very well because it's simple and runs on OpenBSD. This is uh I'm going to go through the parts of the configuration file. Normally a PF configuration file, you have a bunch of lines, and the last matching line is the one that matters. The first line says we allow everything. Second one, uh the first one was a comment. The second one says we elaborate. Then we say

10:18

Speaker 1: um block incoming TCP and UDP connections to localhosts from non-root users. So this means that um if I'm running the Jang if I'm running Unicorn on port 2003, that will be blocked because it's an incoming TCP connection. Then it also blocked the outgoing TCP connections from most users. So this will include the Django user. That means the WarnHack application can't make a web request to Django Day website. We're only blocking TCP and UDP. There are actually other protocols, and you can block them, but it's more annoying. So but I'm actually comfortable with this.

11:03

Speaker 1: Um yeah. So we blocked everything now. Now we can't run our Django application. So now we need to give ac open the part that we do want. So we're gonna say We're allowing incoming connections to port 2002 for the bar application for the bar user, which is running some Django application. 2003 for Bornhack user, and so on. If we were running some other uh relay, we might need to do the same for the relay, but the relay daemon runs as root, so that's covered by the first rule that I set. So now did I have a diagram for this? Yeah. So now note we born hack user can't actually make a request to itself because born

11:49

Speaker 1: hack user is only allowed to incoming TC. TCP, not outgoing TCP. Does that make sense? Yeah. So summary of that configuration file, you if you uh Too much configuration file is we allow some web browser to access our relay daemon running on port 443 with TLS. And send that to the unicorn running on some other port. I forgot to mention. The relay daemon also handles the TLS, so the Django application doesn't need to know anything about certificates. It's it's like you run the as if you ran the management um the run the development server, that doesn't have anything about um TLS. Yeah, and then we block all the other stuff.

12:37

Speaker 1: Yeah, that was a lot of configuration files, so let's talk about something easier, politics. I like freedom, so I like GNU. I uh I like free software and GNU Protect GNU licenses protect the end users by being viral by saying that I'm going to give some free software to you and you can do what you want with it, but if you give it to someone else, you cannot enslave that other person. You have to pass those rights on to the other person. So it's strange that I would use OpenBSD, which um has a different politics and has a permissive license where they give away the software and you're allowed to enslave your users using OpenBSD. BSDs. Because OpenBSD uh the the view of OpenBSD is more like

13:22

Speaker 1: people we want to improve the world. People are, for whatever reason, buying stuff from commercial software companies that are often going to implement bad solutions to already solve problems. So the best we can do Is to give our stuff away from free and hope that the people will take it and integrate it and then the and users will get well-functioning products. And so yeah, but if I'm a user of OpenBSD, it's okay. Uh so that's my attitude. I like freedom. So OpenBSD is the freedom. I contributed very tiny bit to BSD operating systems. It's not enough that the license bothers me, but if I contributed more, maybe I would insist on a copy-left license. The other thing is because OpenBSD doesn't have the same specific goal, they're not certified by Free Software Foundation as a free operating system.

14:12

Speaker 1: But the differences are kind of nitpicky. And I'm going to show you them here. It's not really such something to be worried about if you are competent. So the two things that I think are non-free in OpenBSD. One is that It's possible to build a package from source. It's called a port. Build a port from source. So like you could build a Python from source. Normally you would install the binary, but it's possible to build it from source. And there are some things you can build from source that are proprietary, and so they can't give you the binary. And so if you're doing this, you need to look at the license field to make sure it's free software and you're not being enslaved. That's the one thing. The other thing is Free Software Foundation says drivers If you can update the driver to some chip, some like not some other chip on the CPU, uh on the on the computer, the

15:05

Speaker 1: That needs to be free software. But if you can't update it, it's just stored on the device, like on the PCI card or whatever, and you can't update it, then it doesn't matter. OpenBSD's attitude is either way, it's fine. It doesn't make a difference whether you can update it or not. So OpenBSD allows you to install proprietary firmware on other chips. in your computer. Okay, back to configuration files. Uh yes, I shortened the part on the database. Um okay. We have now like a hundred Django applications running on the same computer. Thank you. Um

15:51

Speaker 1: So one like I like to run them all in the same database. It makes uh like if they're small enough, it makes uh backups easier and stuff. But some some things to consider if we have them all as 100 separate users running 100 separate Django applications. Let's say one category of database is file-based connections. That would be like SQLite or Postgres using a Unix socket, which is a file. For these, if you have each user, um, each Django application separate user, you need to set the permissions of that. Well, you can use a separate SQLite database per user, but if you use Postgres, you have to set the permissions appropriately. 777 is often okay. Because it's a Unix socket. It it's just the communication socket, yeah. Um

16:37

Speaker 1: Then uh the other option, which is maybe more normal, is if you want to have your database go over network connection. And in that case, like this is probably if you don't know what I'm talking about, this is probably what you've done. Um in that case, you need to open the the port in the firewall. The same way I did before, but for the different different port. Okay. Uh yeah, database. I could talk a lot more about database, but I want to get to some like kind of new things that come about when you have everything on one big Unix system. So one thing is installing packages. A lot of people like to use virtual environments. Well I have all my Django applications running as separate users.

17:23

Speaker 1: Uh so I don't need to make a virtual environment. I can use normal pip. In a way it's a virtual environment, but it it's it's using the the local views if you do this, Bornhack is the only user that gets these packages. Because it's installed on the Bornehack user. Except this doesn't work because we set the firewall so you can't access PyPI. So one option is to open the open the firewall for PyPI. There's actually a little smarter way that to do this. Like I actually do it so that I would actually do it so that I could turn it on just when I'm doing the installation and turn it off right after. idea is open port for Pypei, open the firewall for Pypei. But what I actually do is this, I run pipe, I run pip as a separate user.

18:08

Speaker 1: I make a virtual environment as a separate user, and then I copy the whole thing to the Bornhack user. That's what these commands do. And um but they're uh I think they're both pretty good options. Yeah. Um and now we get to the things that are like I I wanted to put in some things that are like kind of maybe a a little uh easy like unusually easy if you do things this way. Yeah. Let's say we have like we could have the Bornhack application being just our only user, but we could have two users collaborate. Let's say we have some big website where you upload lots of different files. Like maybe the one we just talked about actually would be a a good example.

18:55

Speaker 1: Maybe it's a bit too big for one bucks. Um So let's say we have a big website user running a Django application and the media route is this. And big website has like sensitive information in it, and it has all these media files, and it has and it's network connection. Also, we have some search engine. I like to use recall. Recall, um, the way recall works is it in it takes a list of all the files and then it runs a filter on each file. And the filters is you using lots of different programs to support lots of different file types. So recall is also a very complex application. And I want to separate them because if there's a problem with one, I don't want it to mess up the other. So one way I can do this is I

19:40

Speaker 1: um I make the the search engine a member of the big website group, and then I set up the file permissions so that the search engine can get read access to those files, and then the search engine can index all those files, but it doesn't have access to anything else about the database. And then I could actually run a separate website from the search engine user just to search the files. And now they're they're quite separate, but it it uh it operates very smoothly. Did that make any sense? Okay, good. And then another way you can do it is with Duas. Duas is from OpenBSD, but you might be more familiar with sudo, which is a much more complex way of uh doing the same thing. But sudo came first. So let's imagine we're at Bornhack, which is a camp, like on a field, and we have the speaker tent where we give talks, and we have a bar

20:31

Speaker 1: where we uh buy alcohol and pay for the Pay for the camp so that we can have more talks. And let's imagine we're going to tell people where they should go by flying an internet connected flamethrower drone around the camp. And it's going to go to the to the bar when they should go to the bar and drink, and it's going to go to the speaker tent when they should go to the speaker tent and watch talks. This flamethrower drone is very complicated software, so we don't want the the Django application to have a risk of messing with that. And also, um we don't want the flamethrower drone to like mess up our Django, our user, anything about our Bornhack website. Which is quite complex. So we're going to give a very small interface between them. This is the do as configuration file.

21:17

Speaker 1: We're going to allow the born hack user To execute a command as the flamethrower user. And the command only is two options. You can run the flame program with the argument move to speaker tent, and you can run the flame program with the argument move to bar. And we're not even going to give it access to turn on the flamethrower. Let's say for safety reasons we want a person to to turn on the flamethrower. But we're okay with we're okay with the website to direct where the drone should go. So this is the do as configuration, and then we can write this Python in the Bornhack Django application that says as the user flame, run the program flame with the argument move to speaker tent. And it'll um if the command fails, it'll raise an exception. And so this way, Bornhack user can uh

22:04

Speaker 1: can have some event that directs the flamethrower drone to move around, but The only interface is move here or move there. So if there's some problem with either one, it doesn't affect the other So summarizing the stuff we talked about, uh normal um Python stuff and then set up with services, and then we had the reverse proxy, so you can have a bunch of things with TLS. Then uh firewall database some cool things you can do with multiple users. Oh cool, I have a lot of time. And uh yeah, and I thought I wasn't gonna have time for these, but uh it turns out I did. Sorry. Anyway. But that that's it.

22:49

Speaker 1: Thank you.

22:57

Speaker 2: Yeah, we we got ready so quickly that you actually started the talk a little bit before time, so what they're doing there. But I think we're also like mostly Linux users here. So it's super nice to hear how it's done in the BST world. There is a question there.

23:29

Speaker 3: So does this somehow compensate the lack of uh Docker on OpenBSD?

23:35

Speaker 1: Does this uh

23:37

Speaker 3: I mean it doesn't have a Docker, but uh what are the benefit pros and cons?

23:42

Speaker 1: Uh Well, does it compensate for Docker? I think a better way to start to start the discussion is why do we use Docker? Um Technically it kind of works similar to this because it's I think it's kind of like a a jail. Um a lot of people uh think that Docker gives them security. And it doesn't. Um so yeah, but what it uh I think I think one of the main reasons for Docker is that you want to install some configuration that just It just happens that like the packages are written to work as root. So you use like apt get install and it goes as root. And you just don't know how to install it as normal user.

24:27

Speaker 1: But you can compile all these to work as a normal user. So um so one of the reasons for uh Docker is like, I mean, this is like my um controversial view on it, that it's just because people don't know how to install things not as root. Okay, so that one this doesn't help you with. But everything else I think it does. Um so another is so that you can have a like a reproducible environment. Well um Yeah, you can have some recipe to install this. It'll be a little different. What else does Docker help you with? Another thing about Docker is that you have like all these Docker files that you can reuse someone else's Docker file, but this is so easy. So I'm I don't know. Is is there other things you you like about Docker that if you say that then I can tell you

25:12

Speaker 3: No, it also does some networking uh With these ports and so on.

25:17

Speaker 1: Okay. Docker, um one thing you can one setting for Docker is uh yeah, so it it does help with that. One setting for Docker is just to use the normal host. Um networking. Another is to say only expose these ports. And yeah, um that's a this these are alternatives, yeah, for that.

25:45

Speaker 2: Next question? Yes.

25:49

Speaker 1: Oh, and um candy for people who ask questions.

25:56

Speaker 4: Thank you. Um congratulations for the presentation. It was uh super interesting to me personally. Always the BSD world was somewhat exotic and I always always get uh you know mesmerized by it. Uh I have a question because um I'm working a lot with Docker and I always know that It it's what you said, you believe that it's secure, but there's a lot of caveats. One of them is SUID in um in Linux. How a process can use SUID to get root privileges. Is this something that we can get guarded from in the BSD world? And I'm not sure.

26:38

Speaker 1: No, but it's like you know what you're doing, it's the same approach that the way you protect in Docker in this. Um Maybe I should just explain the the risk. Uh you run the set UID binary. The set UID binary is owned by, let's say, root, and it can do special things. So for example, ping is a set UID. binary because I mean at least on OpenBSD, I don't know how it works on Linux. Probably it's also set works the same way. That the root user is the only one that can make that type of network request. Request. But it another user can execute this program and it'll run as if the root user executed it. So like an easy attack with uh Docker

27:23

Speaker 1: is Where do I want to go with this?

27:35

Speaker 4: So the

27:35

Speaker 1: Yeah. So you you that it's the same. To get around that, just do it properly or use a virtual machine, I think, or a separate computer. Yeah.

27:44

Speaker 4: Thank you. Um

27:46

Speaker 1: this reminded me another thing about Docker. Uh you can just use an old fashioned chirot in um OpenBSD. Uh and also FreeBSD has jails which are Similar, it's like a stronger version similar like like truths. And that'll have the same problem, but it'll have the benefit that you can install things as root inside of the truth. Yeah. And you get a candy, if I can reach. Yeah.

28:21

Speaker 2: You're getting uh better at the candy floor, I have to say.

28:25

Speaker 1: Yeah.

28:26

Speaker 2: Any more questions? Yep. We do have time.

28:33

Speaker 5: Um you didn't miss mention uh certificates uh during the build up stack uh

28:41

Speaker 1: Yeah, I just didn't think I'd have so much time. Um but uh is there a particular question you have about certificates? Uh okay. Um I don't know if it 's I can you just want me to say some of those certificates or

28:59

Speaker 5: I want to say you know whether it was not needed or I mean I I I think uh what's it called today that you use it's it's very easy to fairly easy to set up at least on on the Linux with the cert. Yeah, but

29:13

Speaker 2: let's encrypt

29:14

Speaker 1: I find that too too complicated. I just use OpenSSL or Libra SSL. OpenBSD made Libra SSL. Yeah. But yeah, so okay, so actually if it's my internal stuff, I have a wildcard certificate. That's that's the one that you saw in the Configuration file. Um , yeah. But Yeah, so uh the way this configuration file is set up is it's a wildcard certificate. But you can doesn't have to be a wild certificate card certificate. it and this is the the way you do it. And if you want to know more information, look at the manual page for relayd. conf. Yes?

29:59

Speaker 1: And uh are we gonna have flow

30:02

Speaker 6: uh flamethrower uh drones on born hack next yeah?

30:06

Speaker 1: I don't I don't know. You can make one Yeah. We have flamethrowers and we have drones. Just don't know the flamethrower drones. So uh did you already ask a question or did you already get a candy? Okay. I guess someone around here got a candy already. So you and we had another question? Hmm no.

30:37

Speaker 2: It's a you do get candy still. There is still more left.

30:42

Speaker 1: Yeah, yeah, there's uh there's there's a bunch left. Yeah. And you know I these are from uh there's someone here who's from the same place as the candies. Yeah, okay. Did you get one already? No. Why do you have Serbian candies?

31:11

Speaker 2: So I have one final question. question then can I also get one?

31:16

Speaker 1: Well you know you have to ask you have to ask a better question than that. You have to ask a better question than that. But you can get one because you're an organizer.

31:30

Speaker 2: Okay um so uh first of all a big applause for RM really super nice talk um We're gonna do a a short break, um so called water break

Questions this talk answers

How can I run multiple Django apps on one server without giving them access to each other’s files?

Create a separate Unix user for each application and run its service as that user. If one app is compromised, it is limited to that user’s permissions rather than gaining access to the other apps or your personal files.

Discussed at 3:20

How do I serve several Django apps on one host under different HTTPS domain names?

Run each app on its own local port, then use a reverse proxy to route requests according to the host name. The proxy can handle TLS and forward each request to the corresponding app.

Discussed at 5:40

How can I use a firewall to stop Django apps from connecting to each other?

Block most incoming and outgoing TCP/UDP connections, then allow only the specific app ports and users that need access. For example, permit the reverse proxy to reach each app, while preventing an app user from reaching other apps or the internet.

Discussed at 9:18

Do I need a Python virtual environment for each Django app if each has its own Unix user?

Not necessarily: packages installed with pip as an app’s own user are specific to that user. Since the firewall may block PyPI, the speaker suggests either briefly allowing access for installation or building a virtual environment as a separate user and copying it over.

Discussed at 17:23

How can a Django app trigger a command in another service without giving it broad access?

Use a restricted `doas` rule that permits only specific commands and arguments as the other service’s user. The example lets the app direct a drone to one of two locations, without permission to activate its flamethrower.

Discussed at 20:31

Note: 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.

More videos from Django Day Copenhagen