Latest / Linux Server Admin with Fexingo: Sysadmin, Bash, and Server Engineering / How to Harden Your Linux Server with SELinux Policies
Transcript
- Lucas: So you've got your Linux server locked down: firewalld is running, SSH is key-only, root login is disabled. But there's still a gap at the kernel level — any process running as a user can call system calls that have nothing to do with its job. Luna: That's where mandatory access control — SELinux or AppArmor — comes in. But a lot of people still treat it like a cryptic black box. Lucas: Right. And honestly, the reputation is partly deserved. SELinux has a steep learning curve, especially the first time you see an 'avc denial' and have no clue what triggered it. But I think the payoff is huge. Luna: Can you give me a concrete example of a setup that actually got value from it? Lucas: Sure. I talked to an infrastructure team at a mid-size fintech company — about 200 Linux servers running mostly Nginx, PostgreSQL, and a Java app. They had standard user-level permissions, but no MAC. After a penetration test revealed they could escalate privileges via a misconfigured cron job, they decided to go all-in on SELinux. Luna: What was the impact? Lucas: They ran an audit before and after. Before SELinux, their kernel attack surface — measured in terms of syscalls accessible to non-root processes — was something like 320 distinct calls. After enabling targeted SELinux policies and customizing them for their stack, that number dropped to under 60. That's over 80 percent reduction in unnecessary syscall exposure. Luna: That's a massive reduction. But getting there must have been painful — all those denials in the audit log. Lucas: Yeah, the first week was rough. They started in permissive mode, collected the denials, used audit2allow to build custom modules. The trick is to run it in permissive for a full business cycle — at least a week — so you capture all the legitimate traffic. Then you review each denial and decide: should this be allowed, or is it actually a probe? Luna: And if you just blindly pipe everything into audit2allow, you're basically back to no security. Lucas: Exactly. That's the trap. The tool automates the policy generation, but you still need to understand what each denial means. For example, one common denial they saw was for Nginx trying to access a file outside its default context — like custom log directory. That's legitimate. But another denial was for a random PHP script trying to execute something in /tmp — that got flagged and investigated. Luna: So you're not just blocking — you're also gaining visibility into unexpected behavior. Lucas: Right. SELinux acts as an intrusion detection system for system calls. And once you get past the initial learning curve, it becomes a normal part of your provisioning pipeline. They now bake SELinux policy modules into their Ansible roles. Luna: That's smart. Makes it repeatable. But let's talk about the elephant in the room: a lot of people run into issues with SELinux conflicting with container runtimes, like Docker or Podman. Lucas: Great point. Containers add complexity because they share the host kernel. With SELinux, you need to set the right context for container processes. Most container engines now have SELinux support built in — you just need to enable it. For example, with Podman, you set the 'selinux' label in the container spec, and it maps to a confined domain. Luna: But some teams just disable SELinux when they hit these issues. That's a bad habit. Lucas: It's the easiest path, but it's not a solution. Instead, you should learn how to use SELinux booleans — these are toggle switches that adjust policy without writing new modules. For example, there's a boolean called 'httpd_can_network_connect' that lets Apache or Nginx open outbound connections. Turn that on instead of disabling SELinux. Luna: And there's also 'container_use_devices' for containers needing access to host devices. Lucas: Exactly. The key is to treat SELinux as a skill to learn, not a nuisance to bypass. I'd argue it's one of the most undervalued security layers in Linux. Luna: If today's conversation gave you something usable — maybe you're thinking about trying SELinux on a test box, or you finally understand what 'avc denial' means — that's exactly why we do this show. Lucas: And the way we keep it ad-free and focused on that kind of practical value is listener support. If you'd like to help, you can find us at buy me a coffee dot com slash fexingo. Luna: It's a small way to keep the server room conversations going. Now, let's get back to SELinux — because there's another aspect I want to dig into: how SELinux compares to AppArmor. Lucas: Right. They're both mandatory access control systems, but they work differently. AppArmor uses path-based profiles — you define which file paths a program can access. SELinux uses label-based security contexts — every file, process, and port gets a label, and policy rules govern interactions between labels. Luna: So AppArmor is generally easier to set up for simple use cases, but SELinux offers more granular control. Lucas: That's a fair summary. If you're running Ubuntu or Debian, AppArmor is the default and integrates nicely. On Red Hat and Fedora, SELinux is the default. Both are effective, but SELinux's object labeling can handle more complex scenarios, like multi-level security or fine-grained network controls. Luna: And the choice often comes down to your distribution and team expertise. If everyone knows SELinux, stick with it. If you're on Ubuntu and have less experience, starting with AppArmor might be smoother. Lucas: Absolutely. But either way, the principle is the same: confine processes to only what they need. And that's the whole philosophy behind least privilege. Luna: So let's say a listener wants to start with SELinux tomorrow. What's the first concrete step? Lucas: First, check that SELinux is installed and enabled. Run 'getenforce' — if it says 'Disabled', you'll need to enable it in the kernel boot parameters. Then set it to permissive mode temporarily: 'setenforce 0'. Then monitor your audit log with 'ausearch -m avc' or 'journalctl -t audit'. Let it run for a day or two in a test environment. Luna: And don't look at the log all at once — it can be overwhelming. Use 'grep' to filter by service name. Lucas: Good tip. Then once you have a list of denials, use 'audit2allow -a' to generate a policy module, but review each denial before you compile it. You can edit the.te file to allow only what's needed. Luna: One thing I've found helpful is to create a local policy directory and version control it. Lucas: Exactly. Treat your SELinux policies like code. Store them in Git, document why each rule exists. Then when you deploy a new server, you just run a playbook that loads your custom modules and sets booleans. Luna: That turns SELinux from a headache into a repeatable security layer. Lucas: And that's the goal. It's not about being perfect from day one. It's about incremental improvement. Start with one service — say, your web server — and confine it. Then move to your database, and so on. Luna: Plus, once you get comfortable, you'll find it's actually satisfying to see a clean audit log with no denials. Lucas: Hah, true. There's a certain zen to a system that only allows exactly what it's supposed to. And for the listener who's been putting this off — today's a good day to start. Luna: Agreed. And if you hit a weird denial, remember that the SELinux man pages and the Fedora SELinux wiki are excellent resources. Lucas: Or the 'sepolicy' command — you can generate man pages for specific domains. For example, 'sepolicy manpage -d httpd_t' gives you a tailored manual for the web server's SELinux domain. Luna: That's a gem. I didn't know that. Lucas: It's one of those hidden utilities that makes life much easier. So to wrap up: SELinux isn't going away. It's built into the kernel, it's mature, and it's one of the most effective ways to harden a server. The learning curve is real, but the payoff is tangible. Luna: And with tools like audit2allow and booleans, you don't have to become an SELinux expert overnight. Lucas: Exactly. Start small, iterate, and you'll be glad you did.