Add landlock rulesets

This commit is contained in:
2025-12-01 11:15:29 +09:00
parent 2472936857
commit 152c3ddbcc
7 changed files with 136 additions and 4 deletions

View File

@@ -14,6 +14,7 @@ use crate::{
},
server::{
config::{MysqlConfig, ServerConfig},
landlock::landlock_restrict_server,
session_handler,
},
};
@@ -223,6 +224,9 @@ fn invoke_server_with_config(config_path: PathBuf) -> anyhow::Result<StdUnixStre
nix::unistd::ForkResult::Child => {
tracing::debug!("Running server in child process");
landlock_restrict_server(Some(config_path.as_path()))
.context("Failed to apply Landlock restrictions to the server process")?;
match run_forked_server(config_path, server_socket, unix_user) {
Err(e) => Err(e),
Ok(_) => unreachable!(),

View File

@@ -19,7 +19,7 @@ use crate::{
common::executable_is_suid_or_sgid,
protocol::{Response, create_client_to_server_message_stream},
},
server::command::ServerArgs,
server::{command::ServerArgs, landlock::landlock_restrict_server},
};
#[cfg(feature = "mysql-admutils-compatibility")]
@@ -146,6 +146,10 @@ fn handle_server_command(args: &Args) -> anyhow::Result<Option<()>> {
!executable_is_suid_or_sgid()?,
"The executable should not be SUID or SGID when running the server manually"
);
landlock_restrict_server(args.config.as_deref())
.context("Failed to apply Landlock restrictions to the server process")?;
tokio_start_server(
args.config.to_owned(),
args.verbose.to_owned(),

View File

@@ -3,7 +3,7 @@ pub mod command;
mod common;
pub mod config;
pub mod input_sanitization;
// pub mod server_loop;
pub mod landlock;
pub mod session_handler;
pub mod sql;
pub mod supervisor;

89
src/server/landlock.rs Normal file
View File

@@ -0,0 +1,89 @@
#[cfg(target_os = "linux")]
use std::path::Path;
#[cfg(target_os = "linux")]
pub fn landlock_restrict_server(config_path: Option<&Path>) -> anyhow::Result<()> {
use crate::{core::common::DEFAULT_CONFIG_PATH, server::config::ServerConfig};
use anyhow::Context;
use landlock::{
ABI, Access, AccessFs, AccessNet, NetPort, Ruleset, RulesetAttr, RulesetCreatedAttr,
path_beneath_rules,
};
let config_path = config_path.unwrap_or(Path::new(DEFAULT_CONFIG_PATH));
let config = ServerConfig::read_config_from_path(config_path)?;
let abi = ABI::V4;
let mut ruleset = Ruleset::default()
.handle_access(AccessFs::from_all(abi))?
.handle_access(AccessNet::from_all(abi))?
.create()
.context("Failed to create Landlock ruleset")?
.add_rules(path_beneath_rules(
&["/run/muscl"],
AccessFs::from_read(abi),
))
.context("Failed to add Landlock rules for /run/muscl")?
// Needs read access to /etc to access unix user/group info
.add_rules(path_beneath_rules(&["/etc"], AccessFs::from_read(abi)))
.context("Failed to add Landlock rules for /etc")?
.add_rules(path_beneath_rules(&[config_path], AccessFs::from_read(abi)))
.context(format!(
"Failed to add Landlock rules for server config path at {}",
config_path.display()
))?;
if let Some(socket_path) = &config.socket_path {
ruleset = ruleset
.add_rules(path_beneath_rules(&[socket_path], AccessFs::from_all(abi)))
.context(format!(
"Failed to add Landlock rules for server socket path at {}",
socket_path.display()
))?;
}
if let Some(mysql_socket_path) = &config.mysql.socket_path {
ruleset = ruleset
.add_rules(path_beneath_rules(
&[mysql_socket_path],
AccessFs::from_all(abi),
))
.context(format!(
"Failed to add Landlock rules for MySQL socket path at {}",
mysql_socket_path.display()
))?;
}
if let Some(mysql_host) = &config.mysql.host {
ruleset = ruleset
.add_rule(NetPort::new(config.mysql.port, AccessNet::ConnectTcp))
.context(format!(
"Failed to add Landlock rules for MySQL host at {}:{}",
mysql_host, config.mysql.port
))?;
}
if let Some(mysql_passwd_file) = &config.mysql.password_file {
ruleset = ruleset
.add_rules(path_beneath_rules(
&[mysql_passwd_file],
AccessFs::from_read(abi),
))
.context(format!(
"Failed to add Landlock rules for MySQL password file at {}",
mysql_passwd_file.display()
))?;
}
ruleset
.restrict_self()
.context("Failed to apply Landlock restrictions to the server process")?;
Ok(())
}
#[cfg(not(target_os = "linux"))]
pub fn landlock_restrict_server() -> anyhow::Result<()> {
Ok(())
}