Compare commits

...

5 commits

Author SHA1 Message Date
9302bd110c
chore(ci): migrate to forgejo
Some checks failed
Docker / docker (push) Failing after 0s
Rust / build (push) Failing after 0s
2024-09-23 22:30:14 +02:00
ce88978583
docs: update documentation to be easier to understand 2024-09-23 22:28:33 +02:00
6de1b761a5
chore(nix): update dev flake 2024-09-23 22:28:31 +02:00
dcdee5ffaa
chore: cargo-fmt 2024-09-23 22:28:28 +02:00
bf9549d801
chore(gh-actions): provide both docker and binary releases 2024-09-23 22:28:23 +02:00
13 changed files with 338 additions and 86 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

View file

@ -0,0 +1,19 @@
---
name: Docker
on:
push:
branches: [ "main" ]
tags: [ '*' ]
pull_request:
jobs:
docker:
uses: famedly/github-workflows/.github/workflows/docker.yml@main
with:
push: ${{ github.event_name != 'pull_request' }} # Always build, don't publish on pull requests
registry_user: famedly-ci
registry: docker-oss.nexus.famedly.de
image_name: openmetrics-vici-exporter
build_args: "VERSION=${{ matrix.version }}"
secrets: inherit

View file

@ -0,0 +1,13 @@
---
name: Rust
on:
push:
branches: [main]
tags: ['*']
pull_request:
jobs:
build:
uses: famedly/github-workflows/.github/workflows/rust.yml@main
secrets: inherit

View file

@ -1,28 +0,0 @@
name: CI
on:
push:
tags:
- 'v*.*.*'
release:
types: [ published ]
jobs:
docker:
permissions: write-all
runs-on: ubuntu-latest
steps:
-
name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}
-
name: Build and push
uses: docker/build-push-action@v4
with:
push: true
platforms: linux/amd64
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.ref_name }}

View file

@ -1,28 +1,77 @@
[![CI](https://github.com/famedly/openmetrics-vici-expoter/actions/workflows/build.yml/badge.svg?event=release)](https://github.com/famedly/openmetrics-vici-expoter/actions/workflows/build.yml)
# openmetrics-vici-exporter
provides an openmetrics compatible endpoint for strongSwan charon's VICI.
initally tested against strongSwan 5.9 and strongSwan 6.0.
## deployment
pull container image from `ghcr.io`, see `docker-compose.yml` in this repo
Provides an openmetrics compatible endpoint for strongSwan charon's VICI.
## development
## Features
1. `sudo groupadd vici`
2. `sudo chown root:vici /var/run/charon.vici`
3. `sudo chmod 0770 /var/run/charon.vici`
4. `sudo usermod -aG vici $user`
5. `cargo run`
6. `curl http://localhost:8001/metrics`
as of `v0.1.0` the following metrics are exporterd:
| name | |
|------|-|
|`sa_uptime`| seconds since state changed to up |
|`sa_rekey_time`||
## license
| name |
|`sa_child_bytes_in`|
|`sa_child_bytes_out`|
|`sa_child_packets_in`|
|`sa_child_packets_out`|
### Planned Features:
* `v0.2.0`
* an info metric showing the applied configuration
* an enum metric showing the current state / queued jobs per connection
## Usage
### Deployment
You have a few options avalible:
1. [Binary Releases](/releases)
2. Docker Image
* `docker-oss.nexus.famedly.de/openmetrics-vici-exporter
### Configuration
All values have defaults, no configuration is necessary, but an exhaustive default configuration is still provided, see [`config.yml`](/blob/main/config.yml).
You can also set these as environment variables, prefixed with `VICI_EXPORTER`
| Key | Default | |
|-----|---------|-|
|`vici.socket`|`/var/run/charon.vici`| unix socket where vici is reachable |
|`vici.interval`|`10`| how often to get data from the vici, in seconds |
|`server.address`|`0.0.0.0`| any bind address, ipv6 is also allowed `[::1]`|
|`server.port`|`8000`||
## Development
if you'd like to contribute you are free to do so.
we provide a nix flake (`nix develop`) to setup the rust enviroment for you, but there's still some manual setup to do.
you need charon running with the vici plugin enabled in the configuration.
make sure your user has the required permissions to access the vici socket.
on a debian system you can just run the following to give yourself access:
``` bash
sudo groupadd vici
sudo chown root:vici /var/run/charon.vici
sudo chmod 0770 /var/run/charon.vici
sudo usermod -aG vici $(whoami)
```
` cargo run && curl http://[::1]:8001/metrics `
## License
[AGPL-3.0-only](LICENSE.md)
## authors
## Authors
This software is authored and maintained as open-source by Famedly's Infrastructure Team.
- Evelyn Alicke <e.alicke@famedly.com>
- Famedly GmbH <info@famedly.com>

View file

@ -1,7 +1,8 @@
version: '3'
services:
ove:
openmetrics-vici-exporter:
image: docker-oss.famedly.de/openmetrics-vici-exporter
restart: "unless-stopped"
environment:
- VICI_EXPORTER_VICI_SOCKET="/var/run/charon.vici"
@ -9,7 +10,7 @@ services:
- VICI_EXPORTER_SERVER_ADDRESS=0.0.0.0
- VICI_EXPORTER_SERVER_PORT=8001
volumes:
#- ./config.yml:/opt/openmetrics-vici-exporter/config.yml
# - ./config.yml:/opt/openmetrics-vici-exporter/config.yml
- /var/run/charon.vici:/var/run/charon.vici
ports:
- 8111:80/tcp
- 8001:8001/tcp

130
flake.lock generated Normal file
View file

@ -0,0 +1,130 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1705309234,
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1711333969,
"narHash": "sha256-5PiWGn10DQjMZee5NXzeA6ccsv60iLu+Xtw+mfvkUAs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "57e6b3a9e4ebec5aa121188301f04a6b8c354c9b",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1706487304,
"narHash": "sha256-LE8lVX28MV2jWJsidW13D2qrHU/RUUONendL2Q/WlJg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "90f456026d284c22b3e3497be980b2e47d0b28ac",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1711505476,
"narHash": "sha256-yK1zue1c8EdpZvEyQWrjawG9Ykzl7eB2xJ/V+2vU5Jo=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "56f48d6e7559b807763ea03191bfaf95549ce610",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

40
flake.nix Normal file
View file

@ -0,0 +1,40 @@
{
description = "A devShell example";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
rust-overlay.url = "github:oxalica/rust-overlay";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs {
inherit system overlays;
};
in
with pkgs;
{
devShells.default = mkShell {
buildInputs = [
openssl
pkg-config
eza
fd
rust-bin.beta.latest.complete
clippy
rust-analyzer
perf-tools
linuxPackages_latest.perf
];
shellHook = ''
alias ls=eza
alias find=fd
'';
};
}
);
}

View file

@ -1 +0,0 @@
max_width = 120

View file

@ -1,5 +0,0 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
packages = with pkgs; [ rustc cargo gcc rustfmt clippy ];
name = "rust-env";
}

View file

@ -28,14 +28,17 @@ pub struct Configuration {
impl Configuration {
pub async fn load() -> Result<Configuration> {
let mut s = Config::builder();
if std::fs::metadata("config").is_ok(){
s = s.add_source(config::File::with_name("config"));
} else { println!("config file not found. continuing with env vars... ") };
s = s.add_source(config::Environment::with_prefix("VICI_EXPORTER").separator("_"));
// s.build().unwrap();
let conf: Configuration = s.build().unwrap().try_deserialize().unwrap();
Ok(conf)
let settings = Config::builder()
.set_default("vici.socket", "/var/run/charon.vici")?
.set_default("vici.interval", 10)?
.set_default("server.address", "0.0.0.0")?
.set_default("server.port", 8000)?
.add_source(config::File::with_name("config"))
.add_source(config::Environment::with_prefix("VICI_EXPORTER").separator("_"))
.build();
match settings {
Ok(body) => Ok(body.try_deserialize().unwrap()),
Err(err) => Err(err.into()),
}
}
}

View file

@ -1,10 +1,4 @@
use metrics::{
describe_gauge,
gauge,
describe_counter,
counter,
IntoLabels,
Unit};
use metrics::{counter, describe_counter, describe_gauge, gauge, IntoLabels, Unit};
use metrics_exporter_prometheus::PrometheusBuilder;
use tokio::time::{interval, Duration, MissedTickBehavior};
@ -44,11 +38,26 @@ async fn main() -> anyhow::Result<()> {
let mut child_labels = sa_child_values.into_labels();
child_labels.push((&("sa_name", sa_name.clone())).into());
child_labels.push((&("sa_child_name", sa_child_name)).into());
counter!("sa_child_bytes_in", sa_child_values.bytes_in, child_labels.clone());
counter!("sa_child_bytes_out", sa_child_values.bytes_out, child_labels.clone());
counter!("sa_child_packets_in", sa_child_values.packets_in, child_labels.clone());
counter!("sa_child_packets_out", sa_child_values.packets_out, child_labels.clone());
counter!(
"sa_child_bytes_in",
sa_child_values.bytes_in,
child_labels.clone()
);
counter!(
"sa_child_bytes_out",
sa_child_values.bytes_out,
child_labels.clone()
);
counter!(
"sa_child_packets_in",
sa_child_values.packets_in,
child_labels.clone()
);
counter!(
"sa_child_packets_out",
sa_child_values.packets_out,
child_labels.clone()
);
}
}
interval.tick().await;

View file

@ -1,12 +1,12 @@
#![allow(dead_code)]
use serde::Deserialize;
use serde::{de::value::BoolDeserializer, Deserialize};
use std::collections::HashMap;
use futures_util::stream::StreamExt;
use anyhow::Result;
use metrics::{IntoLabels,Label};
use metrics::{IntoLabels, Label};
#[derive(Debug, Deserialize)]
pub struct VICIState {
@ -25,22 +25,45 @@ impl VICIState {
Ok(VICIState {
version: client.request("version", ()).await?,
statistics: client.request("statistics", ()).await?,
policies: collected_stream::<NamedPolicy, Policies>(client, "list-policies", "list-policy").await?,
connections: collected_stream::<NamedConnection, Connections>(client, "list-connections", "list-conn")
.await?,
security_associations: collected_stream::<NamedSecurityAssociation, SecurityAssociations>(
client, "list-sas", "list-sa",
policies: collected_stream::<NamedPolicy, Policies>(
client,
"list-policies",
"list-policy",
)
.await?,
certificates: collected_stream::<NamedCertificate, Certificates>(client, "list-certs", "list-cert").await?,
authorities: collected_stream::<NamedAuthority, Authorities>(client, "list-authorities", "list-authority")
connections: collected_stream::<NamedConnection, Connections>(
client,
"list-connections",
"list-conn",
)
.await?,
security_associations:
collected_stream::<NamedSecurityAssociation, SecurityAssociations>(
client, "list-sas", "list-sa",
)
.await?,
certificates: collected_stream::<NamedCertificate, Certificates>(
client,
"list-certs",
"list-cert",
)
.await?,
authorities: collected_stream::<NamedAuthority, Authorities>(
client,
"list-authorities",
"list-authority",
)
.await?,
pools: collected_stream::<NamedPool, Pools>(client, "list-pools", "list-pool").await?,
})
}
}
async fn collected_stream<N, C>(client: &mut rsvici::Client, command: &str, event: &str) -> Result<C>
async fn collected_stream<N, C>(
client: &mut rsvici::Client,
command: &str,
event: &str,
) -> Result<C>
where
N: for<'de> serde::Deserialize<'de>,
C: std::iter::Extend<N> + Default,
@ -243,7 +266,6 @@ pub struct SecurityAssociation {
pub child_security_associations: HashMap<String, SecurityAssociationChild>,
}
impl IntoLabels for &SecurityAssociationChild {
fn into_labels(self) -> Vec<Label> {
vec![
@ -253,7 +275,6 @@ impl IntoLabels for &SecurityAssociationChild {
}
}
#[derive(Debug, Deserialize)]
pub struct SecurityAssociationChild {
pub name: String,