{"id":18016136,"url":"https://github.com/cpu/promguard","last_synced_at":"2025-03-26T18:31:42.766Z","repository":{"id":89702897,"uuid":"119312075","full_name":"cpu/PromGuard","owner":"cpu","description":"Prometheus scraping over WireGuard: Example Terraform/Ansible for fully authenticated/encrypted stat scraping","archived":true,"fork":false,"pushed_at":"2018-05-02T07:28:43.000Z","size":148,"stargazers_count":27,"open_issues_count":0,"forks_count":4,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-02-15T22:24:17.182Z","etag":null,"topics":["ansible","metrics","prometheus","site-to-site","terraform","vpn","wireguard"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cpu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-29T00:39:37.000Z","updated_at":"2024-02-14T16:15:03.000Z","dependencies_parsed_at":null,"dependency_job_id":"861e3be1-99fa-4382-863f-89619f0fc3a7","html_url":"https://github.com/cpu/PromGuard","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cpu%2FPromGuard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cpu%2FPromGuard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cpu%2FPromGuard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cpu%2FPromGuard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cpu","download_url":"https://codeload.github.com/cpu/PromGuard/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245712809,"owners_count":20660300,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ansible","metrics","prometheus","site-to-site","terraform","vpn","wireguard"],"created_at":"2024-10-30T04:16:16.070Z","updated_at":"2025-03-26T18:31:42.750Z","avatar_url":"https://github.com/cpu.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PromGuard - Authenticated/Encrypted Prometheus stat scraping over WireGuard\n\n1. [Summary](https://github.com/cpu/PromGuard#summary)\n1. [Prerequisites](https://github.com/cpu/PromGuard#prerequisites)\n1. [Initial Setup](https://github.com/cpu/PromGuard#initial-setup)\n1. [Usage](https://github.com/cpu/PromGuard#usage)\n1. [Background](https://github.com/cpu/PromGuard#background)\n1. [Implementation](https://github.com/cpu/PromGuard#implementation)\n1. [Conclusion](https://github.com/cpu/PromGuard#conclusion)\n1. [Example run](https://github.com/cpu/PromGuard#example-run)\n\n## Summary\n\n[Prometheus](https://prometheus.io/) doesn't support authentication/encryption\nout of box. Scraping metrics over the capital I internet without is a no-go.\nPutting mutually authenticated TLS in front is a hassle.\n\n[WireGuard](http://wireguard.com/) is a next-generation VPN technology likely to\nbe part of the mainline Linux kernel Soon(TM). It is: simple, fast, effective.\n\nCan we configure Prometheus to scrape stats over WireGuard? Of course. This is\na repository showing an example of this approach using\n[Terraform](https://www.terraform.io/) and [Ansible](http://ansible.com/) so\nyou can easily try it yourself with as little as _one command*_.\n\n`*` - _Not counting installing Terraform \u0026 Ansible, and configuring\na DigitalOcean API token! Some limitations apply, batteries not included, offer\nnot valid in Quebec._\n\n## Demo Prerequisites\n\n1. [Install Ansible](http://docs.ansible.com/ansible/latest/intro_installation.html)\n1. [Install Terraform](https://www.terraform.io/intro/getting-started/install.html)\n1. [Get a DigitalOcean API key](https://cloud.digitalocean.com/settings/api/tokens)\n1. Clone this repo and `cd` into it.\n\n## Initial Setup\n\n1. Create `terraform.tfvars` in the root of the project directory\n1. Inside of `terraform.tfvars` put:\n    ```\n    do_token = \"YOUR_DIGITAL_OCEAN_API_KEY_HERE\"\n    do_ssh_key_file = \"PATH_TO_YOUR_SSH_PUBLIC_KEY_HERE\"\n    do_ssh_key_name = \"A_NAME_TO_ADD_YOUR_SSH_PUBLIC_KEY_UNDER_IDK_PICK_ONE\"\n    ```\n1. Run `terraform init` to get required plugins\n\n## Demo Usage\n\n1. Run `./run.sh`\n1. Follow the instructions at completion to access Prometheus interface on the\n   monitoring host.\n\n## Background\n\n### Prometheus\n\n[Prometheus](https://prometheus.io/) is \"an open-source systems monitoring and\nalerting toolkit originally built at SoundCloud\". It provides a slick\nmulti-dimensional time series metrics system exposing a powerful query language.\n[LWN](https://lwn.net) recently published a [great introduction to monitoring\nwith Prometheus](https://lwn.net/Articles/744410/). Much like the author of the\narticle I've recently transitioned my own systems from [Munin\nmonitoring](http://munin-monitoring.org/) to Prometheus with great success.\n\nPrometheus is simple and easy to understand. At its core metrics from individual\nservices/machines are exposed via HTTP at a `/metrics` URL path. These endpoints\nare often made available by dedicated programs Prometheus calls \"exporters\".\nPeriodically (every 15s by default) the Prometheus server scrapes configured\nmetrics endpoints (\"targets\" in Prometheus parlance), collecting the data into\nthe time series database.\n\nPrometheus makes available a first-party\n[`node_exporter`](https://github.com/prometheus/node_exporter) that exposes\ntypical system stats (disk space, CPU usage, network interface stats, etc) via\na `/metrics` endpoint. This repostiory/example only configures this one exporter\nbut [others are\navailable](https://github.com/prometheus/docs/blob/master/content/docs/instrumenting/exporters.md)\nand this approach generalizes to them as well.\n\n### Prometheus Authentication/Authorization/Encryption\n\n[Prometheus' own documentation](https://prometheus.io/docs/operating/security/#authentication-authorisation-encryption)\nis clear and up-front about the fact that \"Prometheus and its components do not\nprovide any server-side authentication, authorisation or encryption\". Alone,\nthe `node_exporter` has no ability to encrypt the metrics data it provides to a\nprometheus scraper, and no way to authenticate that the thing requesting metrics\ndata is the prometheus scraper you expect. If your Prometheus server is in\nToronto and your nodes are spread out around the world this poses a significant\nobstacle to overcome.\n\nThe official recommendation is to deploy mutually authenticated TLS with [client\ncertificates](https://en.wikipedia.org/wiki/Transport_Layer_Security#Client-authenticated_TLS_handshake),\nusing a reverse proxy. I'm certainly [not adverse to\nTLS](https://letsencrypt.org/) but building your own internal PKI, deploying\na dedicated reverse proxy to each host next to the exporter, and configuring\nthe reverse proxy instances, the exporter instances, and Prometheus for\nclient authentication is certainly not a walk in the park.\n\nAvoiding the hassle has driven folks to creative (but cumbersome) [SSH based\nsolutions](https://miek.nl/2016/february/24/monitoring-with-ssh-and-prometheus/)\nand, more creatively, [tor hidden services](https://ef.gy/secure-prometheus-ssh-hidden-service).\n\nWhat if there was.... :sparkles: _A better way_ :sparkles:\n\n### WireGuard\n\n[WireGuard](https://www.wireguard.com/) rules. It's an \"extremely\nsimple yet fast and modern VPN that utilizes state-of-the-art cryptography\". The\n[white paper](https://www.wireguard.com/papers/wireguard.pdf), originally\npublished at [NDSS\n2017](https://www.ndss-symposium.org/ndss2017/ndss-2017-programme/wireguard-next-generation-kernel-network-tunnel/)\ngoes into exquisite detail on the protocol and the small, easy to audit, and\nperformant kernel mode implementation\n\nThe tl;dr is that WireGuard lets us create fast, encrypted, authenticated\nlinks between servers. Its implementation is perfectly suited to writing\nfirewall rules and we can easily work with the standard network interface it\ncreates. No PKI or certificates required. There's not a single byte of ASN.1 in\nsight. It's enough to bring you to tears.\n\n### WireGuard meets Prometheus\n\nIf each target machine and Prometheus server has a WireGuard keypair\n\u0026 interface, then we can configure the target exporters to bind only to the\nWireGuard interface. We can also write firewall rules that restrict traffic to\nthe exporter such that it must arrive over the WireGuard interface and from\nthe Prometheus server's WireGuard peer IP. The end result is a system that only\nallows fully encrypted, fully authenticated access to the exporter stats from\nthe minimum number of hosts. It also fails closed! If something goes wrong with\nthe WireGuard configuration the exporter will not be internet accessible - rad!\nNo extra services, or complex configuration.\n\n### Implementation\n\nInitially I was going to write this as a blog post, but talk is cheap! Running\ncode is much better. Using Terraform and Ansible makes this a reproducable\ndemonstration of the idea.\n\n#### Terraform\n\nThe Terraform config in\n[`promguard.tf`](https://github.com/cpu/PromGuard/blob/master/promguard.tf)\nhas three main responsibilities:\n\n1. Creating 1 monitor droplet and 3 to-be-monitored node droplets\n1. Generating an Ansible inventory\n1. Assigning WireGuard IPs to each droplet\n\nThere isn't anything especially fancy about item 1. The [`monitor`\ndroplet](https://github.com/cpu/PromGuard/blob/af52c13d83367f0f049cbb29b5dc73c91270ad93/promguard.tf#L153:L171)\nand the `${var.node_count}` individual [`node`\ndroplets](https://github.com/cpu/PromGuard/blob/af52c13d83367f0f049cbb29b5dc73c91270ad93/promguard.tf#L173:L194)\nboth use a `remote-exec` provisioner. This ensures the droplets have SSH\navailable before continuing and also bootstraps the droplets with Python so that\nAnsible playbooks can be run.\n\nThe [Ansible\ninventory](http://docs.ansible.com/ansible/latest/intro_inventory.html) is\ngenerated in three parts. First, for [each to-be-monitored\nnode](https://github.com/cpu/PromGuard/blob/af52c13d83367f0f049cbb29b5dc73c91270ad93/templates/hostname.tpl),\na inventory line is\n[templated](https://github.com/cpu/PromGuard/blob/af52c13d83367f0f049cbb29b5dc73c91270ad93/templates/hostname.tpl). The end result is a line of the form: `\u003cnode name\u003e ansible_host=\u003cnode IPv4 address\u003e wireguard_ip=\u003cnode wireguard\naddress\u003e`. An inventory line [for the monitor\nnode](https://github.com/cpu/PromGuard/blob/af52c13d83367f0f049cbb29b5dc73c91270ad93/promguard.tf#L98:L109) is generated the same way. Lastly another [template](https://github.com/cpu/PromGuard/blob/af52c13d83367f0f049cbb29b5dc73c91270ad93/templates/inventory.tpl) is used to [stitch together the node and monitor inventory lines](https://github.com/cpu/PromGuard/blob/af52c13d83367f0f049cbb29b5dc73c91270ad93/promguard.tf#L111:L119) into one Ansible inventory.\n\nWhen generating the inventory line each server is given a WireGuard IP in the\n`10.0.0.0` RFC1918 reserved network.\nTo make life easy [the monitor is always the first\naddress](https://github.com/cpu/PromGuard/blob/af52c13d83367f0f049cbb29b5dc73c91270ad93/promguard.tf#L105:L107),\n`10.0.0.1`. The nodes are [assigned sequential\naddresses](https://github.com/cpu/PromGuard/blob/af52c13d83367f0f049cbb29b5dc73c91270ad93/promguard.tf#L90:PL94)\nstarting at `10.0.0.2`.\n\n#### Ansible\n\nThere are four main Ansible playbooks at work:\n1. The [UFW\n   playbook](https://github.com/cpu/PromGuard/tree/367f334819b6ba4c6a323e3bbec76b934f93b7c7/playbooks/roles/ufw/tasks)\n1. The [WireGuard\n   playbook](https://github.com/cpu/PromGuard/tree/367f334819b6ba4c6a323e3bbec76b934f93b7c7/playbooks/roles/wireguard)\n1. The [Node Exporter playbook](https://github.com/cpu/PromGuard/tree/367f334819b6ba4c6a323e3bbec76b934f93b7c7/playbooks/roles/node_exporter)\n1. The [Prometheus server\n   playbook](https://github.com/cpu/PromGuard/tree/367f334819b6ba4c6a323e3bbec76b934f93b7c7/playbooks/roles/prometheus-servera)\n\nThe UFW playbook is very straight-forward. It [installs\nUFW](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/ufw/tasks/main.yml#L3:L5), allows [inbound TCP on port 22](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/ufw/tasks/main.yml#L7:L12) for SSH, and [enables UFW at boot](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/ufw/tasks/main.yml#L14:L18) with a default deny inbound policy.\n\nThe WireGuard playbook [installs `wireguard-dkms` and\n`wireguard-tools`](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/wireguard/tasks/main.yml#L3:L17)\nafter setting up the Ubuntu PPA. Each server [generates its own WireGuard\nprivate\nkey](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/wireguard/tasks/main.yml#L27:L30).\nThe public key is [derived from the private key](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/wireguard/tasks/main.yml#L37:L40) and registered as an Ansible fact [for\nthat\nhost](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/wireguard/tasks/main.yml#L42:L44). This makes it easy to refer to a server's WireGuard public key from templates and tasks. Each private key is only known by the server it belongs to and the host running the Ansible playbooks.\n\nBeyond installing WireGuard and computing keys the WireGuard playbook also\nwrites a [WireGuard config\nfile](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/wireguard/tasks/main.yml#L46:L52), and a [network interface config\nfile](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/wireguard/tasks/main.yml#L54:L60).\n\nThe WireGuard config file (`/etc/wireguard/wg0.conf`) for each host is written from a template that [declares an `[Interface]`](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/wireguard/templates/wg0.conf.j2#L4:L6) and the required `[Peer]` entries. The `[Peer]` config differs based on whether the host is a monitor, needing [one `[Peer]` for every server](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/wireguard/templates/wg0.conf.j2#L19:L23), or if it is a monitored server needing only [one `[Peer]` for the monitor](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/wireguard/templates/wg0.conf.j2#L10:L14). In both cases the `PublicKey` and `AllowedIPs` for each peer are populated using Ansible facts and the inventory.\n\nThe network interface config file (`/etc/network/interfaces.d/60-wireguard.cfg.j2`) for each host is written from a template that [configures a `wg0` network `iface`](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/wireguard/templates/60-wireguard.cfg.j2#L6:L11). The `address` is populated based on the server's `wireguard_ip` assigned in the Ansible inventory. The `pre-up` statements configure the interface as a WireGuard type interface that should use the `/etc/wireguard/wg0.conf` file the WireGuard role creates.\n\nThis gives us a `wg0` interface on each server, configured with the right\nIP/keypair, and ready with peer configuration based on the server's role.\n\n#### Node Exporter\n\nThe `node_exporter` role is pretty simple. The [majority of the\ntasks](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/node_exporter/tasks/main.yml#L3:L46)\nare for setting up a dedicated user, downloading the exporter code, unpacking\nit, and making sure it runs on start with a systemd unit.\n\nNotably [the systemd unit\ntemplate](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/node_exporter/templates/node_exporter.service.j2)\nmakes sure the `ExecStart` line [passes\n`--web.listen-address`](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/node_exporter/templates/node_exporter.service.j2#L11)\nto restrict the `node_exporter` to listening on the `wireguard_ip` (e.g. on\n`wg0`). By default it will listen on `127.0.0.1` and we only want it to be\naccessible over WireGuard instead.\n\nThe `node_exporter` role also [adds a new firewall\nrule](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/node_exporter/tasks/main.yml#L48:L58)\nfor all of the to-be-monitored servers. This rule allows TCP traffic to the\n`node_exporter` port destined to the `wireguard_ip` from the [monitor's\n`wireguard_ip`](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/node_exporter/tasks/main.yml#L50).\n\nThe end result is that every to-be-monitored server has a `node_exporter` that\ncan only be accessed over WireGuard, and only by the monitor server. The monitor\nserver isn't able to access any other ports/services and the metrics data will\nalways be encrypted while it travels between the server and the monitor.\n\n#### Prometheus\n\nLike the `node_exporter` role the [bulk of the\ntasks](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/prometheus-server/tasks/main.yml)\nin the Prometheus role are for adding a dedicated user, downloading Prometheus,\ninstalling it, and making sure it has a systemd unit.\n\nThe main point of interest is the\n[`prometheus.yml.j2`](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/prometheus-server/templates/prometheus.yml.j2)\ntemplate that is used to write the Prometheus server yaml config file on the\nmonitor server.\n\nFor every server in the inventory a [target scrape job is\nwritten](https://github.com/cpu/PromGuard/blob/901e88d145f8dc971822546a130f685bb5035ce7/playbooks/roles/prometheus-server/templates/prometheus.yml.j2#L10:L12). The `targets` IP is the `wireguard_ip` of each server, ensuring the stat collection is done over WireGuard.\n\nThe end result is that Prometheus is configured to scrape stats for each server,\nover the monitor server's WireGuard link to each target server. The target\nservers `node_exporter` is configured to listen on the WireGuard interface and\nthe firewall has a rule in place to allow the monitor to access the\n`node_exporter`.\n\n## Conclusion\n\nPhew! That's a lot of text. Thanks for sticking it out. I hope this was a useful\nexample/resource.\n\nWhile this Terraform/Ansible code is just a demo, and specific to\nPrometheus/Node Exporter the idea and much of the code is transferrable to other\nscenarios where you need to offer a service to a trusted set of hosts in an\nencrypted/authenticated setting or want to use Terraform and Ansible together.\nFeel free to fork \u0026 adapt. Definitely let me know if you use this as a starting\npoint for another fun WireGuard project :-)\n\n## Example Run\n\n* An example `./run.sh` invocation recorded with `asciinema`. The IP addresses\n  referred to elsewhere in this README match up with this recording.\n\n[![asciicast](https://asciinema.org/a/RUGQCKxe8UAPPAMXtfRXrW33F.png)](https://asciinema.org/a/RUGQCKxe8UAPPAMXtfRXrW33F)\n\n* A small diagram of the resulting infrastructure. One monitor node\n  (`promguard-monitor-1`) located in Toronto is configured with a WireGuard\n  tunnel to three nodes to be monitored (`promguard-node-1` in London,\n  `promguard-node-2` in San Francisco, and `promguard-node-3` in Singapore):\n\n![Network Diagram](https://raw.githubusercontent.com/cpu/promguard/master/PromGuard.Network.Diagram.png)\n\n* Here's what the Prometheus targets interface looks like accessed over a SSH\n  port forward to the monitor host. Each target is specified by a WireGuard\n  address (`10.0.0.x`):\n\n![Configured Targets](https://binaryparadox.net/d/3b89f9a4-b2f4-4c1e-bfcc-96cf085c4bcb.jpg)\n\n* The monitor host's (`promguard-monitor-1`) firewall is very simple. Nothing\n  but SSH and WireGuard here! Strictly speaking this node doesn't even need to\n  expose WireGuard since it only connects outbound to the monitored nodes.\n\n```\nroot@promguard-monitor-1:~# ufw status\nStatus: active\n\nTo                         Action      From\n--                         ------      ----\n22/tcp                     ALLOW       Anywhere                   # OpenSSH\n51820/udp                  ALLOW       Anywhere                   # WireGuard\n22/tcp (v6)                ALLOW       Anywhere (v6)              # OpenSSH\n51820/udp (v6)             ALLOW       Anywhere (v6)              # WireGuard\n```\n\n* Here's what the monitor host's (`promguard-monitor-1`) `wg0` interface status\n  looks like. It has one peer configured for each of the nodes (`10.0.0.2`,\n  `10.0.0.3`, and `10.0.0.4`):\n\n```\nroot@promguard-monitor-1:~# wg\ninterface: wg0\n  public key: TxMVo4TkXvp+Av44qL1TiW1E0m6qhdM48E/L8AxdYj4=\n  private key: (hidden)\n  listening port: 51820\n\npeer: uJIL7F6e/02Z4byfX2Tl+WRrAu7SXLt6FpP3WBum3U8=\n  endpoint: 178.62.105.97:51820\n  allowed ips: 10.0.0.2/32\n  latest handshake: 1 minute, 47 seconds ago\n  transfer: 240.56 KiB received, 21.58 KiB sent\n\npeer: oJ0y/SGhq4ebIT1m2Ago4/W4/opkeY9WzKLrxFyxlWw=\n  endpoint: 128.199.186.30:51820\n  allowed ips: 10.0.0.4/32\n  latest handshake: 1 minute, 48 seconds ago\n  transfer: 242.62 KiB received, 21.58 KiB sent\n\npeer: MOCzYMLelX8uo2WaU/y/xSBRUUphPPoMNl8FymHOGlU=\n  endpoint: 138.197.207.168:51820\n  allowed ips: 10.0.0.3/32\n  latest handshake: 1 minute, 49 seconds ago\n  transfer: 241.71 KiB received, 21.58 KiB sent\n```\n\n* Here's what an example node's (`promguard-node-3`) firewall looks like. It\n  only allows access to the `node_exporter` port (`9100`) over the WireGuard\n  interface, and only for the monitor node's source IP (`10.0.0.1`):\n\n```\nroot@promguard-node-3:~# ufw status\nStatus: active\n\nTo                         Action      From\n--                         ------      ----\n22/tcp                     ALLOW       Anywhere                   # OpenSSH\n51820/udp                  ALLOW       Anywhere                   # WireGuard\n10.0.0.4 9100/tcp          ALLOW       10.0.0.1                   # promguard-monitor-1 WireGuard node-exporter scraper\n22/tcp (v6)                ALLOW       Anywhere (v6)              # OpenSSH\n51820/udp (v6)             ALLOW       Anywhere (v6)              # WireGuard\n```\n\n* An example node's (`promguard-node-3` again) `wg0` interface shows only one\n  peer, the monitor host:\n\n```\nroot@promguard-node-3:~# wg\ninterface: wg0\n  public key: oJ0y/SGhq4ebIT1m2Ago4/W4/opkeY9WzKLrxFyxlWw=\n  private key: (hidden)\n  listening port: 51820\n\npeer: TxMVo4TkXvp+Av44qL1TiW1E0m6qhdM48E/L8AxdYj4=\n  endpoint: 165.227.33.184:51820\n  allowed ips: 10.0.0.1/32\n  latest handshake: 31 seconds ago\n  transfer: 25.50 KiB received, 285.54 KiB sent\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcpu%2Fpromguard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcpu%2Fpromguard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcpu%2Fpromguard/lists"}