Initial Commit

This commit is contained in:
Evelyn Alicke 2023-03-28 09:04:26 +02:00
commit a48db9c4c8
No known key found for this signature in database
GPG key ID: 6834780BDA479436
5 changed files with 520 additions and 0 deletions

16
Cargo.toml Normal file
View file

@ -0,0 +1,16 @@
[package]
name = "openmetrics-vici-exporter"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
config = { version = "0.13.1", features = ["yaml"] }
serde = { version = "1.0", features = ["derive"] }
prometheus-client = "0.18.1"
futures-util = "0.3.25"
actix-web-httpauth = "0.8.0"
actix-web = "4.2.1"
tokio = "1.21.2"
rsvici = "0.1"

4
README.md Normal file
View file

@ -0,0 +1,4 @@
`groupadd vici`
`chown root:vici /var/run/charon.vici`
`chmod 0770 /var/run/charon.vici`
`usermod -aG vici $user`

5
config.yml Normal file
View file

@ -0,0 +1,5 @@
---
vici_socket: "/var/run/charon.vici"
actix_bind_addr: "0.0.0.0"
actix_bind_port: "80"
actix_auth_token: ""

122
src/main.rs Normal file
View file

@ -0,0 +1,122 @@
#![allow(dead_code,non_camel_case_types)] // I don't want to be bothered for case stuff decided upon me by the VICI API
use actix_web::{web, App, HttpResponse, HttpServer, Responder, Result};
use prometheus_client::{
registry::Registry,
encoding::{text::encode},
metrics::{
family::Family,
info::Info,
counter::Counter,
gauge::Gauge,
MetricType::Unknown,
}
};
use std::{
collections::HashMap,
error::Error,
sync::Mutex,
net::IpAddr,
path::Path,
};
use futures_util::{
stream::{TryStreamExt},
pin_mut,
};
use serde::Deserialize;
use config::Config;
mod vici_structs;
#[derive(Debug, Deserialize)]
struct Configuration {
vici_socket: String,
actix_bind_addr: IpAddr,
actix_bind_port: u16,
axtix_auth_token: Option<String>,
}
pub async fn metrics_handler(state: web::Data<Mutex<AppState>>) -> Result<HttpResponse> {
let state = state.lock().unwrap();
let mut buf = Vec::new();
encode(&mut buf, &state.registry)?;
let body = std::str::from_utf8(buf.as_slice()).unwrap().to_string();
Ok(HttpResponse::Ok()
.content_type("application/openmetrics-text; version=1.0.0; charset=utf-8")
.body(body))
}
pub struct AppState {
pub registry: Registry,
pub vici: vici_structs::VICIState,
}
pub struct Metrics {
sa_uptime: Family<vici_structs::SecurityAssociationLabels, Counter>,
}
impl Metrics {
pub fn sa_uptime(&self, security_associations: vici_structs::SecurityAssociations) {
for (sa_name, sa_values) in security_associations.into_iter() {
self.sa_uptime.get_or_create(&vici_structs::SecurityAssociationLabels{uniqueid: sa_values.uniqueid}).inner(sa_values.established); // "Vertrau mir Bruder"
}
}
}
/*
pub fn get_vici_state(client: rsvici::Client) -> Result<vici_structs::VICIState, actix_web::Error>{
let version: vici_structs::Version = client.request("version", ()).await?;
let statistics: vici_structs::Statistics = client.request("statistics", ()).await?;
let connections = client.stream_request::<(), vici_structs::Connections>("list-connections", "list-conn", ());
pin_mut!(connections);
while let Some(conn) = connections.try_next().await? {
println!("{:#?}", conn);
}
}
*/
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let settings = Config::builder()
.add_source(config::File::with_name("config"))
.add_source(config::Environment::with_prefix("VICI_EXPORTER"))
.build()
.unwrap();
let mut conf: Configuration = settings.try_deserialize().unwrap();
let mut client = rsvici::unix::connect(conf.vici_socket).await?;
let mut vici_state: vici_structs::VICIState;
let metrics = web::Data::new(Metrics {
sa_uptime: Family::default(),
});
let mut state = AppState {
registry: Registry::default(),
vici: vici_structs::VICIState.update(client),
};
state.registry.register(
"sa_uptime",
"How Long a connection has been established",
Box::new(metrics.sa_uptime.clone()),
);
let state = web::Data::new(Mutex::new(state));
HttpServer::new(move || {
App::new()
//.app_data(metrics.clone())
.app_data(state.clone())
.service(web::resource("/metrics").route(web::get().to(metrics_handler)))
})
.bind((conf.actix_bind_addr, conf.actix_bind_port))?
.run()
.await
}

373
src/vici_structs.rs Normal file
View file

@ -0,0 +1,373 @@
#![allow(dead_code,non_camel_case_types)] // I don't want to be bothered for case stuff decided upon me by the VICI API
use serde::Deserialize;
use std::collections::HashMap;
use prometheus_client::{
encoding::text::Encode,
};
#[derive(Debug, Deserialize)]
pub struct VICIState {
pub version: Version,
pub statistics: Statistics,
pub policies: Policies,
pub connections: Connections,
pub security_associations: SecurityAssociations,
pub certificates: Certificates,
pub authorities: Authorities,
pub pools: Pools,
}
impl<E> VICIState {
async fn update(client: rsvici::Client) -> Result<VICIState, E> {
VICIState {
version: client.request("version", ()).await?,
statistics: client.request("statistics", ()).await?,
policies: client.stream_request::<(), Policies>("list-policies", "list-policy", ()).await?,
connections: client.stream_request::<(), Connections>("list-connections", "list-conn", ()).await?,
security_associations: client.stream_request::<(), SecurityAssociations>("list-sas", "list-sa", ()).await?,
certificates: client.stream_request::<(), Certificates>("list-certs", "list-cert", ()).await?,
authorities: client.stream_request::<(), Authorities>("list-authoroties", "list-authoroty", ()).await?,
pools: client.stream_request::<(), Pools>("list-pools", "list-pool", ()).await?,
}
}
}
// Structs for parsing the control interface
#[derive(Debug, Deserialize)]
pub struct Version {
pub daemon: String,
pub version: String,
pub sysname: String,
pub release: String,
pub machine: String,
}
#[derive(Debug, Deserialize)]
pub struct Statistics {
pub uptime: StatisticsUptime,
pub workers: StatisticsWorkers,
pub queues: StatisticsJobPriorities,
pub scheduled: String,
pub ikesecurity_associations: StatisticsIKESecurityAssociations,
pub plugins: Vec<String>,
pub mem: Option<StatisticsMem>,
pub mallinfo: Option<StatisticsMallinfo>,
}
#[derive(Debug, Deserialize)]
pub struct StatisticsUptime {
pub running: String,
pub since: String,
}
#[derive(Debug, Deserialize)]
pub struct StatisticsWorkers {
pub total: String,
pub idle: String,
pub active: StatisticsJobPriorities,
}
#[derive(Debug, Deserialize)]
pub struct StatisticsJobPriorities {
pub critical: String,
pub high: String,
pub medium: String,
pub low: String,
}
#[derive(Debug, Deserialize)]
pub struct StatisticsIKESecurityAssociations {
pub total: String,
pub half_open: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct StatisticsMem {
pub total: String,
pub allocs: String,
}
#[derive(Debug, Deserialize)]
pub struct StatisticsMallinfo {
pub sbrk: String,
pub mmap: String,
pub used: String,
pub free: String,
}
pub type Policies = HashMap<String, Policy>;
#[derive(Debug, Deserialize)]
pub struct Policy {
pub child: String,
pub ike: Option<String>,
pub mode: PolicyMode,
pub local_ts: Option<Vec<String>>,
pub remote_ts: Option<Vec<String>>,
}
#[derive(Debug, Deserialize)]
enum PolicyMode {
tunnel,
transport,
pass,
drop,
}
pub type Connections = HashMap<String, Conn>;
#[derive(Debug, Deserialize)]
pub struct Conn {
pub local_addrs: Vec<String>,
pub remote_addrs: Vec<String>,
pub version: String,
pub reauth_time: u32,
pub rekey_time: u32,
pub children: HashMap<String, ConnChildSection>,
}
#[derive(Debug, Deserialize)]
pub struct ConnAuthSection {
pub class: String,
pub eap_type: Option<String>,
pub eap_vendor: Option<String>,
pub xauth: Option<String>,
pub revocation: Option<String>,
pub id: String,
pub aaa_id: Option<String>,
pub eap_id: Option<String>,
pub xauth_id: Option<String>,
pub groups: Option<Vec<String>>,
pub certificates: Option<Vec<String>>,
pub cacerts: Option<Vec<String>>,
}
#[derive(Debug, Deserialize)]
pub struct ConnChildSection {
pub mode: ChildSecurityAssociationMode,
pub rekey_time: u32,
pub rekey_bytes: u64,
pub rekey_packets: u64,
pub local_ts: Option<Vec<String>>,
pub remote_ts: Option<Vec<String>>,
}
#[derive(Debug, Deserialize, Clone, Hash, PartialEq, Eq)]
enum ChildSecurityAssociationMode {
TUNNEL,
TRANSPORT,
BEET,
}
#[derive(Debug, Deserialize, Clone, Hash, PartialEq, Eq)]
enum ChildSecurityAssociationProtocol {
AH,
ESP,
}
pub type SecurityAssociations = HashMap<String, SecurityAssociation>;
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct SecurityAssociation {
pub uniqueid: String,
pub version: u8,
pub state: String,
pub local_host: Option<String>,
pub local_port: Option<u16>,
pub local_id: Option<String>,
pub remote_host: Option<String>,
pub remote_port: Option<u16>,
pub remote_id: Option<String>,
pub remote_xauth_id: Option<String>,
pub remote_epa_id: Option<String>,
pub initiator: Option<bool>,
pub initiator_spi: Option<String>,
pub responder_spi: Option<String>,
pub nat_local: Option<bool>,
pub nat_remote: Option<bool>,
pub nat_fake: Option<bool>,
pub nat_any: Option<bool>,
pub if_id_in: Option<String>,
pub if_id_out: Option<String>,
pub encr_alg: Option<String>,
pub encr_keysize: Option<String>,
pub integ_alg: Option<String>,
pub integ_keysize: Option<String>,
pub prf_alg: Option<String>,
pub dh_group: Option<String>,
pub established: u64,
pub rekey_time: Option<u32>,
pub reauth_time: Option<u32>,
pub local_vips: Option<Vec<String>>,
pub remote_vips: Option<Vec<String>>,
pub tasks_queued: Option<Vec<String>>,
pub tasks_active: Option<Vec<String>>,
pub tasks_passive: Option<Vec<String>>,
pub child_security_associations: Option<HashMap<String, SecurityAssociationChild>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct SecurityAssociationChild {
pub name: String,
pub uniqueid: String,
pub reqid: String,
pub state: String,
pub mode: ChildSecurityAssociationMode,
pub protocol: ChildSecurityAssociationProtocol,
pub encap: Option<bool>,
pub spi_in: String,
pub spi_out: String,
pub cpi_in: Option<String>,
pub cpi_out: Option<String>,
pub mark_in: Option<String>,
pub mark_mask_in: Option<String>,
pub mark_out: Option<String>,
pub mark_mask_out: Option<String>,
pub if_id_in: Option<String>,
pub if_id_out: Option<String>,
pub encr_alg: Option<String>,
pub encr_keysize: Option<String>,
pub integ_alg: Option<String>,
pub integ_keysize: Option<String>,
pub prf_alg: Option<String>,
pub dh_group: Option<String>,
pub esn: Option<u16>,
pub bytes_in: u64,
pub packets_in: u64,
pub use_in: Option<u32>,
pub bytes_out: u64,
pub packets_out: u64,
pub use_out: Option<u32>,
pub rekey_time: Option<u32>,
pub life_time: u32,
pub install_time: u64,
pub local_ts: Vec<String>,
pub remote_ts: Vec<String>,
}
pub type Certificates = HashMap<String, Cert>;
#[derive(Debug, Deserialize)]
pub struct Cert {
pub r#type: CertType,
pub flag: X509CertFlag,
pub has_privkey: Option<String>,
pub data: String,
pub subject: Option<String>,
pub not_before: Option<String>,
pub not_after: Option<String>,
}
#[derive(Debug, Deserialize)]
enum CertType {
X509,
X509_AC,
X509_CRL,
OSCP_RESPONSE,
PUBKEY,
}
#[derive(Debug, Deserialize)]
enum X509CertFlag {
NONE,
CA,
AA,
OCSP,
}
pub type Authorities = HashMap<String, Authority>;
#[derive(Debug, Deserialize)]
pub struct Authority {
pub cacert: String,
pub crl_uris: Vec<String>,
pub ocsp_uris: Vec<String>,
pub cert_uri_base: String,
}
pub type Pools = HashMap<String, Pool>;
#[derive(Debug,Deserialize)]
pub struct Pool {
pub name: String,
pub base: String,
pub size: u128,
pub online: u128,
pub offline: u128,
pub leases: Option<HashMap<u16,PoolLease>>,
}
#[derive(Debug,Deserialize)]
pub struct PoolLease {
pub address: String,
pub identity: String,
pub status: PoolLeaseStatus,
}
#[derive(Debug, Deserialize)]
enum PoolLeaseStatus {
online,
offline,
}
// Structs for generating metrics, TODO
/*
#[derive(Debug, Deserialize, Clone, Hash, PartialEq, Eq)]
pub struct SecurityAssociationLabels {
pub uniqueid: String,
pub local_id: String,
pub local_host: String,
pub local_port: u16,
pub remote_id: String,
pub remote_host: String,
pub remote_port: u16,
}
*/
#[derive(Debug, Deserialize, Clone, Hash, PartialEq, Eq)]
pub struct SecurityAssociationInfo {
pub uniqueid: String,
pub version: u8,
pub local_host: String,
pub local_port: u16,
pub local_id: String,
pub remote_host: String,
pub remote_port: u16,
pub remote_id: String,
pub if_id_in: String,
pub if_id_out: String,
pub encr_alg: String,
pub encr_keysize: String,
pub integ_alg: String,
pub integ_keysize: String,
pub prf_alg: String,
pub dh_group: Option<String>,
pub local_vips: Vec<String>,
pub remote_vips: Vec<String>,
}
#[derive(Debug, Deserialize, Clone, Hash, PartialEq, Eq)]
pub struct SecurityAssociationChildInfo {
pub name: String,
pub uniqueid: String,
pub reqid: String,
pub mode: ChildSecurityAssociationMode,
pub if_id_in: String,
pub if_id_out: String,
pub encr_alg: String,
pub encr_keysize: String,
pub integ_alg: String,
pub integ_keysize: String,
pub prf_alg: String,
pub dh_group: Option<String>,
pub local_ts: Vec<String>,
pub remote_ts: Vec<String>,
}
#[derive(Clone, Hash, PartialEq, Eq, Encode)]
pub struct SecurityAssociationLabels {
pub uniqueid: String,
}