mirror of
https://github.com/zerotier/ca_injector.git
synced 2026-05-22 16:27:33 -07:00
@@ -0,0 +1,3 @@
|
||||
/target
|
||||
/.cargo
|
||||
/.rustup
|
||||
@@ -1,2 +1,4 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
/.cargo
|
||||
/.rustup
|
||||
|
||||
@@ -7,3 +7,12 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "^1"
|
||||
which = "^4"
|
||||
glob = "^0.3"
|
||||
log = "^0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
coyote = "*"
|
||||
openssl = "*"
|
||||
tempdir = "*"
|
||||
env_logger = "*"
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
# vim: ft=dockerfile
|
||||
|
||||
FROM debian:latest
|
||||
|
||||
RUN apt-get update -qq && apt-get install curl libnss3-tools build-essential libssl-dev pkg-config ca-certificates -y
|
||||
|
||||
COPY hack/rustup.sh /bin
|
||||
RUN chmod 755 /bin/rustup.sh
|
||||
|
||||
CMD ["bash", "-c", "source /bin/rustup.sh && cd /root/ca_injector && cargo test -- --nocapture"]
|
||||
@@ -0,0 +1,9 @@
|
||||
DOCKER_IMAGE=ca-injector-test
|
||||
DOCKER_RUN=docker run -e RUST_BACKTRACE=full -it --rm -v ${PWD}:/root/ca_injector -v ${PWD}/.rustup:/root/.rustup -v ${PWD}/.cargo:/root/.cargo ${DOCKER_IMAGE}
|
||||
|
||||
test: build
|
||||
mkdir -p .cargo .rustup
|
||||
${DOCKER_RUN}
|
||||
|
||||
build:
|
||||
docker build -t ${DOCKER_IMAGE} .
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -x $HOME/.cargo/bin/rustup ]
|
||||
then
|
||||
curl -sSL sh.rustup.rs >/tmp/rustup.sh && bash /tmp/rustup.sh -y
|
||||
fi
|
||||
|
||||
if [ -f $HOME/.cargo/env ]
|
||||
then
|
||||
. $HOME/.cargo/env
|
||||
fi
|
||||
|
||||
rustup default stable
|
||||
@@ -22,6 +22,3 @@ pub fn uninstall_ca(filename: &str) -> Result<(), anyhow::Error> {
|
||||
filename
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
|
||||
+152
-11
@@ -1,8 +1,103 @@
|
||||
use std::{path::PathBuf, str::FromStr};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use glob::glob;
|
||||
use which::which;
|
||||
|
||||
fn certutil() -> Result<PathBuf, anyhow::Error> {
|
||||
Ok(which("certutil")?)
|
||||
}
|
||||
|
||||
fn nssdbs() -> Result<Vec<PathBuf>, anyhow::Error> {
|
||||
let home_var = std::env::var("HOME").unwrap_or("/".to_string());
|
||||
let home = Path::new(home_var.as_str());
|
||||
|
||||
// append all firefox profiles on the machine for the given user
|
||||
// FIXME might want to enumerate all firefox profiles on the whole machine later
|
||||
let mut paths = glob(home.join(".mozilla/firefox/*").to_str().unwrap())?
|
||||
.map(|p| p.unwrap())
|
||||
.collect::<Vec<PathBuf>>();
|
||||
|
||||
let mut other_paths = vec![
|
||||
home.join(".pki/nssdb"),
|
||||
home.join("snap/chromium/current/.pki/nssdb"),
|
||||
PathBuf::from_str("/etc/pki/nssdb")?,
|
||||
];
|
||||
|
||||
paths.append(&mut other_paths);
|
||||
|
||||
Ok(paths)
|
||||
}
|
||||
|
||||
fn install_nss(filename: &str) -> Result<(), anyhow::Error> {
|
||||
let certutil = certutil()?;
|
||||
|
||||
for db in nssdbs()? {
|
||||
match db.metadata() {
|
||||
Ok(meta) => {
|
||||
if meta.is_dir() {
|
||||
log::debug!(
|
||||
"Running certutil for {} against {} to install the cert",
|
||||
db.display(),
|
||||
filename
|
||||
);
|
||||
std::process::Command::new(certutil.clone())
|
||||
.args(vec![
|
||||
"-A",
|
||||
"-d",
|
||||
db.to_str().unwrap(),
|
||||
"-t",
|
||||
"C,,",
|
||||
"-n",
|
||||
filename,
|
||||
"-i",
|
||||
filename,
|
||||
])
|
||||
.env_clear()
|
||||
.stdin(std::process::Stdio::null())
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null())
|
||||
.status()?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn uninstall_nss(filename: &str) -> Result<(), anyhow::Error> {
|
||||
let certutil = certutil()?;
|
||||
|
||||
for db in nssdbs()? {
|
||||
match db.metadata() {
|
||||
Ok(meta) => {
|
||||
if meta.is_dir() {
|
||||
log::debug!(
|
||||
"Running certutil for {} against {} to install the cert",
|
||||
db.display(),
|
||||
filename
|
||||
);
|
||||
std::process::Command::new(certutil.clone())
|
||||
.args(vec!["-D", "-d", db.to_str().unwrap(), "-n", filename])
|
||||
.env_clear()
|
||||
.stdin(std::process::Stdio::null())
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null())
|
||||
.status()?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct TrustStoreMetadata {
|
||||
dir: &'static str,
|
||||
bin: &'static str,
|
||||
@@ -24,7 +119,7 @@ fn get_trust_store_command() -> Result<TrustStoreMetadata, anyhow::Error> {
|
||||
if md.is_dir() {
|
||||
return Ok(TrustStoreMetadata {
|
||||
dir: "/usr/local/share/ca-certificates",
|
||||
bin: "update-ca-certificates",
|
||||
bin: "/usr/sbin/update-ca-certificates",
|
||||
args: vec![],
|
||||
});
|
||||
}
|
||||
@@ -55,13 +150,21 @@ fn get_trust_store_command() -> Result<TrustStoreMetadata, anyhow::Error> {
|
||||
|
||||
fn template_filename(filename: &str, tsc: &TrustStoreMetadata) -> Result<PathBuf, anyhow::Error> {
|
||||
let pb = PathBuf::from_str(tsc.dir)?;
|
||||
Ok(pb.join(filename.replace(" ", "_").replace(".crt", ".pem")))
|
||||
Ok(pb.join(
|
||||
Path::new(filename)
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.replace(" ", "_")
|
||||
.replace(".pem", ".crt"),
|
||||
))
|
||||
}
|
||||
|
||||
fn update(tsc: &TrustStoreMetadata) -> Result<std::process::ExitStatus, anyhow::Error> {
|
||||
fn update_ca(tsc: &TrustStoreMetadata) -> Result<std::process::ExitStatus, anyhow::Error> {
|
||||
log::debug!("Executing {} {:?}", tsc.bin, tsc.args);
|
||||
|
||||
Ok(std::process::Command::new(tsc.bin)
|
||||
.args(tsc.args.clone())
|
||||
.env_clear()
|
||||
.stdin(std::process::Stdio::null())
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null())
|
||||
@@ -70,26 +173,64 @@ fn update(tsc: &TrustStoreMetadata) -> Result<std::process::ExitStatus, anyhow::
|
||||
|
||||
pub fn install_ca(filename: &str) -> Result<(), anyhow::Error> {
|
||||
let tsc = get_trust_store_command()?;
|
||||
std::fs::copy(filename, template_filename(filename, &tsc)?)?;
|
||||
let new_filename = template_filename(filename, &tsc)?;
|
||||
|
||||
let res = update(&tsc)?;
|
||||
log::debug!(
|
||||
"copying cert from {} to {}",
|
||||
filename.to_string(),
|
||||
new_filename.display()
|
||||
);
|
||||
|
||||
std::fs::copy(filename, new_filename)?;
|
||||
|
||||
let res = update_ca(&tsc)?;
|
||||
|
||||
if !res.success() {
|
||||
return Err(anyhow!("Unable to install CA certificate"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(install_nss(filename)?)
|
||||
}
|
||||
|
||||
pub fn uninstall_ca(filename: &str) -> Result<(), anyhow::Error> {
|
||||
let tsc = get_trust_store_command()?;
|
||||
std::fs::remove_file(template_filename(filename, &tsc)?)?;
|
||||
|
||||
let res = update(&tsc)?;
|
||||
let res = update_ca(&tsc)?;
|
||||
|
||||
if !res.success() {
|
||||
return Err(anyhow!("Unable to uninstall CA certificate"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(uninstall_nss(filename)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use tempdir::TempDir;
|
||||
|
||||
#[test]
|
||||
fn test_install() {
|
||||
env_logger::builder()
|
||||
.filter_level(log::LevelFilter::Debug)
|
||||
.init();
|
||||
|
||||
use coyote::acme::ca::CA;
|
||||
|
||||
for filename in vec![
|
||||
"test.pem",
|
||||
"file with spaces.pem",
|
||||
"certificate.crt",
|
||||
"this_other_thing.crt",
|
||||
] {
|
||||
let ca = CA::new_test_ca().unwrap();
|
||||
let dir = TempDir::new("").unwrap();
|
||||
|
||||
let test_pem = dir.path().join(filename);
|
||||
|
||||
std::fs::write(test_pem.clone(), ca.certificate().to_pem().unwrap()).unwrap();
|
||||
super::install_ca(test_pem.to_str().unwrap()).unwrap();
|
||||
super::uninstall_ca(test_pem.to_str().unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user