fix(security): document unsafe and TLS overrides (#2835)

This commit is contained in:
安正超
2026-05-06 23:09:02 +08:00
committed by GitHub
parent 70be0804ee
commit 4728abcff1
18 changed files with 89 additions and 17 deletions
+4
View File
@@ -2160,6 +2160,8 @@ impl DiskAPI for LocalDisk {
/// Zero-copy file read using memory mapping (Unix) or efficient read (non-Unix).
/// Returns Bytes that can be shared without copying.
// SAFETY: Unix unsafe calls in this function only query page size and mmap
// a read-only file region after bounds and alignment are validated.
#[allow(unsafe_code)]
#[tracing::instrument(level = "debug", skip(self))]
async fn read_file_zero_copy(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result<Bytes> {
@@ -2212,6 +2214,8 @@ impl DiskAPI for LocalDisk {
// mmap offsets on Unix must be page-size aligned. Align the
// mapping down to the nearest page boundary, then slice out the
// originally requested logical range.
// SAFETY: `sysconf(_SC_PAGESIZE)` has no pointer arguments and
// only queries process-global OS configuration.
let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
if page_size <= 0 {
return Err(DiskError::other("failed to determine system page size"));
+3 -1
View File
@@ -443,11 +443,13 @@ impl PoolTier {
}
impl Drop for PooledBuffer {
// SAFETY: Drop has exclusive access to `self`; taking the `ManuallyDrop`
// buffer moves it exactly once into the pool when a tier still owns it.
#[allow(unsafe_code)]
fn drop(&mut self) {
// Return buffer to pool if tier reference exists
if let Some(ref tier) = self.tier {
// Safety: We're in drop(), so this is the last use of the buffer
// SAFETY: We're in drop(), so this is the last use of the buffer
// ManuallyDrop allows us to take the value without running BytesMut's drop
let buffer = unsafe { ManuallyDrop::take(&mut self.buffer) };
tier.return_buffer(buffer);
+5 -1
View File
@@ -119,6 +119,9 @@ impl ZeroCopyObjectReader {
/// let reader = ZeroCopyObjectReader::from_file_mmap_path("large_file.bin", 0, 1024).await?;
/// ```
#[cfg(unix)]
// SAFETY: The mmap is created from a read-only file handle for the
// caller-provided range, then copied into owned `Bytes` before the file and
// mapping are dropped.
#[allow(unsafe_code)]
pub async fn from_file_mmap_path(path: &std::path::Path, offset: u64, size: usize) -> Result<Self, ZeroCopyReadError> {
use memmap2::MmapOptions;
@@ -130,7 +133,8 @@ impl ZeroCopyObjectReader {
// Open the file in sync context
let std_file = std::fs::File::open(&path).map_err(|e| ZeroCopyReadError::Io(e.to_string()))?;
// Create memory map
// SAFETY: `std_file` remains open while the mapping is created and
// copied, and the mapped bytes are not exposed beyond this closure.
let mmap = unsafe { MmapOptions::new().offset(offset).len(size).map(&std_file) }
.map_err(|e| ZeroCopyReadError::Mmap(e.to_string()))?;
+6 -2
View File
@@ -82,6 +82,10 @@ export RUSTFS_KEYSTONE_CACHE_SIZE=10000
export RUSTFS_KEYSTONE_CACHE_TTL=300
```
TLS certificate verification is enabled by default. Set
`RUSTFS_KEYSTONE_VERIFY_SSL=false` only for an explicitly trusted hop; it allows
MITM attacks against the Keystone connection and emits a startup warning.
## API Documentation
### KeystoneClient
@@ -628,8 +632,8 @@ time curl -X GET http://localhost:9000/ \
- Verify token format is correct (no newlines, extra spaces)
**Issue: "SSL verification failed"**
- If using self-signed certificates, set `RUSTFS_KEYSTONE_VERIFY_SSL=false`
- Or install Keystone's CA certificate in system trust store
- Prefer installing Keystone's CA certificate in the system trust store
- If using a trusted non-production hop, set `RUSTFS_KEYSTONE_VERIFY_SSL=false`; this allows MITM attacks and emits a startup warning
**Issue: Slow performance**
- Increase cache size: `RUSTFS_KEYSTONE_CACHE_SIZE=50000`
+7
View File
@@ -58,6 +58,13 @@ impl KeystoneClient {
admin_domain: String,
verify_ssl: bool,
) -> Self {
if !verify_ssl {
warn!(
"Keystone client for '{}' is configured to skip TLS certificate verification. This permits MITM attacks and should not be used in production.",
auth_url
);
}
let client = Client::builder()
.danger_accept_invalid_certs(!verify_ssl)
.timeout(std::time::Duration::from_secs(30))
+2
View File
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// SAFETY: `generated` is prost/tonic-generated protocol code. The allowance is
// scoped to that module so generated internals do not relax lints elsewhere.
#[allow(unsafe_code)]
mod generated;
+1 -1
View File
@@ -668,7 +668,7 @@ pub fn apply_external_env_compat() -> ExternalEnvCompatReport {
let report = build_external_env_compat_report();
for (source_key, rustfs_key) in &report.mapped_pairs {
if let Ok(value) = env::var(source_key) {
// Safety: this helper is intended for early startup bootstrap
// SAFETY: this helper is intended for early startup bootstrap
// before any background threads are created.
unsafe {
env::set_var(rustfs_key, value);
+9 -2
View File
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![allow(unsafe_code)] // TODO: audit unsafe code
use crate::os::{DiskInfo, IOStats};
use std::io::Error;
use std::path::Path;
@@ -21,6 +19,9 @@ use windows::Win32::Foundation::MAX_PATH;
use windows::Win32::Storage::FileSystem::{GetDiskFreeSpaceExW, GetDiskFreeSpaceW, GetVolumeInformationW, GetVolumePathNameW};
/// Returns total and free bytes available in a directory, e.g. `C:\`.
// SAFETY: Windows API calls receive null-terminated UTF-16 paths and valid
// pointers to initialized stack output variables.
#[allow(unsafe_code)]
pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
let path_wide = to_wide_path(p.as_ref());
@@ -81,6 +82,9 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
})
}
// SAFETY: Windows volume APIs receive null-terminated UTF-16 paths and fixed
// stack buffers sized for the documented MAX_PATH outputs used here.
#[allow(unsafe_code)]
fn get_windows_fs_type(p: &[u16]) -> std::io::Result<String> {
let path = get_volume_name(p)?;
@@ -109,6 +113,9 @@ fn get_windows_fs_type(p: &[u16]) -> std::io::Result<String> {
Ok(utf16_to_string(&file_system_name_buffer))
}
// SAFETY: `v` is a null-terminated UTF-16 path and `volume_name_buffer` is a
// writable MAX_PATH-sized stack buffer for the returned volume path.
#[allow(unsafe_code)]
fn get_volume_name(v: &[u16]) -> std::io::Result<Vec<u16>> {
let mut volume_name_buffer = [0u16; MAX_PATH as usize];