Add support for rotating audit logs
Diff
flake.nix | 12 ++++++++++++
src/audit.rs | 29 ++++++++++++++++++++++++++---
src/main.rs | 19 +++++++++++++++++++
3 files changed, 52 insertions(+), 8 deletions(-)
@@ -52,6 +52,7 @@
{
Type = "exec";
ExecStart = "${self.defaultPackage."${system}"}/bin/pisshoff -c \"${conf}\"";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
Restart = "on-failure";
LogsDirectory = "pisshoff";
@@ -81,6 +82,17 @@
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
SystemCallFilter = [ "@system-service" "~@privileged" ];
};
};
services.logrotate.settings.pisshoff = {
files = "/var/log/pisshoff/audit.log";
rotate = 31;
frequency = "daily";
compress = true;
delaycompress = true;
missingok = true;
notifempty = true;
postrotate = "systemctl reload pisshoff";
};
};
};
@@ -11,13 +11,15 @@
use tokio::{
fs::OpenOptions,
io::{AsyncWriteExt, BufWriter},
sync::watch,
task::JoinHandle,
};
use tracing::debug;
use tracing::{debug, info};
use uuid::Uuid;
pub fn start_audit_writer(
config: Arc<Config>,
mut reload: watch::Receiver<()>,
) -> (
tokio::sync::mpsc::UnboundedSender<AuditLog>,
JoinHandle<Result<(), std::io::Error>>,
@@ -25,12 +27,16 @@
let (send, mut recv) = tokio::sync::mpsc::unbounded_channel();
let handle = tokio::spawn(async move {
let file = OpenOptions::default()
.create(true)
.append(true)
.open(&config.audit_output_file)
.await?;
let mut writer = BufWriter::new(file);
let open_writer = || async {
let file = OpenOptions::default()
.create(true)
.append(true)
.open(&config.audit_output_file)
.await?;
Ok::<_, std::io::Error>(BufWriter::new(file))
};
let mut writer = open_writer().await?;
let mut shutdown = false;
loop {
@@ -50,7 +56,16 @@
}
_ = tokio::time::sleep(Duration::from_secs(5)), if !writer.buffer().is_empty() && !shutdown => {
debug!("Flushing audits to disk");
writer.flush().await?;
}
Ok(()) = reload.changed(), if !shutdown => {
info!("Flushing audits to disk");
writer.flush().await?;
info!("Reopening handle to log file");
writer = open_writer().await?;
info!("Successfully re-opened log file");
}
else => break,
}
@@ -6,6 +6,7 @@
use futures::FutureExt;
use std::sync::Arc;
use thrussh::MethodSet;
use tokio::{signal::unix::SignalKind, sync::watch};
use tracing::{error, info};
use tracing_subscriber::EnvFilter;
@@ -47,7 +48,9 @@
..thrussh::server::Config::default()
});
let (audit_send, audit_handle) = audit::start_audit_writer(args.config.clone());
let (reload_send, reload_recv) = watch::channel(());
let (audit_send, audit_handle) = audit::start_audit_writer(args.config.clone(), reload_recv);
let mut audit_handle = audit_handle.fuse();
let server = Server::new(args.config.clone(), audit_send);
@@ -55,9 +58,12 @@
let fut = thrussh::server::run(thrussh_config, &listen_address, server);
let reload_watcher = watch_for_reloads(reload_send);
tokio::select! {
res = fut => res?,
res = &mut audit_handle => res??,
res = reload_watcher => res?,
_ = tokio::signal::ctrl_c() => {
info!("Received ctrl-c, initiating shutdown");
}
@@ -66,6 +72,17 @@
info!("Finishing audit log writes");
audit_handle.await??;
info!("Audit log writes finished");
Ok(())
}
async fn watch_for_reloads(send: watch::Sender<()>) -> Result<(), anyhow::Error> {
let mut signal = tokio::signal::unix::signal(SignalKind::hangup())?;
while let Some(()) = signal.recv().await {
info!("Received SIGHUP, broadcasting reload");
let _res = send.send(());
}
Ok(())
}