v1.0.0 Release - All Phases Complete
Phase 2: Core API Development - 15 REST API endpoints (packages, patches, system, jobs, websocket) - mTLS authentication layer (src/auth/mtls.rs) - IP whitelist enforcement (src/auth/whitelist.rs) - Job manager with async operation support - WebSocket streaming for job status Phase 3: Security Hardening - Security testing: 16/16 tests passing - Fuzz testing: 21 tests, all findings resolved - Threat model validation (STRIDE matrix) - TLS binding fix (critical vulnerability resolved) - Security documentation complete Phase 4: Production Readiness - Performance benchmarking (all targets met) - Package creation (.deb/.rpm structures) - Documentation (README, API docs, deployment guide) - Security hardening (6 vulnerabilities fixed) Deliverables: - API_DOCUMENTATION.md (889 lines) - DEPLOYMENT_GUIDE.md (733 lines) - SECURITY.md (346 lines) - README.md (525 lines) - debian/ package structure - linux-patch-api.spec (RPM) - install.sh installer script - benches/api_benchmarks.rs - Multiple security/performance reports Security Status: 0 vulnerabilities remaining Test Coverage: 31 unit tests, 21 integration tests Build Status: Release optimized
This commit is contained in:
240
tests/integration/auth_test.rs
Normal file
240
tests/integration/auth_test.rs
Normal file
@ -0,0 +1,240 @@
|
||||
//! Integration Tests for Authentication Layer
|
||||
//!
|
||||
//! Tests mTLS authentication and IP whitelist enforcement.
|
||||
|
||||
use linux_patch_api::auth::{mtls, whitelist, AuthResult};
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mtls_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_mtls_config_creation() {
|
||||
let config = mtls::MtlsConfig {
|
||||
ca_cert_path: "/etc/linux_patch_api/certs/ca.pem".to_string(),
|
||||
server_cert_path: "/etc/linux_patch_api/certs/server.pem".to_string(),
|
||||
server_key_path: "/etc/linux_patch_api/certs/server.key".to_string(),
|
||||
min_tls_version: "1.3".to_string(),
|
||||
};
|
||||
|
||||
assert_eq!(config.ca_cert_path, "/etc/linux_patch_api/certs/ca.pem");
|
||||
assert_eq!(config.server_cert_path, "/etc/linux_patch_api/certs/server.pem");
|
||||
assert_eq!(config.server_key_path, "/etc/linux_patch_api/certs/server.key");
|
||||
assert_eq!(config.min_tls_version, "1.3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mtls_error_types() {
|
||||
// Test that error types can be created
|
||||
let io_error = mtls::MtlsError::IoError("test".to_string());
|
||||
assert!(io_error.to_string().contains("test"));
|
||||
|
||||
let parse_error = mtls::MtlsError::ParseError("test".to_string());
|
||||
assert!(parse_error.to_string().contains("test"));
|
||||
|
||||
let validation_error = mtls::MtlsError::ValidationError("test".to_string());
|
||||
assert!(validation_error.to_string().contains("test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_client_cert_info() {
|
||||
let info = mtls::ClientCertInfo {
|
||||
subject: "CN=client001,O=Internal,C=US".to_string(),
|
||||
issuer: "CN=Linux Patch API CA,O=Internal,C=US".to_string(),
|
||||
serial: "01".to_string(),
|
||||
not_before: chrono::Utc::now(),
|
||||
not_after: chrono::Utc::now() + chrono::Duration::days(365),
|
||||
};
|
||||
|
||||
assert!(info.subject.contains("CN=client001"));
|
||||
assert!(info.issuer.contains("Linux Patch API CA"));
|
||||
assert_eq!(info.serial, "01");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod whitelist_tests {
|
||||
use super::*;
|
||||
use std::fs;
|
||||
use tempfile::TempDir;
|
||||
|
||||
fn create_test_whitelist(content: &str) -> (TempDir, String) {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let whitelist_path = temp_dir.path().join("whitelist.yaml");
|
||||
fs::write(&whitelist_path, content).unwrap();
|
||||
(temp_dir, whitelist_path.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_whitelist_single_ip() {
|
||||
let (_temp_dir, whitelist_path) = create_test_whitelist(
|
||||
r#"entries:
|
||||
- "192.168.1.100"
|
||||
"#,
|
||||
);
|
||||
|
||||
let manager = whitelist::WhitelistManager::new(&whitelist_path).unwrap();
|
||||
|
||||
let allowed_ip: Ipv4Addr = "192.168.1.100".parse().unwrap();
|
||||
assert!(manager.is_allowed(&allowed_ip));
|
||||
|
||||
let denied_ip: Ipv4Addr = "192.168.1.101".parse().unwrap();
|
||||
assert!(!manager.is_allowed(&denied_ip));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_whitelist_cidr_subnet() {
|
||||
let (_temp_dir, whitelist_path) = create_test_whitelist(
|
||||
r#"entries:
|
||||
- "192.168.1.0/24"
|
||||
"#,
|
||||
);
|
||||
|
||||
let manager = whitelist::WhitelistManager::new(&whitelist_path).unwrap();
|
||||
|
||||
// IPs within subnet should be allowed
|
||||
assert!(manager.is_allowed(&"192.168.1.1".parse().unwrap()));
|
||||
assert!(manager.is_allowed(&"192.168.1.100".parse().unwrap()));
|
||||
assert!(manager.is_allowed(&"192.168.1.254".parse().unwrap()));
|
||||
|
||||
// IPs outside subnet should be denied
|
||||
assert!(!manager.is_allowed(&"192.168.2.1".parse().unwrap()));
|
||||
assert!(!manager.is_allowed(&"192.167.1.1".parse().unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_whelist_multiple_entries() {
|
||||
let (_temp_dir, whitelist_path) = create_test_whitelist(
|
||||
r#"entries:
|
||||
- "192.168.1.0/24"
|
||||
- "10.0.0.50"
|
||||
- "172.16.0.0/16"
|
||||
"#,
|
||||
);
|
||||
|
||||
let manager = whitelist::WhitelistManager::new(&whitelist_path).unwrap();
|
||||
|
||||
// All these should be allowed
|
||||
assert!(manager.is_allowed(&"192.168.1.100".parse().unwrap()));
|
||||
assert!(manager.is_allowed(&"10.0.0.50".parse().unwrap()));
|
||||
assert!(manager.is_allowed(&"172.16.50.100".parse().unwrap()));
|
||||
|
||||
// These should be denied
|
||||
assert!(!manager.is_allowed(&"192.168.2.100".parse().unwrap()));
|
||||
assert!(!manager.is_allowed(&"10.0.0.51".parse().unwrap()));
|
||||
assert!(!manager.is_allowed(&"172.17.0.1".parse().unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_whitelist_entry_count() {
|
||||
let (_temp_dir, whitelist_path) = create_test_whitelist(
|
||||
r#"entries:
|
||||
- "192.168.1.0/24"
|
||||
- "10.0.0.50"
|
||||
"#,
|
||||
);
|
||||
|
||||
let manager = whitelist::WhitelistManager::new(&whitelist_path).unwrap();
|
||||
assert_eq!(manager.entry_count(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_whitelist_socket_addr() {
|
||||
use std::net::SocketAddr;
|
||||
|
||||
let (_temp_dir, whitelist_path) = create_test_whitelist(
|
||||
r#"entries:
|
||||
- "192.168.1.0/24"
|
||||
"#,
|
||||
);
|
||||
|
||||
let manager = whitelist::WhitelistManager::new(&whitelist_path).unwrap();
|
||||
|
||||
let allowed_socket: SocketAddr = "192.168.1.100:8080".parse().unwrap();
|
||||
assert!(manager.is_socket_allowed(&allowed_socket));
|
||||
|
||||
let denied_socket: SocketAddr = "192.168.2.100:8080".parse().unwrap();
|
||||
assert!(!manager.is_socket_allowed(&denied_socket));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod auth_result_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_auth_result_fully_authenticated() {
|
||||
let result = AuthResult {
|
||||
mtls_valid: true,
|
||||
ip_allowed: true,
|
||||
cert_info: None,
|
||||
client_ip: Some("192.168.1.100".parse().unwrap()),
|
||||
};
|
||||
|
||||
assert!(result.is_authenticated());
|
||||
assert!(result.mtls_valid);
|
||||
assert!(result.ip_allowed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_result_mtls_failed() {
|
||||
let result = AuthResult {
|
||||
mtls_valid: false,
|
||||
ip_allowed: true,
|
||||
cert_info: None,
|
||||
client_ip: Some("192.168.1.100".parse().unwrap()),
|
||||
};
|
||||
|
||||
assert!(!result.is_authenticated());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_result_ip_denied() {
|
||||
let result = AuthResult {
|
||||
mtls_valid: true,
|
||||
ip_allowed: false,
|
||||
cert_info: None,
|
||||
client_ip: Some("192.168.1.100".parse().unwrap()),
|
||||
};
|
||||
|
||||
assert!(!result.is_authenticated());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_result_both_failed() {
|
||||
let result = AuthResult {
|
||||
mtls_valid: false,
|
||||
ip_allowed: false,
|
||||
cert_info: None,
|
||||
client_ip: Some("192.168.1.100".parse().unwrap()),
|
||||
};
|
||||
|
||||
assert!(!result.is_authenticated());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_result_with_cert_info() {
|
||||
let cert_info = mtls::ClientCertInfo {
|
||||
subject: "CN=client001".to_string(),
|
||||
issuer: "CN=Linux Patch API CA".to_string(),
|
||||
serial: "01".to_string(),
|
||||
not_before: chrono::Utc::now(),
|
||||
not_after: chrono::Utc::now() + chrono::Duration::days(365),
|
||||
};
|
||||
|
||||
let result = AuthResult {
|
||||
mtls_valid: true,
|
||||
ip_allowed: true,
|
||||
cert_info: Some(cert_info),
|
||||
client_ip: Some("192.168.1.100".parse().unwrap()),
|
||||
};
|
||||
|
||||
assert!(result.is_authenticated());
|
||||
assert!(result.cert_info.is_some());
|
||||
assert_eq!(
|
||||
result.cert_info.unwrap().subject,
|
||||
"CN=client001"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user