🏡 index : ~doyle/pisshoff.git

author Jordan Doyle <jordan@doyle.la> 2023-06-25 14:03:20.0 +01:00:00
committer Jordan Doyle <jordan@doyle.la> 2023-06-25 14:03:20.0 +01:00:00
commit
fa3feea0da4f40ae6964385689d6cb2f238dd495 [patch]
tree
cdcc7d95726d5d2e0b6e7eb68bd79f718b3b2419
parent
6ca494ccaa345251528dcf0e7036107d3167227c
download
fa3feea0da4f40ae6964385689d6cb2f238dd495.tar.gz

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(-)

diff --git a/flake.nix b/flake.nix
index e267263..4d35e92 100644
--- a/flake.nix
+++ a/flake.nix
@@ -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";
              };
            };
          };
diff --git a/src/audit.rs b/src/audit.rs
index 1f019e2..e2ae288 100644
--- a/src/audit.rs
+++ a/src/audit.rs
@@ -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,
            }
diff --git a/src/main.rs b/src/main.rs
index 6ed62d2..09b9fa4 100644
--- a/src/main.rs
+++ a/src/main.rs
@@ -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(())
}