mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-18 21:11:01 +00:00
PVF: Add Secure Validator Mode (#2486)
Co-authored-by: Javier Viola <javier@parity.io>
This commit is contained in:
@@ -18,18 +18,19 @@ use crate::{Config, SecurityStatus, LOG_TARGET};
|
||||
use futures::join;
|
||||
use std::{fmt, path::Path};
|
||||
|
||||
const SECURE_MODE_ANNOUNCEMENT: &'static str =
|
||||
"In the next release this will be a hard error by default.
|
||||
\nMore information: https://wiki.polkadot.network/docs/maintain-guides-secure-validator#secure-validator-mode";
|
||||
|
||||
/// Run checks for supported security features.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns the set of security features that we were able to enable. If an error occurs while
|
||||
/// enabling a security feature we set the corresponding status to `false`.
|
||||
pub async fn check_security_status(config: &Config) -> SecurityStatus {
|
||||
let Config { prepare_worker_program_path, cache_path, .. } = config;
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error only if we could not fully enforce the security level required by the current
|
||||
/// configuration.
|
||||
pub async fn check_security_status(config: &Config) -> Result<SecurityStatus, String> {
|
||||
let Config { prepare_worker_program_path, secure_validator_mode, cache_path, .. } = config;
|
||||
|
||||
let (landlock, seccomp, change_root) = join!(
|
||||
check_landlock(prepare_worker_program_path),
|
||||
@@ -37,26 +38,81 @@ pub async fn check_security_status(config: &Config) -> SecurityStatus {
|
||||
check_can_unshare_user_namespace_and_change_root(prepare_worker_program_path, cache_path)
|
||||
);
|
||||
|
||||
let security_status = SecurityStatus {
|
||||
can_enable_landlock: landlock.is_ok(),
|
||||
can_enable_seccomp: seccomp.is_ok(),
|
||||
can_unshare_user_namespace_and_change_root: change_root.is_ok(),
|
||||
};
|
||||
let full_security_status =
|
||||
FullSecurityStatus::new(*secure_validator_mode, landlock, seccomp, change_root);
|
||||
let security_status = full_security_status.as_partial();
|
||||
|
||||
let errs: Vec<SecureModeError> = [landlock, seccomp, change_root]
|
||||
.into_iter()
|
||||
.filter_map(|result| result.err())
|
||||
.collect();
|
||||
let err_occurred = print_secure_mode_message(errs);
|
||||
if err_occurred {
|
||||
gum::error!(
|
||||
if full_security_status.err_occurred() {
|
||||
print_secure_mode_error_or_warning(&full_security_status);
|
||||
if !full_security_status.all_errs_allowed() {
|
||||
return Err("could not enable Secure Validator Mode; check logs".into())
|
||||
}
|
||||
}
|
||||
|
||||
if security_status.secure_validator_mode {
|
||||
gum::info!(
|
||||
target: LOG_TARGET,
|
||||
"{}",
|
||||
SECURE_MODE_ANNOUNCEMENT,
|
||||
"👮♀️ Running in Secure Validator Mode. \
|
||||
It is highly recommended that you operate according to our security guidelines. \
|
||||
\nMore information: https://wiki.polkadot.network/docs/maintain-guides-secure-validator#secure-validator-mode"
|
||||
);
|
||||
}
|
||||
|
||||
security_status
|
||||
Ok(security_status)
|
||||
}
|
||||
|
||||
/// Contains the full security status including error states.
|
||||
struct FullSecurityStatus {
|
||||
partial: SecurityStatus,
|
||||
errs: Vec<SecureModeError>,
|
||||
}
|
||||
|
||||
impl FullSecurityStatus {
|
||||
fn new(
|
||||
secure_validator_mode: bool,
|
||||
landlock: SecureModeResult,
|
||||
seccomp: SecureModeResult,
|
||||
change_root: SecureModeResult,
|
||||
) -> Self {
|
||||
Self {
|
||||
partial: SecurityStatus {
|
||||
secure_validator_mode,
|
||||
can_enable_landlock: landlock.is_ok(),
|
||||
can_enable_seccomp: seccomp.is_ok(),
|
||||
can_unshare_user_namespace_and_change_root: change_root.is_ok(),
|
||||
},
|
||||
errs: [landlock, seccomp, change_root]
|
||||
.into_iter()
|
||||
.filter_map(|result| result.err())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_partial(&self) -> SecurityStatus {
|
||||
self.partial.clone()
|
||||
}
|
||||
|
||||
fn err_occurred(&self) -> bool {
|
||||
!self.errs.is_empty()
|
||||
}
|
||||
|
||||
fn all_errs_allowed(&self) -> bool {
|
||||
!self.partial.secure_validator_mode ||
|
||||
self.errs.iter().all(|err| err.is_allowed_in_secure_mode(&self.partial))
|
||||
}
|
||||
|
||||
fn errs_string(&self) -> String {
|
||||
self.errs
|
||||
.iter()
|
||||
.map(|err| {
|
||||
format!(
|
||||
"\n - {}{}",
|
||||
if err.is_allowed_in_secure_mode(&self.partial) { "Optional: " } else { "" },
|
||||
err
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
type SecureModeResult = std::result::Result<(), SecureModeError>;
|
||||
@@ -71,12 +127,17 @@ enum SecureModeError {
|
||||
|
||||
impl SecureModeError {
|
||||
/// Whether this error is allowed with Secure Validator Mode enabled.
|
||||
fn is_allowed_in_secure_mode(&self) -> bool {
|
||||
fn is_allowed_in_secure_mode(&self, security_status: &SecurityStatus) -> bool {
|
||||
use SecureModeError::*;
|
||||
match self {
|
||||
CannotEnableLandlock(_) => true,
|
||||
// Landlock is present on relatively recent Linuxes. This is optional if the unshare
|
||||
// capability is present, providing FS sandboxing a different way.
|
||||
CannotEnableLandlock(_) => security_status.can_unshare_user_namespace_and_change_root,
|
||||
// seccomp should be present on all modern Linuxes unless it's been disabled.
|
||||
CannotEnableSeccomp(_) => false,
|
||||
CannotUnshareUserNamespaceAndChangeRoot(_) => false,
|
||||
// Should always be present on modern Linuxes. If not, Landlock also provides FS
|
||||
// sandboxing, so don't enforce this.
|
||||
CannotUnshareUserNamespaceAndChangeRoot(_) => security_status.can_enable_landlock,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,12 +153,8 @@ impl fmt::Display for SecureModeError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors if Secure Validator Mode and some mandatory errors occurred, warn otherwise.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` if an error was printed, `false` otherwise.
|
||||
fn print_secure_mode_message(errs: Vec<SecureModeError>) -> bool {
|
||||
/// Print an error if Secure Validator Mode and some mandatory errors occurred, warn otherwise.
|
||||
fn print_secure_mode_error_or_warning(security_status: &FullSecurityStatus) {
|
||||
// Trying to run securely and some mandatory errors occurred.
|
||||
const SECURE_MODE_ERROR: &'static str = "🚨 Your system cannot securely run a validator. \
|
||||
\nRunning validation of malicious PVF code has a higher risk of compromising this machine.";
|
||||
@@ -105,39 +162,31 @@ fn print_secure_mode_message(errs: Vec<SecureModeError>) -> bool {
|
||||
// securely.
|
||||
const SECURE_MODE_WARNING: &'static str = "🚨 Some security issues have been detected. \
|
||||
\nRunning validation of malicious PVF code has a higher risk of compromising this machine.";
|
||||
// Message to be printed only when running securely and mandatory errors occurred.
|
||||
const IGNORE_SECURE_MODE_TIP: &'static str =
|
||||
"\nYou can ignore this error with the `--insecure-validator-i-know-what-i-do` \
|
||||
command line argument if you understand and accept the risks of running insecurely. \
|
||||
With this flag, security features are enabled on a best-effort basis, but not mandatory. \
|
||||
\nMore information: https://wiki.polkadot.network/docs/maintain-guides-secure-validator#secure-validator-mode";
|
||||
|
||||
if errs.is_empty() {
|
||||
return false
|
||||
}
|
||||
let all_errs_allowed = security_status.all_errs_allowed();
|
||||
let errs_string = security_status.errs_string();
|
||||
|
||||
let errs_allowed = errs.iter().all(|err| err.is_allowed_in_secure_mode());
|
||||
let errs_string: String = errs
|
||||
.iter()
|
||||
.map(|err| {
|
||||
format!(
|
||||
"\n - {}{}",
|
||||
if err.is_allowed_in_secure_mode() { "Optional: " } else { "" },
|
||||
err
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
if errs_allowed {
|
||||
if all_errs_allowed {
|
||||
gum::warn!(
|
||||
target: LOG_TARGET,
|
||||
"{}{}",
|
||||
SECURE_MODE_WARNING,
|
||||
errs_string,
|
||||
);
|
||||
false
|
||||
} else {
|
||||
gum::error!(
|
||||
target: LOG_TARGET,
|
||||
"{}{}",
|
||||
"{}{}{}",
|
||||
SECURE_MODE_ERROR,
|
||||
errs_string,
|
||||
IGNORE_SECURE_MODE_TIP
|
||||
);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,3 +347,53 @@ async fn check_seccomp(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_secure_mode_error_optionality() {
|
||||
let err = SecureModeError::CannotEnableLandlock(String::new());
|
||||
assert!(err.is_allowed_in_secure_mode(&SecurityStatus {
|
||||
secure_validator_mode: true,
|
||||
can_enable_landlock: false,
|
||||
can_enable_seccomp: false,
|
||||
can_unshare_user_namespace_and_change_root: true
|
||||
}));
|
||||
assert!(!err.is_allowed_in_secure_mode(&SecurityStatus {
|
||||
secure_validator_mode: true,
|
||||
can_enable_landlock: false,
|
||||
can_enable_seccomp: true,
|
||||
can_unshare_user_namespace_and_change_root: false
|
||||
}));
|
||||
|
||||
let err = SecureModeError::CannotEnableSeccomp(String::new());
|
||||
assert!(!err.is_allowed_in_secure_mode(&SecurityStatus {
|
||||
secure_validator_mode: true,
|
||||
can_enable_landlock: false,
|
||||
can_enable_seccomp: false,
|
||||
can_unshare_user_namespace_and_change_root: true
|
||||
}));
|
||||
assert!(!err.is_allowed_in_secure_mode(&SecurityStatus {
|
||||
secure_validator_mode: true,
|
||||
can_enable_landlock: false,
|
||||
can_enable_seccomp: true,
|
||||
can_unshare_user_namespace_and_change_root: false
|
||||
}));
|
||||
|
||||
let err = SecureModeError::CannotUnshareUserNamespaceAndChangeRoot(String::new());
|
||||
assert!(err.is_allowed_in_secure_mode(&SecurityStatus {
|
||||
secure_validator_mode: true,
|
||||
can_enable_landlock: true,
|
||||
can_enable_seccomp: false,
|
||||
can_unshare_user_namespace_and_change_root: false
|
||||
}));
|
||||
assert!(!err.is_allowed_in_secure_mode(&SecurityStatus {
|
||||
secure_validator_mode: true,
|
||||
can_enable_landlock: false,
|
||||
can_enable_seccomp: true,
|
||||
can_unshare_user_namespace_and_change_root: false
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user