├── .clog.toml ├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ ├── lint.yml │ ├── release.yml │ ├── release_nightly.yml │ └── security_audit.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.rs └── src ├── cli.rs ├── macros.rs ├── main.rs ├── metrics.rs └── wireguard.rs /.clog.toml: -------------------------------------------------------------------------------- 1 | [clog] 2 | repository = "https://github.com/kbknapp/iptables_exporter" 3 | subtitle = "iptables_exporter Release" 4 | 5 | [sections] 6 | Documentation = ["docs", "doc"] 7 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: kbknapp 4 | patreon: kbknapp 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | on: 3 | push: 4 | branches: main 5 | pull_request: 6 | paths: 7 | - src/** 8 | - Cargo.toml 9 | - Cargo.lock 10 | - build.rs 11 | 12 | name: Continuous Integration 13 | 14 | jobs: 15 | ci: 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | rust: 20 | - stable 21 | - nightly 22 | - 1.58.1 # MSRV 23 | 24 | steps: 25 | - uses: actions/checkout@v2 26 | 27 | - uses: actions-rs/toolchain@v1 28 | with: 29 | profile: minimal 30 | toolchain: ${{ matrix.rust }} 31 | override: true 32 | 33 | - uses: actions-rs/cargo@v1 34 | with: 35 | command: build 36 | 37 | - uses: actions-rs/cargo@v1 38 | with: 39 | command: test 40 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | on: 3 | pull_request: 4 | paths: 5 | - src/**/*.rs 6 | - build.rs 7 | 8 | name: PR Lints 9 | jobs: 10 | lints: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - uses: actions-rs/toolchain@v1 16 | with: 17 | profile: minimal 18 | toolchain: nightly 19 | override: true 20 | components: clippy, rustfmt 21 | 22 | - uses: actions-rs/clippy-check@v1 23 | with: 24 | token: ${{ secrets.GITHUB_TOKEN }} 25 | args: --all-features 26 | 27 | - uses: actions-rs/cargo@v1 28 | with: 29 | command: fmt 30 | args: --all -- --check 31 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | on: 3 | push: 4 | tags: 5 | - 'v*.*.*' 6 | 7 | name: Create Release 8 | 9 | env: 10 | RELEASE_BIN: wireguard_exporter 11 | RELEASE_ADDS: >- 12 | README.md 13 | LICENSE-MIT 14 | LICENSE-APACHE 15 | CHANGELOG.md 16 | 17 | jobs: 18 | release: 19 | name: Release 20 | 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions-rs/toolchain@v1 26 | with: 27 | profile: minimal 28 | toolchain: stable 29 | override: true 30 | target: x86_64-unknown-linux-musl 31 | 32 | - name: Get the version 33 | id: get_version 34 | run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} 35 | 36 | - uses: actions-rs/cargo@v1 37 | with: 38 | command: build 39 | args: --release --target x86_64-unknown-linux-musl 40 | 41 | - name: Make artifacts dir 42 | run: mkdir -p artifacts/ 43 | 44 | - name: Copy all artifacts into dir 45 | run: cp target/x86_64-unknown-linux-musl/release/${{ env.RELEASE_BIN }} ${{ env.RELEASE_ADDS }} artifacts/ 46 | 47 | - name: Create archive for Linux 48 | run: cd artifacts/ && tar czf ../${{ env.RELEASE_BIN }}-${{ steps.get_version.outputs.VERSION }}-x86_64-linux-musl.tar.gz ./* 49 | 50 | - name: Release 51 | uses: softprops/action-gh-release@v1 52 | with: 53 | body_path: CHANGELOG.md 54 | files: | 55 | ${{ env.RELEASE_BIN }}-${{ steps.get_version.outputs.VERSION }}-x86_64-linux-musl.tar.gz 56 | env: 57 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 58 | GITHUB_REPOSITORY: kbknapp/wireguard_exporter 59 | -------------------------------------------------------------------------------- /.github/workflows/release_nightly.yml: -------------------------------------------------------------------------------- 1 | --- 2 | on: 3 | push: 4 | branches: main 5 | workflow_call: 6 | 7 | name: Nightly Release 8 | 9 | env: 10 | RELEASE_BIN: wireguard_exporter 11 | RELEASE_ADDS: >- 12 | LICENSE-APACHE 13 | LICENSE-MIT 14 | nightly-CHANGELOG.md 15 | README.md 16 | 17 | jobs: 18 | nightly-release: 19 | name: Nightly Release 20 | 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions-rs/toolchain@v1 26 | with: 27 | profile: minimal 28 | toolchain: nightly 29 | override: true 30 | target: x86_64-unknown-linux-musl 31 | 32 | - name: Compile 33 | uses: actions-rs/cargo@v1 34 | with: 35 | command: build 36 | args: --release --target x86_64-unknown-linux-musl 37 | 38 | - name: Install CLOG 39 | uses: actions-rs/cargo@v1 40 | with: 41 | command: install 42 | args: clog-cli 43 | 44 | - name: Generate Changelog 45 | run: clog -F -o nightly-CHANGELOG.md -i /dev/null 46 | 47 | - name: Make artifacts dir 48 | run: mkdir -p artifacts/ 49 | 50 | - name: Copy all artifacts into dir 51 | run: cp target/x86_64-unknown-linux-musl/release/${{ env.RELEASE_BIN }} ${{ env.RELEASE_ADDS }} artifacts/ 52 | 53 | - uses: benjlevesque/short-sha@v1.2 54 | id: short-sha 55 | 56 | - name: Create archive for Linux 57 | run: cd artifacts/ && tar czf ../${{ env.RELEASE_BIN }}-${{ env.SHA }}-x86_64-linux-musl.tar.gz ./* 58 | env: 59 | SHA: ${{ steps.short-sha.outputs.sha }} 60 | 61 | - name: Remove previous Nightly Release 62 | uses: dev-drprasad/delete-tag-and-release@v1.0.1 63 | with: 64 | delete_release: true 65 | tag_name: nightly 66 | repo: kbknapp/wireguard_exporter 67 | env: 68 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 69 | 70 | - name: Create Nightly Release 71 | uses: softprops/action-gh-release@v1 72 | with: 73 | name: wireguard_exporter Nightly (${{ env.SHA }}) 74 | tag_name: nightly 75 | prerelease: true 76 | body_path: nightly-CHANGELOG.md 77 | files: | 78 | ${{ env.RELEASE_BIN }}-${{ env.SHA }}-x86_64-linux-musl.tar.gz 79 | env: 80 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 81 | GITHUB_REPOSITORY: kbknapp/wireguard_exporter 82 | SHA: ${{ steps.short-sha.outputs.sha }} 83 | -------------------------------------------------------------------------------- /.github/workflows/security_audit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | on: 3 | push: 4 | paths: 5 | - '**/Cargo.toml' 6 | - '**/Cargo.lock' 7 | 8 | name: Security audit 9 | 10 | jobs: 11 | security_audit: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v1 15 | - uses: actions-rs/audit-check@v1 16 | with: 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | target/ 12 | 13 | # Temp files 14 | .*~ 15 | 16 | # Backup files 17 | *.bak 18 | *.bk 19 | *.orig 20 | 21 | # Project files 22 | .vscode/* 23 | .idea/* 24 | 25 | # ctags 26 | **/*.vi 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Features 4 | 5 | - Can now lookup country geoip lookup for endpoint addresses via MaxMindDB ([#6](https://github.com/kbknapp/wireguard_exporter/pull/6)) (Thanks to [@furmur](https://github.com/furmur)) 6 | 7 | ### Important Changes 8 | 9 | - bump MSRV to 1.58.1 (3713673)[https://github.com/kbknapp/wireguard_exporter/commit/3713673adc0b1391076086eb41c4b2d14403b43e] 10 | 11 | 12 | ## 0.2.0 Initial Release (2024-03-28) 13 | 14 | ### Features 15 | 16 | - Adds metrics for peer endpoints ([#5](https://github.com/kbknapp/wireguard_exporter/pull/5)) (Thanks to [@furmur](https://github.com/furmur)) 17 | - Adds peer aliases ([#1](https://github.com/kbknapp/wireguard_exporter/pull/1)) 18 | 19 | ### Maintanance 20 | 21 | - Switch crate `chrono` for `time` ([#3](https://github.com/kbknapp/wireguard_exporter/pull/3)) 22 | 23 | ### Documentation 24 | 25 | - Fix links in README ([#2](https://github.com/kbknapp/wireguard_exporter/pull/2)) 26 | 27 | 28 | ## 0.1.0 Initial Release (2021-11-20) 29 | 30 | - Initial Release 31 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.17.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "ansi_term" 22 | version = "0.12.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 25 | dependencies = [ 26 | "winapi", 27 | ] 28 | 29 | [[package]] 30 | name = "atty" 31 | version = "0.2.14" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 34 | dependencies = [ 35 | "hermit-abi", 36 | "libc", 37 | "winapi", 38 | ] 39 | 40 | [[package]] 41 | name = "autocfg" 42 | version = "1.0.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 45 | 46 | [[package]] 47 | name = "backtrace" 48 | version = "0.3.63" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" 51 | dependencies = [ 52 | "addr2line", 53 | "cc", 54 | "cfg-if", 55 | "libc", 56 | "miniz_oxide", 57 | "object", 58 | "rustc-demangle", 59 | ] 60 | 61 | [[package]] 62 | name = "base64" 63 | version = "0.13.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 66 | 67 | [[package]] 68 | name = "bitflags" 69 | version = "1.3.2" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 72 | 73 | [[package]] 74 | name = "bytes" 75 | version = "1.1.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" 78 | 79 | [[package]] 80 | name = "cc" 81 | version = "1.0.72" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" 84 | 85 | [[package]] 86 | name = "cfg-if" 87 | version = "1.0.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 90 | 91 | [[package]] 92 | name = "clap" 93 | version = "3.0.0-beta.5" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "feff3878564edb93745d58cf63e17b63f24142506e7a20c87a5521ed7bfb1d63" 96 | dependencies = [ 97 | "atty", 98 | "bitflags", 99 | "clap_derive", 100 | "indexmap", 101 | "lazy_static", 102 | "os_str_bytes", 103 | "strsim", 104 | "termcolor", 105 | "textwrap", 106 | "unicase", 107 | ] 108 | 109 | [[package]] 110 | name = "clap_derive" 111 | version = "3.0.0-beta.5" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "8b15c6b4f786ffb6192ffe65a36855bc1fc2444bcd0945ae16748dcd6ed7d0d3" 114 | dependencies = [ 115 | "heck", 116 | "proc-macro-error", 117 | "proc-macro2", 118 | "quote", 119 | "syn", 120 | ] 121 | 122 | [[package]] 123 | name = "color-eyre" 124 | version = "0.5.11" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7" 127 | dependencies = [ 128 | "backtrace", 129 | "eyre", 130 | "indenter", 131 | "once_cell", 132 | "owo-colors", 133 | ] 134 | 135 | [[package]] 136 | name = "eyre" 137 | version = "0.6.5" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "221239d1d5ea86bf5d6f91c9d6bc3646ffe471b08ff9b0f91c44f115ac969d2b" 140 | dependencies = [ 141 | "indenter", 142 | "once_cell", 143 | ] 144 | 145 | [[package]] 146 | name = "fnv" 147 | version = "1.0.7" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 150 | 151 | [[package]] 152 | name = "futures-channel" 153 | version = "0.3.17" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" 156 | dependencies = [ 157 | "futures-core", 158 | ] 159 | 160 | [[package]] 161 | name = "futures-core" 162 | version = "0.3.17" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" 165 | 166 | [[package]] 167 | name = "futures-task" 168 | version = "0.3.17" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" 171 | 172 | [[package]] 173 | name = "futures-util" 174 | version = "0.3.17" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" 177 | dependencies = [ 178 | "autocfg", 179 | "futures-core", 180 | "futures-task", 181 | "pin-project-lite", 182 | "pin-utils", 183 | ] 184 | 185 | [[package]] 186 | name = "gimli" 187 | version = "0.26.1" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" 190 | 191 | [[package]] 192 | name = "hashbrown" 193 | version = "0.11.2" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 196 | 197 | [[package]] 198 | name = "heck" 199 | version = "0.3.3" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 202 | dependencies = [ 203 | "unicode-segmentation", 204 | ] 205 | 206 | [[package]] 207 | name = "hermit-abi" 208 | version = "0.1.19" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 211 | dependencies = [ 212 | "libc", 213 | ] 214 | 215 | [[package]] 216 | name = "http" 217 | version = "0.2.5" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" 220 | dependencies = [ 221 | "bytes", 222 | "fnv", 223 | "itoa", 224 | ] 225 | 226 | [[package]] 227 | name = "http-body" 228 | version = "0.4.4" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" 231 | dependencies = [ 232 | "bytes", 233 | "http", 234 | "pin-project-lite", 235 | ] 236 | 237 | [[package]] 238 | name = "httparse" 239 | version = "1.5.1" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" 242 | 243 | [[package]] 244 | name = "httpdate" 245 | version = "1.0.2" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 248 | 249 | [[package]] 250 | name = "hyper" 251 | version = "0.14.15" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "436ec0091e4f20e655156a30a0df3770fe2900aa301e548e08446ec794b6953c" 254 | dependencies = [ 255 | "bytes", 256 | "futures-channel", 257 | "futures-core", 258 | "futures-util", 259 | "http", 260 | "http-body", 261 | "httparse", 262 | "httpdate", 263 | "itoa", 264 | "pin-project-lite", 265 | "socket2", 266 | "tokio", 267 | "tower-service", 268 | "tracing", 269 | "want", 270 | ] 271 | 272 | [[package]] 273 | name = "indenter" 274 | version = "0.3.3" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" 277 | 278 | [[package]] 279 | name = "indexmap" 280 | version = "1.7.0" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" 283 | dependencies = [ 284 | "autocfg", 285 | "hashbrown", 286 | ] 287 | 288 | [[package]] 289 | name = "instant" 290 | version = "0.1.12" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 293 | dependencies = [ 294 | "cfg-if", 295 | ] 296 | 297 | [[package]] 298 | name = "ipnetwork" 299 | version = "0.20.0" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" 302 | dependencies = [ 303 | "serde", 304 | ] 305 | 306 | [[package]] 307 | name = "itoa" 308 | version = "0.4.8" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 311 | 312 | [[package]] 313 | name = "lazy_static" 314 | version = "1.4.0" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 317 | 318 | [[package]] 319 | name = "libc" 320 | version = "0.2.107" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" 323 | 324 | [[package]] 325 | name = "lock_api" 326 | version = "0.4.5" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" 329 | dependencies = [ 330 | "scopeguard", 331 | ] 332 | 333 | [[package]] 334 | name = "log" 335 | version = "0.4.14" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 338 | dependencies = [ 339 | "cfg-if", 340 | ] 341 | 342 | [[package]] 343 | name = "maxminddb" 344 | version = "0.24.0" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "d6087e5d8ea14861bb7c7f573afbc7be3798d3ef0fae87ec4fd9a4de9a127c3c" 347 | dependencies = [ 348 | "ipnetwork", 349 | "log", 350 | "memchr", 351 | "serde", 352 | ] 353 | 354 | [[package]] 355 | name = "memchr" 356 | version = "2.4.1" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 359 | 360 | [[package]] 361 | name = "miniz_oxide" 362 | version = "0.4.4" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 365 | dependencies = [ 366 | "adler", 367 | "autocfg", 368 | ] 369 | 370 | [[package]] 371 | name = "mio" 372 | version = "0.7.14" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" 375 | dependencies = [ 376 | "libc", 377 | "log", 378 | "miow", 379 | "ntapi", 380 | "winapi", 381 | ] 382 | 383 | [[package]] 384 | name = "miow" 385 | version = "0.3.7" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 388 | dependencies = [ 389 | "winapi", 390 | ] 391 | 392 | [[package]] 393 | name = "ntapi" 394 | version = "0.3.6" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 397 | dependencies = [ 398 | "winapi", 399 | ] 400 | 401 | [[package]] 402 | name = "num_cpus" 403 | version = "1.13.0" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 406 | dependencies = [ 407 | "hermit-abi", 408 | "libc", 409 | ] 410 | 411 | [[package]] 412 | name = "object" 413 | version = "0.27.1" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" 416 | dependencies = [ 417 | "memchr", 418 | ] 419 | 420 | [[package]] 421 | name = "once_cell" 422 | version = "1.8.0" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" 425 | 426 | [[package]] 427 | name = "os_str_bytes" 428 | version = "4.2.0" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "addaa943333a514159c80c97ff4a93306530d965d27e139188283cd13e06a799" 431 | dependencies = [ 432 | "memchr", 433 | ] 434 | 435 | [[package]] 436 | name = "owo-colors" 437 | version = "1.3.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55" 440 | 441 | [[package]] 442 | name = "parking_lot" 443 | version = "0.11.2" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" 446 | dependencies = [ 447 | "instant", 448 | "lock_api", 449 | "parking_lot_core", 450 | ] 451 | 452 | [[package]] 453 | name = "parking_lot_core" 454 | version = "0.8.5" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" 457 | dependencies = [ 458 | "cfg-if", 459 | "instant", 460 | "libc", 461 | "redox_syscall", 462 | "smallvec", 463 | "winapi", 464 | ] 465 | 466 | [[package]] 467 | name = "pin-project-lite" 468 | version = "0.2.7" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" 471 | 472 | [[package]] 473 | name = "pin-utils" 474 | version = "0.1.0" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 477 | 478 | [[package]] 479 | name = "proc-macro-error" 480 | version = "1.0.4" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 483 | dependencies = [ 484 | "proc-macro-error-attr", 485 | "proc-macro2", 486 | "quote", 487 | "syn", 488 | "version_check", 489 | ] 490 | 491 | [[package]] 492 | name = "proc-macro-error-attr" 493 | version = "1.0.4" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 496 | dependencies = [ 497 | "proc-macro2", 498 | "quote", 499 | "version_check", 500 | ] 501 | 502 | [[package]] 503 | name = "proc-macro2" 504 | version = "1.0.32" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" 507 | dependencies = [ 508 | "unicode-xid", 509 | ] 510 | 511 | [[package]] 512 | name = "prometheus" 513 | version = "0.13.0" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "b7f64969ffd5dd8f39bd57a68ac53c163a095ed9d0fb707146da1b27025a3504" 516 | dependencies = [ 517 | "cfg-if", 518 | "fnv", 519 | "lazy_static", 520 | "memchr", 521 | "parking_lot", 522 | "protobuf", 523 | "thiserror", 524 | ] 525 | 526 | [[package]] 527 | name = "prometheus-hyper" 528 | version = "0.1.3" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "c2c8d04467323c4bc0ef47e86764e7e9525560be8347f98df248e6eab34794f2" 531 | dependencies = [ 532 | "hyper", 533 | "prometheus", 534 | "tokio", 535 | "tracing", 536 | ] 537 | 538 | [[package]] 539 | name = "protobuf" 540 | version = "2.25.2" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "47c327e191621a2158159df97cdbc2e7074bb4e940275e35abf38eb3d2595754" 543 | 544 | [[package]] 545 | name = "quote" 546 | version = "1.0.10" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 549 | dependencies = [ 550 | "proc-macro2", 551 | ] 552 | 553 | [[package]] 554 | name = "redox_syscall" 555 | version = "0.2.10" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 558 | dependencies = [ 559 | "bitflags", 560 | ] 561 | 562 | [[package]] 563 | name = "rustc-demangle" 564 | version = "0.1.21" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" 567 | 568 | [[package]] 569 | name = "scopeguard" 570 | version = "1.1.0" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 573 | 574 | [[package]] 575 | name = "serde" 576 | version = "1.0.136" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" 579 | dependencies = [ 580 | "serde_derive", 581 | ] 582 | 583 | [[package]] 584 | name = "serde_derive" 585 | version = "1.0.136" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" 588 | dependencies = [ 589 | "proc-macro2", 590 | "quote", 591 | "syn", 592 | ] 593 | 594 | [[package]] 595 | name = "sharded-slab" 596 | version = "0.1.4" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" 599 | dependencies = [ 600 | "lazy_static", 601 | ] 602 | 603 | [[package]] 604 | name = "signal-hook-registry" 605 | version = "1.4.0" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 608 | dependencies = [ 609 | "libc", 610 | ] 611 | 612 | [[package]] 613 | name = "smallvec" 614 | version = "1.7.0" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" 617 | 618 | [[package]] 619 | name = "socket2" 620 | version = "0.4.2" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" 623 | dependencies = [ 624 | "libc", 625 | "winapi", 626 | ] 627 | 628 | [[package]] 629 | name = "strsim" 630 | version = "0.10.0" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 633 | 634 | [[package]] 635 | name = "syn" 636 | version = "1.0.81" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" 639 | dependencies = [ 640 | "proc-macro2", 641 | "quote", 642 | "unicode-xid", 643 | ] 644 | 645 | [[package]] 646 | name = "termcolor" 647 | version = "1.1.2" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 650 | dependencies = [ 651 | "winapi-util", 652 | ] 653 | 654 | [[package]] 655 | name = "textwrap" 656 | version = "0.14.2" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" 659 | dependencies = [ 660 | "unicode-width", 661 | ] 662 | 663 | [[package]] 664 | name = "thiserror" 665 | version = "1.0.30" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 668 | dependencies = [ 669 | "thiserror-impl", 670 | ] 671 | 672 | [[package]] 673 | name = "thiserror-impl" 674 | version = "1.0.30" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 677 | dependencies = [ 678 | "proc-macro2", 679 | "quote", 680 | "syn", 681 | ] 682 | 683 | [[package]] 684 | name = "thread_local" 685 | version = "1.1.3" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" 688 | dependencies = [ 689 | "once_cell", 690 | ] 691 | 692 | [[package]] 693 | name = "time" 694 | version = "0.3.5" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" 697 | dependencies = [ 698 | "libc", 699 | ] 700 | 701 | [[package]] 702 | name = "tokio" 703 | version = "1.14.0" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144" 706 | dependencies = [ 707 | "autocfg", 708 | "bytes", 709 | "libc", 710 | "memchr", 711 | "mio", 712 | "num_cpus", 713 | "once_cell", 714 | "parking_lot", 715 | "pin-project-lite", 716 | "signal-hook-registry", 717 | "tokio-macros", 718 | "winapi", 719 | ] 720 | 721 | [[package]] 722 | name = "tokio-macros" 723 | version = "1.6.0" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e" 726 | dependencies = [ 727 | "proc-macro2", 728 | "quote", 729 | "syn", 730 | ] 731 | 732 | [[package]] 733 | name = "tower-service" 734 | version = "0.3.1" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 737 | 738 | [[package]] 739 | name = "tracing" 740 | version = "0.1.29" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" 743 | dependencies = [ 744 | "cfg-if", 745 | "pin-project-lite", 746 | "tracing-attributes", 747 | "tracing-core", 748 | ] 749 | 750 | [[package]] 751 | name = "tracing-attributes" 752 | version = "0.1.18" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" 755 | dependencies = [ 756 | "proc-macro2", 757 | "quote", 758 | "syn", 759 | ] 760 | 761 | [[package]] 762 | name = "tracing-core" 763 | version = "0.1.21" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" 766 | dependencies = [ 767 | "lazy_static", 768 | ] 769 | 770 | [[package]] 771 | name = "tracing-log" 772 | version = "0.1.2" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" 775 | dependencies = [ 776 | "lazy_static", 777 | "log", 778 | "tracing-core", 779 | ] 780 | 781 | [[package]] 782 | name = "tracing-subscriber" 783 | version = "0.3.1" 784 | source = "registry+https://github.com/rust-lang/crates.io-index" 785 | checksum = "80a4ddde70311d8da398062ecf6fc2c309337de6b0f77d6c27aff8d53f6fca52" 786 | dependencies = [ 787 | "ansi_term", 788 | "sharded-slab", 789 | "smallvec", 790 | "thread_local", 791 | "tracing-core", 792 | "tracing-log", 793 | ] 794 | 795 | [[package]] 796 | name = "try-lock" 797 | version = "0.2.3" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 800 | 801 | [[package]] 802 | name = "unicase" 803 | version = "2.6.0" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 806 | dependencies = [ 807 | "version_check", 808 | ] 809 | 810 | [[package]] 811 | name = "unicode-segmentation" 812 | version = "1.8.0" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" 815 | 816 | [[package]] 817 | name = "unicode-width" 818 | version = "0.1.9" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 821 | 822 | [[package]] 823 | name = "unicode-xid" 824 | version = "0.2.2" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 827 | 828 | [[package]] 829 | name = "version_check" 830 | version = "0.9.3" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 833 | 834 | [[package]] 835 | name = "want" 836 | version = "0.3.0" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 839 | dependencies = [ 840 | "log", 841 | "try-lock", 842 | ] 843 | 844 | [[package]] 845 | name = "winapi" 846 | version = "0.3.9" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 849 | dependencies = [ 850 | "winapi-i686-pc-windows-gnu", 851 | "winapi-x86_64-pc-windows-gnu", 852 | ] 853 | 854 | [[package]] 855 | name = "winapi-i686-pc-windows-gnu" 856 | version = "0.4.0" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 859 | 860 | [[package]] 861 | name = "winapi-util" 862 | version = "0.1.5" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 865 | dependencies = [ 866 | "winapi", 867 | ] 868 | 869 | [[package]] 870 | name = "winapi-x86_64-pc-windows-gnu" 871 | version = "0.4.0" 872 | source = "registry+https://github.com/rust-lang/crates.io-index" 873 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 874 | 875 | [[package]] 876 | name = "wireguard_exporter" 877 | version = "0.3.0" 878 | dependencies = [ 879 | "base64", 880 | "clap", 881 | "color-eyre", 882 | "maxminddb", 883 | "prometheus", 884 | "prometheus-hyper", 885 | "time", 886 | "tokio", 887 | "tracing", 888 | "tracing-subscriber", 889 | ] 890 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wireguard_exporter" 3 | version = "0.3.0" 4 | edition = "2021" 5 | authors = [ 6 | "Kevin K. ", 7 | ] 8 | description = "A Prometheus exporter for WireGuard" 9 | repository = "https://github.com/kbknapp/wireguard_exporter" 10 | keywords = [ 11 | "prometheus", 12 | "wireguard", 13 | "exporter", 14 | "prometheus_exporter", 15 | ] 16 | categories = ["command-line-utilities"] 17 | license = "MIT OR Apache-2.0" 18 | readme = "README.md" 19 | 20 | [badges] 21 | is-it-maintained-issue-resolution = { repository = "kbknapp/wireguard_exporter" } 22 | is-it-maintained-open-issues = { repository = "kbknapp/wireguard_exporter" } 23 | maintenance = {status = "actively-developed"} 24 | 25 | [dependencies] 26 | color-eyre = { version = "0.5.11", default-features = false } 27 | clap = "3.0.0-beta.5" 28 | prometheus = "0.13.0" 29 | prometheus-hyper = "0.1.3" 30 | tokio = { version = "1.5.0", features = ["full"] } 31 | tracing = "0.1.25" 32 | tracing-subscriber = "0.3.1" 33 | base64 = "0.13.0" 34 | time = { version = "0.3.5", features = ["local-offset"] } 35 | maxminddb = "0.24.0" 36 | 37 | [build-dependencies] 38 | clap = "3.0.0-beta.5" 39 | 40 | [features] 41 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Kevin B. Knapp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `wireguard_exporter` 2 | 3 | ![Rust Version][rustc-image] 4 | [![crates.io][crate-image]][crate-link] 5 | [![Dependency Status][deps-image]][deps-link] 6 | 7 | An asynchronous Prometheus exporter for `wireguard` 8 | 9 | `wireguard_exporter` runs `wg show [..]` and scrapes the output to 10 | build Prometheus metrics. Because `wg` requires `root` privileges, 11 | this tool must be run as `root` (or via `sudo`) or with the following 12 | capabilities in both the ambient and bounding set: 13 | 14 | - CAP_DAC_READ_SEARCH 15 | - CAP_NET_ADMIN 16 | - CAP_NET_RAW 17 | 18 | # Metrics Provided 19 | 20 | - Total number of bytes transferred in/out per peer 21 | - Total number of bytes transferred in/out per interface 22 | - Metrics with the info for each connected peer 23 | - Time since last handshake per peer 24 | - Scrape duration in milliseconds 25 | - Scrape success 26 | 27 | # Installation 28 | 29 | `wireguard_exporter` is a single binary that must be placed somewhere in your 30 | `$PATH`. One can either download 64-bit Linux binaries from [the Release Page](https://github.com/kbknapp/wireguard_exporter/releases) 31 | or one can also compile from source. 32 | 33 | ## Compile from Source 34 | 35 | Ensure you have a [Rust toolchain installed](https://rustup.rs). Some of the 36 | dependencies also require `gcc` to be installed. 37 | 38 | ``` 39 | $ git clone https://github.com/kbknapp/wireguard_exporter 40 | $ cd wireguard_exporter 41 | $ cargo build --release 42 | $ sudo cp target/release/wireguard_exporter /usr/local/bin/ 43 | ``` 44 | 45 | # Usage 46 | 47 | ## Command Line Interface 48 | 49 | ``` 50 | USAGE: 51 | wireguard_exporter [OPTIONS] 52 | 53 | OPTIONS: 54 | -a, --alias ... Add an alias for a given public key in the form of 55 | 'pubkey:alias' (separate multiple with commas) 56 | --collect-interval How often metrics are gathered [default: 5] 57 | -h, --help Print help information 58 | -l, --listen-address The listen address scraping metrics [default: 0.0.0.0] 59 | -p, --listen-port The listen port for scraping metrics [default: 9586] 60 | -q, --quiet Supress output at a level or lower. -q: INFO, -qq: WARN, -qqq: 61 | ERROR (i.e. everything) 62 | -v, --verbose Show verbose output at a level or higher. -v: DEBUG, -vv: 63 | TRACE 64 | -V, --version Print version information 65 | ``` 66 | 67 | To run with the default options, and the binary is installed somewhere in your 68 | `$PATH`: 69 | 70 | ``` 71 | $ sudo wireguard_exporter 72 | ``` 73 | 74 | # Prometheus Configuration 75 | 76 | You can add the following scrape configs to Prometheus: 77 | 78 | ```yaml 79 | scrape_configs: 80 | - job_name: 'wireguard' 81 | static_configs: 82 | - targets: 83 | - 'localhost:9586' 84 | - 'other_host:9586' 85 | 86 | relabel_configs: 87 | - source_labels: [ '__address__' ] 88 | regex: '(.*):\d+' 89 | target_label: instance 90 | ``` 91 | 92 | # Example Metrics 93 | 94 | ``` 95 | # HELP wireguard_bytes_total Total number of bytes per direction per interface 96 | # TYPE wireguard_bytes_total counter 97 | wireguard_bytes_total{direction="rx",interface="custom"} 19576636452 98 | wireguard_bytes_total{direction="rx",interface="wg0"} 1091996152 99 | wireguard_bytes_total{direction="tx",interface="custom"} 3919310388 100 | wireguard_bytes_total{direction="tx",interface="wg0"} 2393043528 101 | # HELP wireguard_duration_since_latest_handshake During since latest handshake for a peer 102 | # TYPE wireguard_duration_since_latest_handshake gauge 103 | wireguard_duration_since_latest_handshake{interface="custom",peer="q2JWEKWfLPU5UjG2Sq31xx2GsSjdhKNtdT/X/tFVyjs=",alias="kevin"} 51405 104 | wireguard_duration_since_latest_handshake{interface="custom",peer="2ELWFmGnqhtRpu4r2PUKc0cw+ELtuMPLd6l0KsoCUBQ=",alias="jane"} 88405 105 | wireguard_duration_since_latest_handshake{interface="custom",peer="duVVziZbyIiIPoRprisE69K0By198Cn8dPwY5bFecEk=",alias="robert"} 116405 106 | wireguard_duration_since_latest_handshake{interface="custom",peer="nwj+Zw49AbYrzUAPzeRf8hhll/1dz8SjoOYZuB+JdT4="} 15296341405 107 | wireguard_duration_since_latest_handshake{interface="custom",peer="QF01u5CZhH9+CWcVY9pbsuTu3QsTcSqFvni3VfOiL2s="} 34405 108 | wireguard_duration_since_latest_handshake{interface="custom",peer="N5UQp3XbysLBAavUm1Cpv7xxjk99LwJD99z5//PsyCc="} 95405 109 | wireguard_duration_since_latest_handshake{interface="custom",peer="QlgHHfYP3aMlRG7d6/Zp9IhUOLrpT5G2GIdODODaUHQ="} 10690033405 110 | wireguard_duration_since_latest_handshake{interface="custom",peer="FtUeMGdNxgkVN0G9lpvOc5jtAQQ1m9DpvZPDCUdKBx0="} 96405 111 | wireguard_duration_since_latest_handshake{interface="wg0",peer="bRQZOyOZUvHMhBvCWq2sXO0VsRu6Aq5LCACi/R3AJk8="} 42405 112 | # HELP wireguard_interfaces_total Total number of interfaces 113 | # TYPE wireguard_interfaces_total gauge 114 | wireguard_interfaces_total 2 115 | # HELP wireguard_peer_bytes_total Total number of bytes per direction for a peer 116 | # TYPE wireguard_peer_bytes_total counter 117 | wireguard_peer_bytes_total{direction="rx",interface="custom",peer=q2JWEKWfLPU5UjG2Sq31xx2GsSjdhKNtdT/X/tFVyjs="",alias="kevin"} 0 118 | wireguard_peer_bytes_total{direction="rx",interface="custom",peer="2ELWFmGnqhtRpu4r2PUKc0cw+ELtuMPLd6l0KsoCUBQ=",alias="jane"} 1240506784 119 | wireguard_peer_bytes_total{direction="rx",interface="custom",peer="duVVziZbyIiIPoRprisE69K0By198Cn8dPwY5bFecEk=",alias="robert"} 1312403276 120 | wireguard_peer_bytes_total{direction="rx",interface="custom",peer="nwj+Zw49AbYrzUAPzeRf8hhll/1dz8SjoOYZuB+JdT4="} 11962543712 121 | wireguard_peer_bytes_total{direction="rx",interface="custom",peer="QF01u5CZhH9+CWcVY9pbsuTu3QsTcSqFvni3VfOiL2s="} 0 122 | wireguard_peer_bytes_total{direction="rx",interface="custom",peer="N5UQp3XbysLBAavUm1Cpv7xxjk99LwJD99z5//PsyCc="} 0 123 | wireguard_peer_bytes_total{direction="rx",interface="custom",peer="QlgHHfYP3aMlRG7d6/Zp9IhUOLrpT5G2GIdODODaUHQ="} 353261276 124 | wireguard_peer_bytes_total{direction="rx",interface="custom",peer="FtUeMGdNxgkVN0G9lpvOc5jtAQQ1m9DpvZPDCUdKBx0="} 2150081456 125 | wireguard_peer_bytes_total{direction="rx",interface="wg0",peer=""} 1091996152 126 | wireguard_peer_bytes_total{direction="tx",interface="custom",peer="q2JWEKWfLPU5UjG2Sq31xx2GsSjdhKNtdT/X/tFVyjs=",alias="kevin"} 0 127 | wireguard_peer_bytes_total{direction="tx",interface="custom",peer="2ELWFmGnqhtRpu4r2PUKc0cw+ELtuMPLd6l0KsoCUBQ=",alias="jane"} 708900060 128 | wireguard_peer_bytes_total{direction="tx",interface="custom",peer="duVVziZbyIiIPoRprisE69K0By198Cn8dPwY5bFecEk=",alias="robert"} 714718444 129 | wireguard_peer_bytes_total{direction="tx",interface="custom",peer="nwj+Zw49AbYrzUAPzeRf8hhll/1dz8SjoOYZuB+JdT4="} 1171658320 130 | wireguard_peer_bytes_total{direction="tx",interface="custom",peer="QF01u5CZhH9+CWcVY9pbsuTu3QsTcSqFvni3VfOiL2s="} 0 131 | wireguard_peer_bytes_total{direction="tx",interface="custom",peer="N5UQp3XbysLBAavUm1Cpv7xxjk99LwJD99z5//PsyCc="} 0 132 | wireguard_peer_bytes_total{direction="tx",interface="custom",peer="QlgHHfYP3aMlRG7d6/Zp9IhUOLrpT5G2GIdODODaUHQ="} 88648 133 | wireguard_peer_bytes_total{direction="tx",interface="custom",peer="FtUeMGdNxgkVN0G9lpvOc5jtAQQ1m9DpvZPDCUdKBx0="} 480852300 134 | wireguard_peer_bytes_total{direction="tx",interface="wg0",peer="bRQZOyOZUvHMhBvCWq2sXO0VsRu6Aq5LCACi/R3AJk8="} 2393043528 135 | # HELP wireguard_peer_endpoint Peers info. static value 136 | # TYPE wireguard_peer_endpoint gauge 137 | wireguard_peer_endpoint{alias="kevin",endpoint_ip="1.1.1.1",interface="custom",peer="q2JWEKWfLPU5UjG2Sq31xx2GsSjdhKNtdT/X/tFVyjs="} 1 138 | wireguard_peer_endpoint{alias="jane",endpoint_ip="8.8.8.8",interface="custom",peer="2ELWFmGnqhtRpu4r2PUKc0cw+ELtuMPLd6l0KsoCUBQ="} 1 139 | wireguard_peer_endpoint{alias="robert",endpoint_ip="127.0.0.1",interface="custom",peer="duVVziZbyIiIPoRprisE69K0By198Cn8dPwY5bFecEk="} 1 140 | # HELP wireguard_peers_total Total number of peers per interfaces 141 | # TYPE wireguard_peers_total gauge 142 | wireguard_peers_total{interface="custom"} 7 143 | wireguard_peers_total{interface="wg0"} 1 144 | # HELP wireguard_scrape_duration_milliseconds Duration in milliseconds of the scrape 145 | # TYPE wireguard_scrape_duration_milliseconds gauge 146 | wireguard_scrape_duration_milliseconds 1 147 | # HELP wireguard_scrape_success If the scrape was a success 148 | # TYPE wireguard_scrape_success gauge 149 | wireguard_scrape_success 1 150 | ``` 151 | 152 | # License 153 | 154 | This crate is licensed under either of 155 | 156 | * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) 157 | * [MIT license](http://opensource.org/licenses/MIT) 158 | 159 | at your option. 160 | 161 | ## Contribution 162 | 163 | Unless you explicitly state otherwise, any contribution intentionally submitted 164 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 165 | dual licensed as above, without any additional terms or conditions. 166 | 167 | [//]: # (badges) 168 | 169 | [rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg 170 | [crate-image]: https://img.shields.io/crates/v/wireguard_exporter.svg 171 | [crate-link]: https://crates.io/crates/wireguard_exporter 172 | [deps-image]: https://deps.rs/repo/github/kbknapp/wireguard_exporter/status.svg 173 | [deps-link]: https://deps.rs/repo/github/kbknapp/wireguard_exporter 174 | 175 | 176 | [//]: # (Links) 177 | 178 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | use clap::crate_version; 4 | 5 | fn main() { 6 | let output = Command::new("git") 7 | .args(&["rev-parse", "HEAD"]) 8 | .output() 9 | .unwrap(); 10 | let git_hash = String::from_utf8(output.stdout).unwrap(); 11 | println!( 12 | "cargo:rustc-env=VERSION_WITH_GIT_HASH='v{} ({})'", 13 | crate_version!(), 14 | &git_hash[..10] 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, env, net::IpAddr, path::PathBuf, str::FromStr}; 2 | 3 | use clap::{crate_authors, Parser}; 4 | 5 | static VERSION: &str = env!("VERSION_WITH_GIT_HASH"); 6 | static AUTHORS: &str = crate_authors!(); 7 | 8 | /// A Prometheus exporter for WireGuard 9 | #[derive(Parser)] 10 | #[clap(author = AUTHORS, version = VERSION)] 11 | pub struct Args { 12 | /// How often metrics are gathered 13 | #[clap(long, default_value = "5", value_name = "SECS")] 14 | pub collect_interval: u64, 15 | /// The listen port for scraping metrics 16 | #[clap(short = 'p', long, default_value = "9586", value_name = "PORT")] 17 | pub listen_port: u16, 18 | /// The listen address scraping metrics 19 | #[clap(short, long, default_value = "0.0.0.0", value_name = "ADDR")] 20 | pub listen_address: IpAddr, 21 | /// Show verbose output at a level or higher. -v: DEBUG, -vv: TRACE 22 | #[clap(long, short, parse(from_occurrences))] 23 | pub verbose: u8, 24 | /// Supress output at a level or lower. -q: INFO, -qq: WARN, -qqq: ERROR (i.e. everything) 25 | #[clap(long, short, overrides_with = "verbose", parse(from_occurrences))] 26 | pub quiet: u8, 27 | /// Add an alias for a given public key in the form of 'pubkey:alias' (separate multiple with commas) 28 | #[clap(long, short, value_delimiter = ',', multiple_occurrences = true)] 29 | pub alias: Vec, 30 | /// Do geoip lookup using Country MMDB from the PATH for 'endpoint_ip' attribute in the 'wireguard_peer_endpoint' metric and add attribute 'endpoint_country' 31 | #[clap(short, long, value_name = "PATH")] 32 | pub geoip_path: Option, 33 | } 34 | 35 | impl Args { 36 | pub fn aliases(&self) -> HashMap<&str, &str> { 37 | let mut map = HashMap::new(); 38 | for alias in &self.alias { 39 | let Alias { 40 | inner: (pubkey, alias), 41 | } = alias; 42 | map.insert(pubkey.as_ref(), alias.as_ref()); 43 | } 44 | 45 | map 46 | } 47 | } 48 | 49 | #[derive(Clone, Debug)] 50 | pub struct Alias { 51 | // A base64 encoded public key and a human readable alias 52 | // (pubkey, alias) 53 | pub inner: (String, String), 54 | } 55 | 56 | impl FromStr for Alias { 57 | type Err = String; 58 | fn from_str(s: &str) -> Result { 59 | let mut parts = s.split(':'); 60 | let pubkey = parts.next(); 61 | let alias = parts.next(); 62 | 63 | match (pubkey, alias) { 64 | (Some(pubkey), None) => Err(format!( 65 | "must be in the format 'PUBKEY:ALIAS' but found '{}'", 66 | pubkey 67 | )), 68 | (None, _) => unreachable!(), 69 | (Some(pubkey), Some(alias)) => { 70 | if pubkey.is_empty() || alias.is_empty() { 71 | return Err(format!( 72 | "\t\nMust be in the format 'PUBKEY:ALIAS' but found '{}:{}'", 73 | pubkey, alias 74 | )); 75 | } 76 | 77 | if pubkey.len() != 44 { 78 | return Err(format!("\t\nPUBKEY '{}' has an invalid length", pubkey,)); 79 | } 80 | 81 | if base64::decode(pubkey).is_err() { 82 | return Err(format!("\n\t'{}' is not a valid public key", pubkey,)); 83 | } 84 | 85 | Ok(Alias { 86 | inner: (pubkey.into(), alias.into()), 87 | }) 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! unwrap_or_exit { 2 | ($e:expr) => {{ 3 | use std::process; 4 | use tracing::error; 5 | ($e).map_err(|e| { 6 | error!("{}", e); 7 | eprintln!("error: {}", e); 8 | process::exit(1); 9 | }) 10 | .unwrap() 11 | }}; 12 | } 13 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, 3 | net::SocketAddr, 4 | sync::{ 5 | atomic::{AtomicBool, Ordering}, 6 | Arc, 7 | }, 8 | }; 9 | 10 | use clap::Parser; 11 | use color_eyre::{ 12 | config::{HookBuilder, Theme}, 13 | eyre::Result, 14 | }; 15 | use maxminddb; 16 | use prometheus::{IntGauge, Registry}; 17 | use prometheus_hyper::Server; 18 | use tokio::time::{Duration, Instant}; 19 | use tracing::{debug, info}; 20 | 21 | #[macro_use] 22 | mod macros; 23 | mod cli; 24 | mod metrics; 25 | mod wireguard; 26 | 27 | use cli::Args; 28 | use metrics::Metrics; 29 | use wireguard::WireguardState; 30 | 31 | #[tokio::main(flavor = "current_thread")] 32 | async fn main() -> Result<()> { 33 | let args = cli::Args::parse(); 34 | 35 | HookBuilder::blank() 36 | .theme(Theme::new()) 37 | .panic_section( 38 | "consider reporting the bug at https://github.com/kbknapp/wireguard_exporter", 39 | ) 40 | .install()?; 41 | 42 | match args.verbose { 43 | 0 => match args.quiet { 44 | 0 => env::set_var("RUST_LOG", "wireguard_exporter=info"), 45 | 1 => env::set_var("RUST_LOG", "wireguard_exporter=warn"), 46 | 2 => env::set_var("RUST_LOG", "wireguard_exporter=error"), 47 | _ => env::set_var("RUST_LOG", "wireguard_exporter=off"), 48 | }, 49 | 1 => env::set_var("RUST_LOG", "wireguard_exporter=debug"), 50 | _ => env::set_var("RUST_LOG", "wireguard_exporter=trace"), 51 | } 52 | 53 | try_main(args).await 54 | } 55 | 56 | async fn try_main(args: Args) -> Result<()> { 57 | tracing_subscriber::fmt::init(); 58 | 59 | let aliases = args.aliases(); 60 | 61 | let maxminddb_reader = args.geoip_path.as_ref().map_or(None, |path| { 62 | Some(unwrap_or_exit!(maxminddb::Reader::open_readfile(path))) 63 | }); 64 | 65 | let running = Arc::new(AtomicBool::new(true)); 66 | 67 | info!("Registering metrics..."); 68 | let registry = Arc::new(Registry::new()); 69 | let mut metrics = unwrap_or_exit!(Metrics::new(®istry, maxminddb_reader)); 70 | let scrape_duration = unwrap_or_exit!(IntGauge::new( 71 | "wireguard_scrape_duration_milliseconds", 72 | "Duration in milliseconds of the scrape", 73 | )); 74 | 75 | let scrape_success = unwrap_or_exit!(IntGauge::new( 76 | "wireguard_scrape_success", 77 | "If the scrape was a success" 78 | )); 79 | debug!("Registering scrape metrics..."); 80 | unwrap_or_exit!(registry.register(Box::new(scrape_duration.clone()))); 81 | unwrap_or_exit!(registry.register(Box::new(scrape_success.clone()))); 82 | 83 | info!("Spawning server..."); 84 | tokio::spawn(Server::run( 85 | Arc::clone(®istry), 86 | SocketAddr::new(args.listen_address, args.listen_port), 87 | shutdown_signal(Arc::clone(&running)), 88 | )); 89 | 90 | let mut collect_int = tokio::time::interval(Duration::from_secs(args.collect_interval)); 91 | while running.load(Ordering::Relaxed) { 92 | info!("Collecting metrics..."); 93 | let before = Instant::now(); 94 | 95 | debug!("Updating metrics..."); 96 | metrics 97 | .update(&WireguardState::scrape(&aliases).await?) 98 | .await; 99 | let after = Instant::now(); 100 | 101 | let elapsed = after.duration_since(before); 102 | scrape_duration.set((elapsed.as_secs() * 1000 + elapsed.subsec_millis() as u64) as i64); 103 | scrape_success.set(1); 104 | 105 | debug!("Sleeping..."); 106 | collect_int.tick().await; 107 | } 108 | info!("Stopped the exporter"); 109 | 110 | Ok(()) 111 | } 112 | 113 | async fn shutdown_signal(running: Arc) { 114 | // Wait for the CTRL+C Signal 115 | tokio::signal::ctrl_c() 116 | .await 117 | .expect("Failed to install CTRL+C signal handler"); 118 | running.store(false, Ordering::Relaxed); 119 | } 120 | -------------------------------------------------------------------------------- /src/metrics.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::eyre::Result; 2 | use maxminddb::geoip2; 3 | use prometheus::{IntCounterVec, IntGaugeVec, Opts, Registry}; 4 | use time::OffsetDateTime; 5 | use tracing::{debug, trace}; 6 | 7 | use crate::wireguard::WireguardState; 8 | 9 | pub struct Metrics { 10 | interfaces_total: IntGaugeVec, 11 | peers_total: IntGaugeVec, 12 | peer_endpoint: IntGaugeVec, 13 | bytes_total: IntCounterVec, 14 | peer_bytes_total: IntCounterVec, 15 | duration_since_latest_handshake: IntGaugeVec, 16 | maxminddb_reader: Option>>, 17 | } 18 | 19 | impl Metrics { 20 | pub fn new(r: &Registry, maxminddb_reader: Option>>) -> Result { 21 | trace!("Metrics::new"); 22 | 23 | let interfaces_total = IntGaugeVec::new( 24 | Opts::new("wireguard_interfaces_total", "Total number of interfaces"), 25 | &[], 26 | )?; 27 | 28 | let peers_total = IntGaugeVec::new( 29 | Opts::new( 30 | "wireguard_peers_total", 31 | "Total number of peers per interfaces", 32 | ), 33 | &["interface"], 34 | )?; 35 | 36 | let peer_endpoint = IntGaugeVec::new( 37 | Opts::new("wireguard_peer_endpoint", "Peers info. static value"), 38 | match maxminddb_reader { 39 | Some(_) => &[ 40 | "interface", 41 | "endpoint_ip", 42 | "endpoint_country", 43 | "peer", 44 | "alias", 45 | ], 46 | None => &["interface", "endpoint_ip", "peer", "alias"], 47 | }, 48 | )?; 49 | 50 | let bytes_total = IntCounterVec::new( 51 | Opts::new( 52 | "wireguard_bytes_total", 53 | "Total number of bytes per direction per interface", 54 | ), 55 | &["interface", "direction"], 56 | )?; 57 | 58 | let peer_bytes_total = IntCounterVec::new( 59 | Opts::new( 60 | "wireguard_peer_bytes_total", 61 | "Total number of bytes per direction for a peer", 62 | ), 63 | &["interface", "peer", "direction", "alias"], 64 | )?; 65 | 66 | let duration_since_latest_handshake = IntGaugeVec::new( 67 | Opts::new( 68 | "wireguard_duration_since_latest_handshake", 69 | "Duration in milliseconds since latest handshake for a peer", 70 | ), 71 | &["interface", "peer", "alias"], 72 | )?; 73 | 74 | debug!("Registering wireguard metrics"); 75 | r.register(Box::new(interfaces_total.clone()))?; 76 | r.register(Box::new(peers_total.clone()))?; 77 | r.register(Box::new(peer_endpoint.clone()))?; 78 | r.register(Box::new(bytes_total.clone()))?; 79 | r.register(Box::new(peer_bytes_total.clone()))?; 80 | r.register(Box::new(duration_since_latest_handshake.clone()))?; 81 | 82 | Ok(Self { 83 | interfaces_total, 84 | bytes_total, 85 | peers_total, 86 | peer_endpoint, 87 | peer_bytes_total, 88 | duration_since_latest_handshake, 89 | maxminddb_reader, 90 | }) 91 | } 92 | 93 | pub async fn update(&mut self, state: &WireguardState) { 94 | let it = self.interfaces_total.with_label_values(&[]); 95 | it.set(state.interfaces.len() as i64); 96 | 97 | for (i, iface) in state.interfaces.iter().enumerate() { 98 | let pt = self.peers_total.with_label_values(&[iface]); 99 | pt.set( 100 | state 101 | .peers 102 | .iter() 103 | .filter(|peer| peer.interface == i) 104 | .count() as i64, 105 | ); 106 | 107 | let btt = self.bytes_total.with_label_values(&[iface, "tx"]); 108 | let diff = state 109 | .peers 110 | .iter() 111 | .filter(|peer| peer.interface == i) 112 | .map(|peer| peer.tx_bytes) 113 | .sum::() 114 | - btt.get(); 115 | btt.inc_by(diff); 116 | 117 | let btr = self.bytes_total.with_label_values(&[iface, "rx"]); 118 | let diff = state 119 | .peers 120 | .iter() 121 | .filter(|peer| peer.interface == i) 122 | .map(|peer| peer.rx_bytes) 123 | .sum::() 124 | - btr.get(); 125 | btr.inc_by(diff); 126 | } 127 | 128 | self.peer_endpoint.reset(); 129 | 130 | for p in &state.peers { 131 | assert!(p.interface < state.interfaces.len()); 132 | 133 | if let Some(endpoint) = p.endpoint { 134 | match &self.maxminddb_reader { 135 | Some(reader) => { 136 | let endpoint_country = match reader.lookup::(endpoint.ip()) 137 | { 138 | Ok(reader_response) => { 139 | reader_response.country.map_or_else(String::new, |c| { 140 | c.iso_code 141 | .map(|code| code.to_string()) 142 | .unwrap_or_else(String::new) 143 | }) 144 | } 145 | _ => String::new(), 146 | }; 147 | 148 | self.peer_endpoint.with_label_values(&[ 149 | &state.interfaces[p.interface], 150 | &endpoint.ip().to_string(), 151 | &endpoint_country.to_string(), 152 | &p.pubkey, 153 | &p.alias 154 | .as_ref() 155 | .map(ToOwned::to_owned) 156 | .unwrap_or_else(String::new), 157 | ]) 158 | } 159 | None => self.peer_endpoint.with_label_values(&[ 160 | &state.interfaces[p.interface], 161 | &endpoint.ip().to_string(), 162 | &p.pubkey, 163 | &p.alias 164 | .as_ref() 165 | .map(ToOwned::to_owned) 166 | .unwrap_or_else(String::new), 167 | ]), 168 | } 169 | .set(1); 170 | }; 171 | 172 | let pbtt = self.peer_bytes_total.with_label_values(&[ 173 | &state.interfaces[p.interface], 174 | &p.pubkey, 175 | "tx", 176 | &p.alias 177 | .as_ref() 178 | .map(ToOwned::to_owned) 179 | .unwrap_or_else(String::new), 180 | ]); 181 | let diff = p.tx_bytes - pbtt.get(); 182 | pbtt.inc_by(diff); 183 | 184 | let pbtr = self.peer_bytes_total.with_label_values(&[ 185 | &state.interfaces[p.interface], 186 | &p.pubkey, 187 | "rx", 188 | &p.alias 189 | .as_ref() 190 | .map(ToOwned::to_owned) 191 | .unwrap_or_else(String::new), 192 | ]); 193 | let diff = p.rx_bytes - pbtr.get(); 194 | pbtr.inc_by(diff); 195 | 196 | if let Some(latest_hs_ts) = p.handshake_timestamp { 197 | let now = OffsetDateTime::now_local() 198 | .expect("Failed to get local offset time") 199 | .unix_timestamp(); 200 | 201 | let elapsed = now - latest_hs_ts as i64; 202 | 203 | self.duration_since_latest_handshake 204 | .with_label_values(&[ 205 | &state.interfaces[p.interface], 206 | &p.pubkey, 207 | &p.alias 208 | .as_ref() 209 | .map(ToOwned::to_owned) 210 | .unwrap_or_else(String::new), 211 | ]) 212 | .set(elapsed * 1000); 213 | } 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/wireguard.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::eyre::{Result, WrapErr}; 2 | use std::{collections::HashMap, net::SocketAddr, str::FromStr}; 3 | use tokio::process::Command; 4 | 5 | pub async fn wg_show_dump() -> Result { 6 | String::from_utf8( 7 | Command::new("wg") 8 | .args(&["show", "all", "dump"]) 9 | .output() 10 | .await 11 | .wrap_err("Failed to run 'wg show'")? 12 | .stdout, 13 | ) 14 | .wrap_err("Failed wg show output to valid UTF-8") 15 | } 16 | 17 | #[derive(Debug)] 18 | pub struct WireguardState { 19 | pub interfaces: Vec, 20 | pub peers: Vec, 21 | } 22 | 23 | impl WireguardState { 24 | pub async fn scrape(aliases: &HashMap<&str, &str>) -> Result { 25 | let mut peers = Vec::with_capacity(32); // Picked by fair dice roll 26 | let mut interfaces = Vec::new(); 27 | 28 | for line in wg_show_dump().await?.lines() { 29 | // Peer lines: 30 | // INT PUBKEY PSK ENDPOINT ALLOWED-IPS HANDSHAKE TX RX KA 31 | // 32 | // Interface Lines: 33 | // INT PRIVKEY PUBKEY PORT FWMARK 34 | let mut segs = line.split('\t'); 35 | let f1 = segs.next(); 36 | let f2 = segs.next(); 37 | let f3 = segs.next(); 38 | let f4 = segs.next(); 39 | let f5 = segs.next(); 40 | let f6 = segs.next(); 41 | let f7 = segs.next(); 42 | let f8 = segs.next(); 43 | let f9 = segs.next(); 44 | 45 | match (f1, f2, f3, f4, f5, f6, f7, f8, f9) { 46 | ( 47 | Some(_iface), 48 | Some(pubkey), 49 | Some(_psk), 50 | Some(endpoint), 51 | Some(_allowed_ips), 52 | Some(handshake_ts), 53 | Some(tx_bytes), 54 | Some(rx_bytes), 55 | Some(_keepalives), 56 | ) => { 57 | let ts = handshake_ts.parse()?; 58 | peers.push(Peer { 59 | interface: interfaces.len() - 1, 60 | alias: aliases.get(pubkey).map(|&s| s.into()), 61 | pubkey: pubkey.into(), 62 | endpoint: SocketAddr::from_str(endpoint).ok(), 63 | handshake_timestamp: if ts == 0 { None } else { Some(ts) }, 64 | tx_bytes: tx_bytes.parse()?, 65 | rx_bytes: rx_bytes.parse()?, 66 | }); 67 | } 68 | ( 69 | Some(iface), 70 | Some(_privkey), 71 | Some(_pubkey), 72 | Some(_port), 73 | Some(_fwmark), 74 | None, 75 | None, 76 | None, 77 | None, 78 | ) => interfaces.push(iface.into()), 79 | _ => todo!("return Err on invalid line from parse_line"), 80 | } 81 | } 82 | 83 | Ok(Self { interfaces, peers }) 84 | } 85 | } 86 | 87 | #[derive(Clone, PartialEq, Debug, Default)] 88 | pub struct Peer { 89 | pub pubkey: String, 90 | pub endpoint: Option, 91 | pub alias: Option, 92 | pub interface: usize, 93 | pub tx_bytes: u64, 94 | pub rx_bytes: u64, 95 | pub handshake_timestamp: Option, 96 | } 97 | --------------------------------------------------------------------------------