Compare commits
5 commits
ff32cda7df
...
d01c3f84cc
| Author | SHA1 | Date | |
|---|---|---|---|
| d01c3f84cc | |||
| 0a6b6efd7a | |||
| ddf645e19e | |||
| 97057db8d2 | |||
| 136c348f01 |
9 changed files with 50 additions and 21 deletions
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::command_runner::{SetuidCommandRunner, StdCommandRunner};
|
use crate::command_runner::{SetuidCommandRunner, StdCommandRunner};
|
||||||
use crate::resources::{
|
use crate::resources::{
|
||||||
AcmeAccountKey, AcmeChallengesDir, AcmeChallengesNginxSnippet, AcmeUser, Cert,
|
AcmeAccountKey, AcmeChallengesDir, AcmeChallengesNginxSnippet, AcmeUser, Cert, CertChain, Cron,
|
||||||
CertChain, Cron, Csr, DefaultServer, Dir, File, GitCheckout, Key, KeyAndCertBundle,
|
Csr, DefaultServer, Dir, File, GitCheckout, Key, KeyAndCertBundle, LoadedDirectory,
|
||||||
LoadedDirectory, MariaDbDatabase, MariaDbUser, NpmInstall, Owner, PhpFpmPool, PostgresqlDatabase,
|
MariaDbDatabase, MariaDbUser, NpmInstall, Owner, PhpFpmPool, PostgresqlDatabase, Resource,
|
||||||
Resource, ServeCustom, ServePhp, ServeRedir, ServeService, ServeStatic, StoredDirectory,
|
ServeCustom, ServePhp, ServeRedir, ServeService, ServeStatic, StoredDirectory,
|
||||||
SystemdSocketService, User, UserForDomain, WordpressPlugin, WordpressTranslation,
|
SystemdSocketService, User, UserForDomain, WordpressPlugin, WordpressTranslation,
|
||||||
};
|
};
|
||||||
use crate::storage::SimpleStorage;
|
use crate::storage::SimpleStorage;
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,19 @@ pub fn is_success(res: Result<Output, impl Error + 'static>) -> Result<Output, B
|
||||||
check_success(res?)
|
check_success(res?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_output(output: Output) -> Result<Vec<u8>, Box<dyn Error>> {
|
pub fn get_stdout(output: Output) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
Ok(check_success(output)?.stdout)
|
Ok(check_success(output)?.stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_stderr_or_stdout(output: Output) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
let output = check_success(output)?;
|
||||||
|
Ok(if output.stderr.is_empty() {
|
||||||
|
output.stdout
|
||||||
|
} else {
|
||||||
|
output.stderr
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
pub trait CommandRunner {
|
pub trait CommandRunner {
|
||||||
async fn run<'a>(&self, program: &str, args: &'a [&'a OsStr], input: &[u8]) -> IoResult<Output>;
|
async fn run<'a>(&self, program: &str, args: &'a [&'a OsStr], input: &[u8]) -> IoResult<Output>;
|
||||||
|
|
@ -46,7 +55,7 @@ pub trait CommandRunner {
|
||||||
args: &'a [&'a OsStr],
|
args: &'a [&'a OsStr],
|
||||||
) -> Result<Vec<u8>, Box<dyn Error>> {
|
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
let output = self.run_with_args(program, args).await?;
|
let output = self.run_with_args(program, args).await?;
|
||||||
get_output(output)
|
get_stdout(output)
|
||||||
}
|
}
|
||||||
async fn run_successfully<'a>(
|
async fn run_successfully<'a>(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ use crate::artifacts::{
|
||||||
UserName as UserNameArtifact,
|
UserName as UserNameArtifact,
|
||||||
};
|
};
|
||||||
use crate::resources::{
|
use crate::resources::{
|
||||||
AcmeAccountKey, AcmeChallengesDir, AcmeChallengesNginxSnippet, AcmeUser, Cert,
|
AcmeAccountKey, AcmeChallengesDir, AcmeChallengesNginxSnippet, AcmeUser, Cert, CertChain, Cron,
|
||||||
CertChain, Cron, Csr, DefaultServer, Dir, File, GitCheckout, Key, KeyAndCertBundle,
|
Csr, DefaultServer, Dir, File, GitCheckout, Key, KeyAndCertBundle, LoadedDirectory,
|
||||||
LoadedDirectory, MariaDbDatabase, MariaDbUser, NpmInstall, Owner, PhpFpmPool, PostgresqlDatabase,
|
MariaDbDatabase, MariaDbUser, NpmInstall, Owner, PhpFpmPool, PostgresqlDatabase, Resource,
|
||||||
Resource, ServeCustom, ServePhp, ServeRedir, ServeService, ServeStatic, StoredDirectory,
|
ServeCustom, ServePhp, ServeRedir, ServeService, ServeStatic, StoredDirectory,
|
||||||
SystemdSocketService, User, UserForDomain, WordpressPlugin, WordpressTranslation,
|
SystemdSocketService, User, UserForDomain, WordpressPlugin, WordpressTranslation,
|
||||||
};
|
};
|
||||||
use crate::to_artifact::ToArtifact;
|
use crate::to_artifact::ToArtifact;
|
||||||
|
|
@ -198,7 +198,10 @@ impl<P: Policy> ResourceLocator<AcmeUser> for DefaultLocator<P> {
|
||||||
fn locate(_resource: &AcmeUser) -> (<AcmeUser as Resource>::Artifact, Self::Prerequisites) {
|
fn locate(_resource: &AcmeUser) -> (<AcmeUser as Resource>::Artifact, Self::Prerequisites) {
|
||||||
let user_name = P::acme_user();
|
let user_name = P::acme_user();
|
||||||
let home = P::user_home(&user_name);
|
let home = P::user_home(&user_name);
|
||||||
((UserNameArtifact(user_name.into()), PathArtifact::from(home)), ())
|
(
|
||||||
|
(UserNameArtifact(user_name.into()), PathArtifact::from(home)),
|
||||||
|
(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,9 @@ impl Recorder {
|
||||||
slog_term::CompactFormat::new(decorator).build(),
|
slog_term::CompactFormat::new(decorator).build(),
|
||||||
move |record| record.level().is_at_least(filter_level),
|
move |record| record.level().is_at_least(filter_level),
|
||||||
);
|
);
|
||||||
let Ok(mutex) = Arc::try_unwrap(self.0) else { panic!("cannot unwrap Arc") }; // AsyncRecord does not implement Debug, so we cannot unwrap
|
let Ok(mutex) = Arc::try_unwrap(self.0) else {
|
||||||
|
panic!("cannot unwrap Arc")
|
||||||
|
}; // AsyncRecord does not implement Debug, so we cannot unwrap
|
||||||
for record in mutex.into_inner().unwrap() {
|
for record in mutex.into_inner().unwrap() {
|
||||||
record.log_to(&drain).unwrap();
|
record.log_to(&drain).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,12 +66,18 @@ impl<_C: CommandRunner, C: Borrow<_C>, D: AsRef<str>, P: AsRef<Path>> Symbol for
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
if output.status.success()
|
if output.status.success()
|
||||||
&& output.stdout
|
&& (output.stdout
|
||||||
== format!(
|
== format!(
|
||||||
"subject=CN = {}\nCertificate will not expire\n",
|
"subject=CN = {}\nCertificate will not expire\n",
|
||||||
self.domain.as_ref()
|
self.domain.as_ref()
|
||||||
)
|
)
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
|
|| output.stdout
|
||||||
|
== format!(
|
||||||
|
"subject=CN={}\nCertificate will not expire\n",
|
||||||
|
self.domain.as_ref()
|
||||||
|
)
|
||||||
|
.as_bytes())
|
||||||
{
|
{
|
||||||
Ok(
|
Ok(
|
||||||
self
|
self
|
||||||
|
|
@ -94,12 +100,18 @@ impl<_C: CommandRunner, C: Borrow<_C>, D: AsRef<str>, P: AsRef<Path>> Symbol for
|
||||||
.is_ok(),
|
.is_ok(),
|
||||||
)
|
)
|
||||||
} else if output.status.code() == Some(1)
|
} else if output.status.code() == Some(1)
|
||||||
&& output.stdout
|
&& (output.stdout
|
||||||
== format!(
|
== format!(
|
||||||
"subject=CN = {}\nCertificate will expire\n",
|
"subject=CN = {}\nCertificate will expire\n",
|
||||||
self.domain.as_ref()
|
self.domain.as_ref()
|
||||||
)
|
)
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
|
|| output.stdout
|
||||||
|
== format!(
|
||||||
|
"subject=CN={}\nCertificate will expire\n",
|
||||||
|
self.domain.as_ref()
|
||||||
|
)
|
||||||
|
.as_bytes())
|
||||||
{
|
{
|
||||||
Ok(false)
|
Ok(false)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ impl<C: AsRef<[u8]>, U: AsRef<str>, R: CommandRunner> Symbol for Cron<'_, C, U,
|
||||||
.run(
|
.run(
|
||||||
"crontab",
|
"crontab",
|
||||||
args!["-u", self.user.as_ref(), "-",],
|
args!["-u", self.user.as_ref(), "-",],
|
||||||
self.content.as_ref(),
|
self.content.as_ref(), // input
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
)?;
|
)?;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::command_runner::CommandRunner;
|
use crate::command_runner::{get_stderr_or_stdout, CommandRunner};
|
||||||
use crate::symbols::Symbol;
|
use crate::symbols::Symbol;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
|
@ -32,13 +32,14 @@ impl<C: CommandRunner, D: Borrow<str>, K: AsRef<Path>, P: AsRef<Path>> Symbol fo
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = self
|
let result = self
|
||||||
.command_runner
|
.command_runner
|
||||||
.get_stderr(
|
.run_with_args(
|
||||||
"openssl",
|
"openssl",
|
||||||
args!["req", "-in", self.csr_path.as_ref(), "-noout", "-verify",],
|
args!["req", "-in", self.csr_path.as_ref(), "-noout", "-verify",],
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
let output = get_stderr_or_stdout(result)?;
|
||||||
Ok(output == b"verify OK\n" || output == b"Certificate request self-signature verify OK\n")
|
Ok(output == b"verify OK\n" || output == b"Certificate request self-signature verify OK\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ use crate::symbols::Symbol;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use tokio::sync::Semaphore;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use tokio::sync::Semaphore;
|
||||||
|
|
||||||
pub type Wait = Lazy<Semaphore>;
|
pub type Wait = Lazy<Semaphore>;
|
||||||
static WAIT: Wait = Lazy::new(|| Semaphore::new(1));
|
static WAIT: Wait = Lazy::new(|| Semaphore::new(1));
|
||||||
|
|
@ -74,6 +74,7 @@ mod test {
|
||||||
let symbol = User {
|
let symbol = User {
|
||||||
user_name: "nonexisting",
|
user_name: "nonexisting",
|
||||||
command_runner: StdCommandRunner,
|
command_runner: StdCommandRunner,
|
||||||
|
home_path: "/home/nonexisting",
|
||||||
};
|
};
|
||||||
assert_eq!(run(symbol.target_reached()).unwrap(), false);
|
assert_eq!(run(symbol.target_reached()).unwrap(), false);
|
||||||
}
|
}
|
||||||
|
|
@ -83,6 +84,7 @@ mod test {
|
||||||
let symbol = User {
|
let symbol = User {
|
||||||
user_name: "root",
|
user_name: "root",
|
||||||
command_runner: StdCommandRunner,
|
command_runner: StdCommandRunner,
|
||||||
|
home_path: "/root",
|
||||||
};
|
};
|
||||||
assert_eq!(run(symbol.target_reached()).unwrap(), true);
|
assert_eq!(run(symbol.target_reached()).unwrap(), true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ fn test(
|
||||||
#[test]
|
#[test]
|
||||||
fn can_create_an_acme_user() {
|
fn can_create_an_acme_user() {
|
||||||
let mut result = test(1, |setup| {
|
let mut result = test(1, |setup| {
|
||||||
assert_eq!(&*(run(setup.add(AcmeUser)).unwrap().0).0, "acme");
|
assert_eq!(((run(setup.add(AcmeUser)).unwrap().0).0).0.as_ref(), "acme");
|
||||||
});
|
});
|
||||||
let entry = result
|
let entry = result
|
||||||
.pop()
|
.pop()
|
||||||
|
|
@ -127,7 +127,7 @@ fn can_create_an_acme_cert() {
|
||||||
.pop()
|
.pop()
|
||||||
.expect("log is empty but should contain one entry");
|
.expect("log is empty but should contain one entry");
|
||||||
assert_eq!(entry.0, 3, "log entry has wrong level");
|
assert_eq!(entry.0, 3, "log entry has wrong level");
|
||||||
assert_eq!(entry.1.matches("run_symbol").count(), 19);
|
assert_eq!(entry.1.matches("run_symbol").count(), 18);
|
||||||
assert_eq!(result.len(), 0, "log has more than one entry");
|
assert_eq!(result.len(), 0, "log has more than one entry");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue