mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-11 15:46:53 +00:00
fix(security): document unsafe and TLS overrides (#2835)
This commit is contained in:
@@ -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"));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()))?;
|
||||
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user