mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-02 16:21:42 +00:00
Enable Compatibility with Windows (#2333)
## Issue Addressed Windows incompatibility. ## Proposed Changes On windows, lighthouse needs to default to STDIN as tty doesn't exist. Also Windows uses ACLs for file permissions. So to mirror chmod 600, we will remove every entry in a file's ACL and add only a single SID that is an alias for the file owner. Beyond that, there were several changes made to different unit tests because windows has slightly different error messages as well as frustrating nuances around killing a process :/ ## Additional Info Tested on my Windows VM and it appears to work, also compiled & tested on Linux with these changes. Permissions look correct on both platforms now. Just waiting for my validator to activate on Prater so I can test running full validator client on windows. Co-authored-by: ethDreamer <37123614+ethDreamer@users.noreply.github.com> Co-authored-by: Michael Sproul <micsproul@gmail.com>
This commit is contained in:
@@ -74,8 +74,11 @@ impl GanacheInstance {
|
||||
/// RPC connections.
|
||||
pub fn new(network_id: u64, chain_id: u64) -> Result<Self, String> {
|
||||
let port = unused_port()?;
|
||||
|
||||
let child = Command::new("ganache-cli")
|
||||
let binary = match cfg!(windows) {
|
||||
true => "ganache-cli.cmd",
|
||||
false => "ganache-cli",
|
||||
};
|
||||
let child = Command::new(binary)
|
||||
.stdout(Stdio::piped())
|
||||
.arg("--defaultBalanceEther")
|
||||
.arg("1000000000")
|
||||
@@ -83,6 +86,8 @@ impl GanacheInstance {
|
||||
.arg("1000000000")
|
||||
.arg("--accounts")
|
||||
.arg("10")
|
||||
.arg("--keepAliveTimeout")
|
||||
.arg("0")
|
||||
.arg("--port")
|
||||
.arg(format!("{}", port))
|
||||
.arg("--mnemonic")
|
||||
@@ -94,9 +99,9 @@ impl GanacheInstance {
|
||||
.spawn()
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Failed to start ganache-cli. \
|
||||
Is it ganache-cli installed and available on $PATH? Error: {:?}",
|
||||
e
|
||||
"Failed to start {}. \
|
||||
Is it installed and available on $PATH? Error: {:?}",
|
||||
binary, e
|
||||
)
|
||||
})?;
|
||||
|
||||
@@ -105,21 +110,26 @@ impl GanacheInstance {
|
||||
|
||||
pub fn fork(&self) -> Result<Self, String> {
|
||||
let port = unused_port()?;
|
||||
|
||||
let child = Command::new("ganache-cli")
|
||||
let binary = match cfg!(windows) {
|
||||
true => "ganache-cli.cmd",
|
||||
false => "ganache-cli",
|
||||
};
|
||||
let child = Command::new(binary)
|
||||
.stdout(Stdio::piped())
|
||||
.arg("--fork")
|
||||
.arg(self.endpoint())
|
||||
.arg("--port")
|
||||
.arg(format!("{}", port))
|
||||
.arg("--keepAliveTimeout")
|
||||
.arg("0")
|
||||
.arg("--chainId")
|
||||
.arg(format!("{}", self.chain_id))
|
||||
.spawn()
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Failed to start ganache-cli. \
|
||||
Is it ganache-cli installed and available on $PATH? Error: {:?}",
|
||||
e
|
||||
"Failed to start {}. \
|
||||
Is it installed and available on $PATH? Error: {:?}",
|
||||
binary, e
|
||||
)
|
||||
})?;
|
||||
|
||||
@@ -202,6 +212,23 @@ pub fn unused_port() -> Result<u16, String> {
|
||||
|
||||
impl Drop for GanacheInstance {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.child.kill();
|
||||
if cfg!(windows) {
|
||||
// Calling child.kill() in Windows will only kill the process
|
||||
// that spawned ganache, leaving the actual ganache process
|
||||
// intact. You have to kill the whole process tree. What's more,
|
||||
// if you don't spawn ganache with --keepAliveTimeout=0, Windows
|
||||
// will STILL keep the server running even after you've ended
|
||||
// the process tree and it's disappeared from the task manager.
|
||||
// Unbelievable...
|
||||
Command::new("taskkill")
|
||||
.arg("/pid")
|
||||
.arg(self.child.id().to_string())
|
||||
.arg("/T")
|
||||
.arg("/F")
|
||||
.output()
|
||||
.expect("failed to execute taskkill");
|
||||
} else {
|
||||
let _ = self.child.kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,3 +18,8 @@ tempfile = "3.1.0"
|
||||
tokio = { version = "1.1.0", features = ["time"] }
|
||||
types = { path = "../../consensus/types" }
|
||||
sensitive_url = { path = "../../common/sensitive_url" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = "~0.3.5"
|
||||
windows-acl = "~0.3.0"
|
||||
|
||||
|
||||
@@ -5,11 +5,9 @@ pub use local_signer_test_data::*;
|
||||
pub use mock::*;
|
||||
use remote_signer_client::Client;
|
||||
pub use remote_signer_test_data::*;
|
||||
use std::fs;
|
||||
use std::fs::{create_dir, File};
|
||||
use std::io::Write;
|
||||
use std::net::IpAddr::{V4, V6};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::Path;
|
||||
use tempfile::TempDir;
|
||||
use types::{
|
||||
@@ -18,6 +16,32 @@ use types::{
|
||||
Hash256, IndexedAttestation, ProposerSlashing, PublicKeyBytes, Signature, SignatureBytes,
|
||||
SignedBeaconBlockHeader, SignedVoluntaryExit, Slot, Unsigned, VariableList, VoluntaryExit,
|
||||
};
|
||||
#[cfg(windows)]
|
||||
use winapi::um::winnt::{
|
||||
FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_READ_ATTRIBUTES, FILE_READ_EA, READ_CONTROL,
|
||||
STANDARD_RIGHTS_ALL, SYNCHRONIZE, WRITE_DAC,
|
||||
};
|
||||
|
||||
/// This is the security identifier in Windows for the owner of a file. See:
|
||||
/// - https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/security-identifiers-in-windows#well-known-sids-all-versions-of-windows
|
||||
#[cfg(windows)]
|
||||
const OWNER_SID_STR: &str = "S-1-3-4";
|
||||
/// We don't need any of the `AceFlags` listed here:
|
||||
/// - https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header
|
||||
#[cfg(windows)]
|
||||
const OWNER_ACL_ENTRY_FLAGS: u8 = 0;
|
||||
/// See here for explanation:
|
||||
/// - https://docs.microsoft.com/en-us/windows/win32/wmisdk/file-and-directory-access-rights-constants
|
||||
#[cfg(windows)]
|
||||
const OWNER_ACL_ENTRY_RESTRICT_MASK: u32 =
|
||||
FILE_READ_ATTRIBUTES | FILE_READ_EA | READ_CONTROL | WRITE_DAC | SYNCHRONIZE;
|
||||
/// Generic Rights:
|
||||
/// - https://docs.microsoft.com/en-us/windows/win32/fileio/file-security-and-access-rights
|
||||
/// STANDARD_RIGHTS_ALL
|
||||
/// - https://docs.microsoft.com/en-us/windows/win32/secauthz/access-mask
|
||||
#[cfg(windows)]
|
||||
const OWNER_ACL_ENTRY_UNRESTRICT_MASK: u32 =
|
||||
FILE_GENERIC_READ | FILE_GENERIC_WRITE | STANDARD_RIGHTS_ALL;
|
||||
|
||||
pub fn get_address(client: &Client) -> String {
|
||||
let listening_address = client.get_listening_address();
|
||||
@@ -29,11 +53,78 @@ pub fn get_address(client: &Client) -> String {
|
||||
format!("http://{}:{}", ip, listening_address.port())
|
||||
}
|
||||
|
||||
pub fn set_permissions(path: &Path, perm_octal: u32) {
|
||||
let metadata = fs::metadata(path).unwrap();
|
||||
let mut permissions = metadata.permissions();
|
||||
permissions.set_mode(perm_octal);
|
||||
fs::set_permissions(path, permissions).unwrap();
|
||||
pub fn restrict_permissions(path: &Path) {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::fs;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let metadata = fs::metadata(path).unwrap();
|
||||
let mut permissions = metadata.permissions();
|
||||
permissions.set_mode(0o0311); // set to '*-wx--x--x'
|
||||
fs::set_permissions(path, permissions).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use winapi::um::winnt::PSID;
|
||||
use windows_acl::acl::{AceType, ACL};
|
||||
|
||||
let path_str = path.to_str().unwrap();
|
||||
let mut acl = ACL::from_file_path(&path_str, false).unwrap();
|
||||
|
||||
let owner_sid = windows_acl::helper::string_to_sid(OWNER_SID_STR).unwrap();
|
||||
let entries = acl.all().unwrap();
|
||||
// remove all AccessAllow entries
|
||||
for entry in &entries {
|
||||
if let Some(ref entry_sid) = entry.sid {
|
||||
acl.remove(entry_sid.as_ptr() as PSID, Some(AceType::AccessAllow), None)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
// add single entry for minimal access to file owner
|
||||
// allowing them only to read attributes of the file
|
||||
// and read/modify permissions
|
||||
acl.add_entry(
|
||||
owner_sid.as_ptr() as PSID,
|
||||
AceType::AccessAllow,
|
||||
OWNER_ACL_ENTRY_FLAGS,
|
||||
OWNER_ACL_ENTRY_RESTRICT_MASK,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unrestrict_permissions(path: &Path) {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::fs;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let metadata = fs::metadata(path).unwrap();
|
||||
let mut permissions = metadata.permissions();
|
||||
permissions.set_mode(0o0755); // set to '*rwxr-xr-x'
|
||||
fs::set_permissions(path, permissions).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use winapi::um::winnt::PSID;
|
||||
use windows_acl::acl::{AceType, ACL};
|
||||
|
||||
let path_str = path.to_str().unwrap();
|
||||
let mut acl = ACL::from_file_path(&path_str, false).unwrap();
|
||||
|
||||
let owner_sid = windows_acl::helper::string_to_sid(OWNER_SID_STR).unwrap();
|
||||
// add single entry for file owner
|
||||
acl.add_entry(
|
||||
owner_sid.as_ptr() as PSID,
|
||||
AceType::AccessAllow,
|
||||
OWNER_ACL_ENTRY_FLAGS,
|
||||
OWNER_ACL_ENTRY_UNRESTRICT_MASK,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_key_files(tmp_dir: &TempDir) {
|
||||
|
||||
Reference in New Issue
Block a user