Add landlock rulesets
This commit is contained in:
34
Cargo.lock
generated
34
Cargo.lock
generated
@@ -507,6 +507,26 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enumflags2"
|
||||||
|
version = "0.7.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
|
||||||
|
dependencies = [
|
||||||
|
"enumflags2_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enumflags2_derive"
|
||||||
|
version = "0.7.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@@ -934,6 +954,17 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "landlock"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49fefd6652c57d68aaa32544a4c0e642929725bdc1fd929367cdeb673ab81088"
|
||||||
|
dependencies = [
|
||||||
|
"enumflags2",
|
||||||
|
"libc",
|
||||||
|
"thiserror 2.0.17",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -1054,6 +1085,7 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
"indoc",
|
"indoc",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
"landlock",
|
||||||
"nix",
|
"nix",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"prettytable",
|
"prettytable",
|
||||||
@@ -1093,7 +1125,7 @@ version = "0.50.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ tracing-journald = "0.3.2"
|
|||||||
tracing-subscriber = "0.3.22"
|
tracing-subscriber = "0.3.22"
|
||||||
uuid = { version = "1.18.1", features = ["v4"] }
|
uuid = { version = "1.18.1", features = ["v4"] }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
landlock = "0.4.4"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["mysql-admutils-compatibility"]
|
default = ["mysql-admutils-compatibility"]
|
||||||
mysql-admutils-compatibility = []
|
mysql-admutils-compatibility = []
|
||||||
|
|||||||
@@ -51,6 +51,6 @@ RestrictRealtime=true
|
|||||||
RestrictSUIDSGID=true
|
RestrictSUIDSGID=true
|
||||||
SocketBindDeny=any
|
SocketBindDeny=any
|
||||||
SystemCallArchitectures=native
|
SystemCallArchitectures=native
|
||||||
SystemCallFilter=@system-service
|
SystemCallFilter=@system-service @sandbox
|
||||||
SystemCallFilter=~@privileged @resources
|
SystemCallFilter=~@privileged @resources
|
||||||
UMask=0777
|
UMask=0777
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
server::{
|
server::{
|
||||||
config::{MysqlConfig, ServerConfig},
|
config::{MysqlConfig, ServerConfig},
|
||||||
|
landlock::landlock_restrict_server,
|
||||||
session_handler,
|
session_handler,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -223,6 +224,9 @@ fn invoke_server_with_config(config_path: PathBuf) -> anyhow::Result<StdUnixStre
|
|||||||
nix::unistd::ForkResult::Child => {
|
nix::unistd::ForkResult::Child => {
|
||||||
tracing::debug!("Running server in child process");
|
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) {
|
match run_forked_server(config_path, server_socket, unix_user) {
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
Ok(_) => unreachable!(),
|
Ok(_) => unreachable!(),
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use crate::{
|
|||||||
common::executable_is_suid_or_sgid,
|
common::executable_is_suid_or_sgid,
|
||||||
protocol::{Response, create_client_to_server_message_stream},
|
protocol::{Response, create_client_to_server_message_stream},
|
||||||
},
|
},
|
||||||
server::command::ServerArgs,
|
server::{command::ServerArgs, landlock::landlock_restrict_server},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "mysql-admutils-compatibility")]
|
#[cfg(feature = "mysql-admutils-compatibility")]
|
||||||
@@ -146,6 +146,10 @@ fn handle_server_command(args: &Args) -> anyhow::Result<Option<()>> {
|
|||||||
!executable_is_suid_or_sgid()?,
|
!executable_is_suid_or_sgid()?,
|
||||||
"The executable should not be SUID or SGID when running the server manually"
|
"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(
|
tokio_start_server(
|
||||||
args.config.to_owned(),
|
args.config.to_owned(),
|
||||||
args.verbose.to_owned(),
|
args.verbose.to_owned(),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ pub mod command;
|
|||||||
mod common;
|
mod common;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod input_sanitization;
|
pub mod input_sanitization;
|
||||||
// pub mod server_loop;
|
pub mod landlock;
|
||||||
pub mod session_handler;
|
pub mod session_handler;
|
||||||
pub mod sql;
|
pub mod sql;
|
||||||
pub mod supervisor;
|
pub mod supervisor;
|
||||||
|
|||||||
89
src/server/landlock.rs
Normal file
89
src/server/landlock.rs
Normal 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(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user