simplified the monitoring tutorial

This commit is contained in:
nihilist 2025-04-27 18:36:18 +02:00
parent 055249d719
commit 68196c4d47
31 changed files with 1917 additions and 305 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

View file

@ -1,545 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
<dia:diagramdata>
<dia:attribute name="background">
<dia:color val="#ffffffff"/>
</dia:attribute>
<dia:attribute name="pagebreak">
<dia:color val="#000099ff"/>
</dia:attribute>
<dia:attribute name="paper">
<dia:composite type="paper">
<dia:attribute name="name">
<dia:string>#A4#</dia:string>
</dia:attribute>
<dia:attribute name="tmargin">
<dia:real val="2.8222"/>
</dia:attribute>
<dia:attribute name="bmargin">
<dia:real val="2.8222"/>
</dia:attribute>
<dia:attribute name="lmargin">
<dia:real val="2.8222"/>
</dia:attribute>
<dia:attribute name="rmargin">
<dia:real val="2.8222"/>
</dia:attribute>
<dia:attribute name="is_portrait">
<dia:boolean val="true"/>
</dia:attribute>
<dia:attribute name="scaling">
<dia:real val="1"/>
</dia:attribute>
<dia:attribute name="fitto">
<dia:boolean val="false"/>
</dia:attribute>
</dia:composite>
</dia:attribute>
<dia:attribute name="grid">
<dia:composite type="grid">
<dia:attribute name="dynamic">
<dia:boolean val="true"/>
</dia:attribute>
<dia:attribute name="width_x">
<dia:real val="1"/>
</dia:attribute>
<dia:attribute name="width_y">
<dia:real val="1"/>
</dia:attribute>
<dia:attribute name="visible_x">
<dia:int val="1"/>
</dia:attribute>
<dia:attribute name="visible_y">
<dia:int val="1"/>
</dia:attribute>
<dia:composite type="color"/>
</dia:composite>
</dia:attribute>
<dia:attribute name="color">
<dia:color val="#d8e5e5ff"/>
</dia:attribute>
<dia:attribute name="guides"/>
<dia:attribute name="guide_color">
<dia:color val="#00ff00ff"/>
</dia:attribute>
<dia:attribute name="display">
<dia:composite type="display">
<dia:attribute name="antialiased">
<dia:boolean val="true"/>
</dia:attribute>
<dia:attribute name="snap-to-grid">
<dia:boolean val="true"/>
</dia:attribute>
<dia:attribute name="snap-to-guides">
<dia:boolean val="true"/>
</dia:attribute>
<dia:attribute name="snap-to-object">
<dia:boolean val="true"/>
</dia:attribute>
<dia:attribute name="show-grid">
<dia:boolean val="true"/>
</dia:attribute>
<dia:attribute name="show-guides">
<dia:boolean val="true"/>
</dia:attribute>
<dia:attribute name="show-connection-points">
<dia:boolean val="true"/>
</dia:attribute>
</dia:composite>
</dia:attribute>
</dia:diagramdata>
<dia:layer name="Background" visible="true" connectable="false"/>
<dia:layer name="Background" visible="true" connectable="true" active="true">
<dia:object type="Cisco - IBM Tower" version="1" id="O0">
<dia:attribute name="obj_pos">
<dia:point val="7.12747,13.2"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="6.88485,12.9197;9.25015,16.9315"/>
</dia:attribute>
<dia:attribute name="meta">
<dia:composite type="dict"/>
</dia:attribute>
<dia:attribute name="elem_corner">
<dia:point val="7.12747,13.2"/>
</dia:attribute>
<dia:attribute name="elem_width">
<dia:real val="1.997892498066502"/>
</dia:attribute>
<dia:attribute name="elem_height">
<dia:real val="3.6314740435786836"/>
</dia:attribute>
<dia:attribute name="line_width">
<dia:real val="0.10000000000000001"/>
</dia:attribute>
<dia:attribute name="line_colour">
<dia:color val="#000000ff"/>
</dia:attribute>
<dia:attribute name="fill_colour">
<dia:color val="#ffffffff"/>
</dia:attribute>
<dia:attribute name="show_background">
<dia:boolean val="true"/>
</dia:attribute>
<dia:attribute name="line_style">
<dia:enum val="0"/>
<dia:real val="1"/>
</dia:attribute>
<dia:attribute name="flip_horizontal">
<dia:boolean val="false"/>
</dia:attribute>
<dia:attribute name="flip_vertical">
<dia:boolean val="false"/>
</dia:attribute>
<dia:attribute name="subscale">
<dia:real val="1"/>
</dia:attribute>
</dia:object>
<dia:object type="Cisco - IBM Tower" version="1" id="O1">
<dia:attribute name="obj_pos">
<dia:point val="7.12747,3.20034"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="6.88485,2.92;9.25015,6.93181"/>
</dia:attribute>
<dia:attribute name="meta">
<dia:composite type="dict"/>
</dia:attribute>
<dia:attribute name="elem_corner">
<dia:point val="7.12747,3.20034"/>
</dia:attribute>
<dia:attribute name="elem_width">
<dia:real val="1.997892498066502"/>
</dia:attribute>
<dia:attribute name="elem_height">
<dia:real val="3.6314740435786836"/>
</dia:attribute>
<dia:attribute name="line_width">
<dia:real val="0.10000000000000001"/>
</dia:attribute>
<dia:attribute name="line_colour">
<dia:color val="#000000ff"/>
</dia:attribute>
<dia:attribute name="fill_colour">
<dia:color val="#ffffffff"/>
</dia:attribute>
<dia:attribute name="show_background">
<dia:boolean val="true"/>
</dia:attribute>
<dia:attribute name="line_style">
<dia:enum val="0"/>
<dia:real val="1"/>
</dia:attribute>
<dia:attribute name="flip_horizontal">
<dia:boolean val="false"/>
</dia:attribute>
<dia:attribute name="flip_vertical">
<dia:boolean val="false"/>
</dia:attribute>
<dia:attribute name="subscale">
<dia:real val="1"/>
</dia:attribute>
</dia:object>
<dia:object type="Cisco - Web browser" version="1" id="O2">
<dia:attribute name="obj_pos">
<dia:point val="28.0373,9.05"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="28.0352,9.04772;30.0652,11.0525"/>
</dia:attribute>
<dia:attribute name="meta">
<dia:composite type="dict"/>
</dia:attribute>
<dia:attribute name="elem_corner">
<dia:point val="28.0373,9.05"/>
</dia:attribute>
<dia:attribute name="elem_width">
<dia:real val="2.02532561505065"/>
</dia:attribute>
<dia:attribute name="elem_height">
<dia:real val="1.9999999999999989"/>
</dia:attribute>
<dia:attribute name="line_width">
<dia:real val="0.10000000000000001"/>
</dia:attribute>
<dia:attribute name="line_colour">
<dia:color val="#000000ff"/>
</dia:attribute>
<dia:attribute name="fill_colour">
<dia:color val="#ffffffff"/>
</dia:attribute>
<dia:attribute name="show_background">
<dia:boolean val="true"/>
</dia:attribute>
<dia:attribute name="line_style">
<dia:enum val="0"/>
<dia:real val="1"/>
</dia:attribute>
<dia:attribute name="flip_horizontal">
<dia:boolean val="false"/>
</dia:attribute>
<dia:attribute name="flip_vertical">
<dia:boolean val="false"/>
</dia:attribute>
<dia:attribute name="subscale">
<dia:real val="1"/>
</dia:attribute>
</dia:object>
<dia:object type="Standard - Text" version="1" id="O3">
<dia:attribute name="obj_pos">
<dia:point val="6.07906,2.45"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="6.07906,1.765;10.0341,2.64"/>
</dia:attribute>
<dia:attribute name="text">
<dia:composite type="text">
<dia:attribute name="string">
<dia:string>#Target server#</dia:string>
</dia:attribute>
<dia:attribute name="font">
<dia:font family="sans" style="0" name="Helvetica"/>
</dia:attribute>
<dia:attribute name="height">
<dia:real val="0.80000000000000004"/>
</dia:attribute>
<dia:attribute name="pos">
<dia:point val="6.07906,2.45"/>
</dia:attribute>
<dia:attribute name="color">
<dia:color val="#000000ff"/>
</dia:attribute>
<dia:attribute name="alignment">
<dia:enum val="0"/>
</dia:attribute>
</dia:composite>
</dia:attribute>
<dia:attribute name="valign">
<dia:enum val="3"/>
</dia:attribute>
</dia:object>
<dia:object type="Standard - Text" version="1" id="O4">
<dia:attribute name="obj_pos">
<dia:point val="3.65,18"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="3.65,17.315;12.485,18.19"/>
</dia:attribute>
<dia:attribute name="text">
<dia:composite type="text">
<dia:attribute name="string">
<dia:string>#Grafana + Prometheus server#</dia:string>
</dia:attribute>
<dia:attribute name="font">
<dia:font family="sans" style="0" name="Helvetica"/>
</dia:attribute>
<dia:attribute name="height">
<dia:real val="0.80000000000000004"/>
</dia:attribute>
<dia:attribute name="pos">
<dia:point val="3.65,18"/>
</dia:attribute>
<dia:attribute name="color">
<dia:color val="#000000ff"/>
</dia:attribute>
<dia:attribute name="alignment">
<dia:enum val="0"/>
</dia:attribute>
</dia:composite>
</dia:attribute>
<dia:attribute name="valign">
<dia:enum val="3"/>
</dia:attribute>
</dia:object>
<dia:object type="Cisco - Web cluster" version="1" id="O5">
<dia:attribute name="obj_pos">
<dia:point val="13.7817,7.2"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="13.7807,7.199;24.238,13.468"/>
</dia:attribute>
<dia:attribute name="meta">
<dia:composite type="dict"/>
</dia:attribute>
<dia:attribute name="elem_corner">
<dia:point val="13.7817,7.2"/>
</dia:attribute>
<dia:attribute name="elem_width">
<dia:real val="10.455280265819127"/>
</dia:attribute>
<dia:attribute name="elem_height">
<dia:real val="6.2670116037409072"/>
</dia:attribute>
<dia:attribute name="line_width">
<dia:real val="0.10000000000000001"/>
</dia:attribute>
<dia:attribute name="line_colour">
<dia:color val="#000000ff"/>
</dia:attribute>
<dia:attribute name="fill_colour">
<dia:color val="#ffffffff"/>
</dia:attribute>
<dia:attribute name="show_background">
<dia:boolean val="true"/>
</dia:attribute>
<dia:attribute name="line_style">
<dia:enum val="0"/>
<dia:real val="1"/>
</dia:attribute>
<dia:attribute name="flip_horizontal">
<dia:boolean val="false"/>
</dia:attribute>
<dia:attribute name="flip_vertical">
<dia:boolean val="false"/>
</dia:attribute>
<dia:attribute name="subscale">
<dia:real val="1"/>
</dia:attribute>
</dia:object>
<dia:object type="Standard - Text" version="1" id="O6">
<dia:attribute name="obj_pos">
<dia:point val="17.6,6.45"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="17.6,5.765;21.305,6.64"/>
</dia:attribute>
<dia:attribute name="text">
<dia:composite type="text">
<dia:attribute name="string">
<dia:string>#Tor Network#</dia:string>
</dia:attribute>
<dia:attribute name="font">
<dia:font family="sans" style="0" name="Helvetica"/>
</dia:attribute>
<dia:attribute name="height">
<dia:real val="0.80000000000000004"/>
</dia:attribute>
<dia:attribute name="pos">
<dia:point val="17.6,6.45"/>
</dia:attribute>
<dia:attribute name="color">
<dia:color val="#000000ff"/>
</dia:attribute>
<dia:attribute name="alignment">
<dia:enum val="0"/>
</dia:attribute>
</dia:composite>
</dia:attribute>
<dia:attribute name="valign">
<dia:enum val="3"/>
</dia:attribute>
</dia:object>
<dia:object type="Standard - Text" version="1" id="O7">
<dia:attribute name="obj_pos">
<dia:point val="26,12.3"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="26,11.5931;32.1169,12.5337"/>
</dia:attribute>
<dia:attribute name="text">
<dia:composite type="text">
<dia:attribute name="string">
<dia:string>#Whonix Workstation#</dia:string>
</dia:attribute>
<dia:attribute name="font">
<dia:font family="sans" style="0" name="Helvetica"/>
</dia:attribute>
<dia:attribute name="height">
<dia:real val="0.80000000000000004"/>
</dia:attribute>
<dia:attribute name="pos">
<dia:point val="26,12.3"/>
</dia:attribute>
<dia:attribute name="color">
<dia:color val="#000000ff"/>
</dia:attribute>
<dia:attribute name="alignment">
<dia:enum val="0"/>
</dia:attribute>
</dia:composite>
</dia:attribute>
<dia:attribute name="valign">
<dia:enum val="3"/>
</dia:attribute>
</dia:object>
<dia:object type="Standard - Arc" version="0" id="O8">
<dia:attribute name="obj_pos">
<dia:point val="8.28789,13.1957"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="8.23789,5.59814;15.7,14.4337"/>
</dia:attribute>
<dia:attribute name="conn_endpoints">
<dia:point val="8.28789,13.1957"/>
<dia:point val="8.28789,6.83608"/>
</dia:attribute>
<dia:attribute name="arc_color">
<dia:color val="#00ff00ff"/>
</dia:attribute>
<dia:attribute name="curve_distance">
<dia:real val="7.3621075348901277"/>
</dia:attribute>
<dia:attribute name="end_arrow">
<dia:enum val="22"/>
</dia:attribute>
<dia:attribute name="end_arrow_length">
<dia:real val="0.5"/>
</dia:attribute>
<dia:attribute name="end_arrow_width">
<dia:real val="0.5"/>
</dia:attribute>
<dia:connections>
<dia:connection handle="0" to="O0" connection="7"/>
<dia:connection handle="1" to="O1" connection="7"/>
</dia:connections>
</dia:object>
<dia:object type="Standard - Text" version="1" id="O9">
<dia:attribute name="obj_pos">
<dia:point val="9.85,13"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="9.85,12.315;13.6925,13.19"/>
</dia:attribute>
<dia:attribute name="text">
<dia:composite type="text">
<dia:attribute name="string">
<dia:string>#Scrapes data#</dia:string>
</dia:attribute>
<dia:attribute name="font">
<dia:font family="sans" style="0" name="Helvetica"/>
</dia:attribute>
<dia:attribute name="height">
<dia:real val="0.80000000000000004"/>
</dia:attribute>
<dia:attribute name="pos">
<dia:point val="9.85,13"/>
</dia:attribute>
<dia:attribute name="color">
<dia:color val="#000000ff"/>
</dia:attribute>
<dia:attribute name="alignment">
<dia:enum val="0"/>
</dia:attribute>
</dia:composite>
</dia:attribute>
<dia:attribute name="valign">
<dia:enum val="3"/>
</dia:attribute>
</dia:object>
<dia:object type="Standard - Arc" version="0" id="O10">
<dia:attribute name="obj_pos">
<dia:point val="28.3765,10.4287"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="9.06369,10.1594;28.439,16.4023"/>
</dia:attribute>
<dia:attribute name="conn_endpoints">
<dia:point val="28.3765,10.4287"/>
<dia:point val="9.12616,16.3398"/>
</dia:attribute>
<dia:attribute name="arc_color">
<dia:color val="#ff0000ff"/>
</dia:attribute>
<dia:attribute name="curve_distance">
<dia:real val="1"/>
</dia:attribute>
<dia:attribute name="start_arrow">
<dia:enum val="3"/>
</dia:attribute>
<dia:attribute name="start_arrow_length">
<dia:real val="0.5"/>
</dia:attribute>
<dia:attribute name="start_arrow_width">
<dia:real val="0.5"/>
</dia:attribute>
<dia:attribute name="end_arrow">
<dia:enum val="22"/>
</dia:attribute>
<dia:attribute name="end_arrow_length">
<dia:real val="0.5"/>
</dia:attribute>
<dia:attribute name="end_arrow_width">
<dia:real val="0.5"/>
</dia:attribute>
<dia:connections>
<dia:connection handle="0" to="O2" connection="8"/>
<dia:connection handle="1" to="O0" connection="6"/>
</dia:connections>
</dia:object>
</dia:layer>
<dia:layer name="New layer 1" visible="true" connectable="true">
<dia:object type="Standard - Text" version="1" id="O11">
<dia:attribute name="obj_pos">
<dia:point val="23.5,10.25"/>
</dia:attribute>
<dia:attribute name="obj_bb">
<dia:rectangle val="23.5,9.565;27.6125,10.44"/>
</dia:attribute>
<dia:attribute name="text">
<dia:composite type="text">
<dia:attribute name="string">
<dia:string>#Visualize data#</dia:string>
</dia:attribute>
<dia:attribute name="font">
<dia:font family="sans" style="0" name="Helvetica"/>
</dia:attribute>
<dia:attribute name="height">
<dia:real val="0.80000000000000004"/>
</dia:attribute>
<dia:attribute name="pos">
<dia:point val="23.5,10.25"/>
</dia:attribute>
<dia:attribute name="color">
<dia:color val="#000000ff"/>
</dia:attribute>
<dia:attribute name="alignment">
<dia:enum val="0"/>
</dia:attribute>
</dia:composite>
</dia:attribute>
<dia:attribute name="valign">
<dia:enum val="3"/>
</dia:attribute>
</dia:object>
</dia:layer>
</dia:diagram>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

View file

@ -4,11 +4,11 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Cloud provider threat model">
<meta name="author" content="MulliganSecurity">
<meta name="description" content="">
<meta name="author" content="">
<link rel="shortcut icon" href="../../../../../../assets/img/favicon.png">
<title>Anonymous Server Monitoring</title>
<title>Anonymous Monitoring (Grafana, Prometheus, Node-exporter)</title>
<!-- Bootstrap core CSS -->
<link href="../../assets/css/bootstrap.css" rel="stylesheet">
@ -56,329 +56,366 @@
<!-- +++++ Posts Lists +++++ -->
<!-- +++++ First Post +++++ -->
<div id="anon2">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2">
<a href="../index.html">Previous Page</a></br></br><p><img src="../../assets/img/user.png" width="50px" height="50px"> <ba>nihilist - 27 / 04 / 2025</ba></p>
<h1>Anonymous Monitoring (Grafana, Prometheus, Node-exporter) </h1>
<p>In this tutorial we're going to cover how you can monitor the resource consumption of your remote servers while maintaining your anonymity, using Grafana, Prometheus, and node exporter.</p>
<p><img src="../logos/daturagit.png" style="width:100px"> <u>Sidenote:</u> Help us improve this tutorial by letting us know if there's anything missing or incorrect on this <a href="http://git.nowherejezfoltodf4jiyl6r56jnzintap5vyjlia7fkirfsnfizflqd.onion/nihilist/blog-contributions/issues/221">git issue</a> directly!</p>
</div>
</div><!-- /row -->
</div> <!-- /container -->
</div><!-- /grey -->
<!-- +++++ Second Post +++++ -->
<div id="anon3">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2">
<h2><b>What is the targeted setup? </b></h2>
<p>In this setup, we have 3 servers. Server A is going to monitor Server B and C.</p>
<img src="0.png" class="imgRz">
<p>Server A is going to have the following 3 services:</p>
<pre><code class="nim">
-Grafana: to display the data retrieved by prometheus
-Prometheus: to retrieve the data served by the node-exporters
-node-exporter: to serve the server resource usage data (ex: CPU consumption, RAM consumption, etc)
</code></pre>
<p>Server B and C are going to have the node-exporter service on them, and thanks to it Server A's prometheus service is going to be able to monitor their resource consumption.</p>
<p>Now the added complexity that we have to navigate in this tutorial is that <b>each server can only be reached by their own .onion hostnames.</b> we cannot query them via their IPs directly because otherwise we'd reveal the origin and destination of the servers. Hence in this tutorial we're going to maintain the Serverside anonymity.</p>
</div>
</div><!-- /row -->
</div> <!-- /container -->
</div><!-- /white -->
<div id="anon2">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2">
<h2><b>Server A setup (Grafana, Prometheus, Node-exporter)</b></h2> </br> </br>
<p>First let's setup Server A:</p>
<pre><code class="nim">
[ Wonderland ] [ /dev/pts/22 ] [/srv/]
→ sudo apt install docker.io docker-compose -y
[ Wonderland ] [ /dev/pts/22 ] [/srv/]
→ mkdir /srv/grafana ; cd /srv/grafana
[ Wonderland ] [ /dev/pts/22 ] [/srv/]
→ vim docker-compose.yml
</code></pre>
<p>Then, we have the following docker-compose.yml:</p>
<pre><code class="nim">
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ cat docker-compose.yml
networks:
tor-monitoring:
driver: bridge
ipam:
config:
- subnet: 10.16.0.0/24
gateway: 10.16.0.1
services:
grafana:
image: grafana/grafana-enterprise:latest
container_name: grafana
restart: unless-stopped
ports:
- '127.0.0.1:3222:3000'
volumes:
- grafana-data:/var/lib/grafana
environment:
GF_INSTALL_PLUGINS: "grafana-clock-panel,grafana-simple-json-datasource,grafana-worldmap-panel,grafana-piechart-panel"
networks:
tor-monitoring:
ipv4_address: 10.16.0.5
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus/:/etc/prometheus/
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'
networks:
tor-monitoring:
ipv4_address: 10.16.0.4
restart: always
tor:
image: osminogin/tor-simple
container_name: tormonitoring
volumes:
- ./tor-data:/var/lib/tor
- ./tor-data/torrc:/etc/tor
networks:
tor-monitoring:
ipv4_address: 10.16.0.3
volumes:
prometheus_data: {}
grafana-data: {}
</code></pre>
<p>From there, you can already pull the containers and activate them:</p>
<pre><code class="nim">
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ docker-compose pull ; docker-compose up -d
</code></pre>
<p>It's going to first pull the containers and then activate them, but we need to do some changes first to ensure that the tor container works as intended:</p>
<pre><code class="nim">
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ docker-compose down
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ vim tor-data/torrc/torrc
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ cat tor-data/torrc/torrc
SOCKSPort 0.0.0.0:9050
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ chown -R 100:65533 tor-data/
</code></pre>
<p>then we also configure prometheus to scrape the destination servers:</p>
<pre><code class="nim">
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ vim prometheus/prometheus.yml
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ cat prometheus/prometheus.yml
global:
scrape_interval: 15s # By default, scrape targets every 15 seconds.
external_labels:
monitor: 'datura-monitor'
scrape_configs:
- job_name: 'wonderland'
scrape_interval: 5s
proxy_url: socks5h://10.16.0.3:9050
static_configs:
- labels: {}
targets:
- serverAjezfoltodf4jiyl6r56jnzintap5vyjlia7fkirfsnfizflqd.onion:9100
- serverBjezfoltodf4jiyl6r56jnzintap5vyjlia7fkirfsnfizflqd.onion:9100
- serverCjezfoltodf4jiyl6r56jnzintap5vyjlia7fkirfsnfizflqd.onion:9100
basic_auth:
username: 'admin'
password: 'P@SSW0RD'
</code></pre>
<p>And then we can re-activate the containers:</p>
<pre><code class="nim">
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ docker-compose up -d
</code></pre>
<p>once done, we can configure the nginx reverse proxy to make sure that we can access our grafana instance:</p>
<pre><code class="nim">
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ vim /etc/nginx/sites-available/monitoring
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ cat /etc/nginx/sites-available/monitoring
upstream monitoringend {
server 127.0.0.1:3222;
#server 10.8.0.2:3009;
}
server {
listen 4443;
listen [::]:4443;
server_name monitoring.nowherejezfoltodf4jiyl6r56jnzintap5vyjlia7fkirfsnfizflqd.onion;
location / {
proxy_set_header Host $http_host;
proxy_pass http://monitoringend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
#client_max_body_size 1G;
}
}
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ ln -s /etc/nginx/sites-available/monitoring /etc/nginx/sites-enabled/
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[ Wonderland ] [ /dev/pts/19 ] [/srv/grafana]
→ systemctl restart nginx
</code></pre>
</div>
</div><!-- /row -->
</div> <!-- /container -->
</div><!-- /white -->
<!-- +++++ Second Post +++++ -->
<div id="anon1">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2">
<a href="../index.html">Previous Page</a></br></br><p><img src="../../assets/img/mulligan_sec.jpeg" width="50px" height="50px"> <ba>Mulligan Security - 2025-02-07 </a></p>
<p>
<h1><b>Anonymous Server Monitoring</b></h1>
<h2><b>Configuring Grafana</b></h2> </br> </br>
<p>Next, we can access our grafana instance to create the admin account and login:</p>
<img src="1.png" class="imgRz">
<p>Once logged in we make sure that our grafana instance uses our prometheus instance as a datasource:</p>
<img src="2.png" class="imgRz">
<img src="3.png" class="imgRz">
<img src="4.png" class="imgRz">
<p>here we mention the local IP of the prometheus container, being 10.16.0.4, with the service accessible on port 9090:</p>
<img src="5.png" class="imgRz">
<img src="6.png" class="imgRz">
<p>Then, we import a dashboard to make sure that we can visualize the data we are monitoring, out of which i recommend the excellent <a href="https://grafana.com/grafana/dashboards/1860-node-exporter-full/">"Node Exporter Full"</a> dashboard (whose ID is 1860) </p>
<img src="7.png" class="imgRz">
<img src="8.png" class="imgRz">
<img src="9.png" class="imgRz">
<img src="10.png" class="imgRz">
<img src="11.png" class="imgRz">
<p>There, the dashboard is imported, but there's no data to be seen yet because we didn't configure the node-exporter daemons on the servers that we want to monitor.</p>
<h2>What is server monitoring?</h2>
When deploying compute resources (bare-metal, VPSes or more abstract work units) you will have to manage a living system. This system will <b>always</b> have the following characteristics:
<ul>
<li>Limited ressources: the amounts of RAM and CPU cycles, network bandwidth as well as storage space are neither infinite nor free.</li>
<li>Evolving requirements: depending on how you use your services, how many concurrent users you have you might need more or less ressources than what you initially purchased </li>
<li>Nominal operating parameters: range of RAM and CPU use, temperatures and so on in which your service performs as expected </li>
</ul>
<br><br>
The first item is fixed and only linked to your financial constraints. The other two are constantly evolving and thus must be <b>monitored</b>.
<h2>How do I do it?</h2>
How you monitor your systems can vary based on your technical requirements. It can be as simple as logging in once a week, check the output of some diagnostic command and calling it a day. <br>
This will give you a snapshot but you will miss a lot of important information.
<br><br>
You can also set up a complicated system that reports current metrics, trends and gives you capacity planning alerts
based on the data obtained!
You will have to find the middle-ground yourself, this article will propose one that you can tweak whichever way you need.
<h2>Risks of doing it improperly</h2>
Accessing your server for monitoring purposes is, from a risk perspective, pretty much the same as doing any other administration task or interacting with the services hosted therein. If done improperly (say logging in over the clearweb from your home address) you've just given anyone looking an undeniable link between your overt identity and your clandestine activities. (which should never happen since you're supposed to <a href="../internetsegmentation/index.html">segment your internet uses</a>)<br><br>
A <b>fail-closed</b> system is what you should strive for: opsec best practices should be the default and if there's a technical issue preventing you from following them (attack on tor, flaky network, client or server-side misconfiguration) the system should prevent access at all in order to keep you safe.
<h2>What if I don't monitor my Servers ?</h2>
If you don't properly monitor your infrastructure you will face the following consequences sooner or later:
<ul>
<li>service instability: you won't notice when things start going awry</li>
<li>costs overrun: you will end up paying more than you need to in order to deliver the same service</li>
<li>undetected attacks: attacks that impact your services can go unnoticed when the cues (eg: high RAM consumption from a cryptojacking) are not picked up</li>
<li>And lastly, if you are going to run a sensitive service on a remote server, it will anyway be on borrowed time as <a href="../cloud_provider_adversary/index.html">we have explained previously</a>, therefore you need to be able to easily detect whenever there is a downtime on one of the servers, while at the same time maintaining your anonymity.</li>
</ul>
</p>
</div>
</div>
</div><!-- /row -->
</div> <!-- /container -->
</div><!-- /grey -->
</div><!-- /white -->
<!-- +++++ Second Post +++++ -->
<div id="anon2">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2">
<h1><b>Risks</b></h1>
Whenever you connect to your server, such as for monitoring or other administrative tasks, if you do so without going through Tor, then the cloud provider knows that you are the one connecting to that server. Even when using SSH you will leave a trail of metadata all the way back to your access point. That might be enough to get your door busted down the line if you intend on hosting anything sensitive on that server.
<br><br>
In the following part of the post we will look into how to set up advanced monitoring tools so you don't have to keep an eye on a bunch of tmux sessions with glances/top open in order to know the behaviour of your systems over time.
<br><br>
This tutorial will assume that you have acquired servers anonymously via non-KYC cloud providers, and that you are only accessing them anonymously through tor
See <a href="../anonymousremoteserver/index.html">this article</a> if you have not already.
<br>
<br>
...
<br>
...
<br>
Done? Let's proceed.
<br>
</div>
</div><!-- /row -->
</div> <!-- /container -->
</div><!-- /grey -->
<div id="anon3">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2">
<p>
<h1><b>Target Architecture</b></h1>
First, let's have a look at the network topology we'll be building:
<br>
<img src="architecture.png"/>
<br>
<ul>
<li>Our whonix workstation will connect through tor to a central monitoring server in order to access the grafana dashboard containing our monitoring data.</li>
<li>Our monitoring server will itself connect through tor to the target monitored servers using prometheus</li>
</ul>
<h1><b>Setting up the central monitoring server</b></h1>
First you want to set up your central monitoring server. For ease of use and better performance we are going to colocate the prometheus collector along with grafana.
<br>
<h2>Required installation</h2>
To get started we need the following software on the machine:
<br>
<ul>
<li>Tor: anonymize traffic</li>
<li>prometheus: aggregate metrics</li>
<li>prometheus-node-exporter: export local server metrics</li>
<li>docker: to run grafana</li>
</ul>
<img src="install.png"/>
<h2>Tor Configuration</h2>
<h3>On the target server to be monitored</h3>
run the following as root to create a hidden service for the prometheus collector
<h2><b>Configuring node-exporter on the remote servers</b></h2> </br> </br>
<p>Next, we're going to configure node-exporter on the servers that we want to monitor:</p>
<pre><code class="nim">
apt update
apt install prometheus-node-exporter tor
systemctl stop tor #stop the tor service
[ Wonderland ] [ /dev/pts/19 ] [/srv/node-exporter]
→ cat docker-compose.yml
version: '3.7'
mkdir -p /var/lib/tor/onion/prometheus/authorized_clients #create the client auth keys folder to store our second layer of authentication
chmod 400 -R /var/lib/tor/prometheus #set restrictive file permissions
services:
vi /etc/tor/torrc #edit the torrc file to add content
node-exporter:
container_name: node-exporter
image: quay.io/prometheus/node-exporter:latest
#command:
#- '--path.rootfs=/host'
network_mode: host
pid: host
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
- /:/host:ro,rslave
- ./web.yml:/etc/prometheus/web.yml
command:
- '--path.rootfs=/host'
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- --collector.filesystem.ignored-mount-points
- "^/(sys|proc|dev|host|etc|rootfs/var/lib/docker/containers|rootfs/var/lib/docker/overlay2|rootfs/run/docker/netns|rootfs/var/lib/docker/aufs)($$|/)"
- "--web.config.file=/etc/prometheus/web.yml"
restart: always
deploy:
mode: global
cat /etc/tor/torrc
AutomapHostsSuffixes .onion,.exit
DataDirectory /var/lib/tor
SOCKSPort 127.0.0.1:9050 IsolateDestAddr
HiddenServiceDir /var/lib/tor/onion/prometheus
</pre></code>
<p>Now that docker-compose.yml is written, we need to write a small python script to hash the basicauth password:</p>
<pre><code class="nim">
[ Wonderland ] [ /dev/pts/19 ] [/srv/node-exporter]
→ vim gen-pass.py
[ Wonderland ] [ /dev/pts/19 ] [/srv/node-exporter]
→ cat gen-pass.py
import getpass
import bcrypt
#sudo apt install python3-bcrypt
password = getpass.getpass("password: ")
hashed_password = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt())
print(hashed_password.decode())
[ Wonderland ] [ /dev/pts/19 ] [/srv/node-exporter]
→ sudo apt install python3-bcrypt -y
[ Wonderland ] [ /dev/pts/19 ] [/srv/node-exporter]
→ python3 gen-pass.py
password: P@SSW0RD
$2b$12$AZg14Yp.hvDLk/iaYk9.ReqXyfonW94cwqzzxewZDWzTdAQZFo3zy
</pre></code>
<p>now with the hashed password, we can write the web.yml config file that the node exporter will use:</p>
<pre><code class="nim">
[ Wonderland ] [ /dev/pts/19 ] [/srv/node-exporter]
→ vim web.yml
[ Wonderland ] [ /dev/pts/19 ] [/srv/node-exporter]
→ cat web.yml
basic_auth_users:
admin: $2b$12$AZg14Yp.hvDLk/iaYk9.ReqXyfonW94cwqzzxewZDWzTdAQZFo3zy
</pre></code>
<p>And now finally we can pull the container image and activate it:</p>
<pre><code class="nim">
[ Wonderland ] [ /dev/pts/19 ] [/srv/node-exporter]
→ docker-compose pull ; docker-compose up -d
</pre></code>
<p>Then we also make sure that the node-exporter port 9100 is accessible via the onion domain, as otherwise we can't access it while maintaining the serverside anonymity: </p>
<pre><code class="nim">
[ Wonderland ] [ /dev/pts/19 ] [/srv/node-exporter]
→ cat /etc/tor/torrc | grep 9100
HiddenServicePort 9100 127.0.0.1:9100
tor-client-auth-gen
private_key=descriptor:x25519:DBQW3GP5FCN2KQBDKTDKDAQUQWBEGBZ5TFYJE4KTJFBUOJPKYZBQ #paste this key to your local machine as your prometheus node will need it
echo "descriptor:x25519:6HDNHLLKIFNU5Q6T75B6Q3GBYDO5ZF4SQUX7EYDEKWNLPQUWUBTA" > /var/lib/tor/onion/prometheus/0.auth
[ Wonderland ] [ /dev/pts/19 ] [/srv/node-exporter]
→ systemctl restart tor@default
chown debian-tor:debian-tor -R /var/lib/tor # make tor owner of this folder
systemctl start tor #restart tor
systemctl status tor #check that everything works
cat /var/lib/tor/onion/prometheus/hostname
[clientaddr].onion
</code></pre>
What's that tor-client-auth-gen you ask? In order to protect this critical service from attacks that could be done against the grafana servers or from stolen credentials we need more than just security by obscurity
(relying on the attacker not knowing our hidden service address).
<br>
When a client tries to connect to an onion service they request a server descriptor from a tor directory server that gives them a path to a rendez-vous point where they will be able to talk to each other. The keys we just created will be used to encrypt this descriptor. Without the proper private key, even with the onion service address, an attacker won't be able to connect to it because they won't be able to find the rendez-vous point.
<br>
<br>
This is better than basic-auth for the following reasons:
<ul>
<li>More resistant to bruteforce attacks</li>
<li>Also protects against flaws in your application itself</li>
<li>Also protects you from fingerprinting attacks as no trafic can reach you without the required secret key</li>
</ul>
<br>
<br>
<h3>On the central monitoring server</h3>
The prometheus collector will only be accessed locally by grafana so it doesn't need to be accessible over tor. Grafana, on the other hand, does.
<br>
Let's start with tor's configuration, use the following commands as root:
<br>
<pre><code class="nim">
sudo systemctl stop tor #stop the tor service
mkdir -p /var/lib/tor/auth_keys #create the client auth keys folder to store our second layer of authentication
mkdir -p /var/lib/tor/onion/grafana #create the client auth keys folder to store our second layer of authentication
chmod 400 -R /var/lib/tor/auth_keys #set restrictive file permissions
#line below will allow your aggregator to connect to your monitored server. Without it no requests can even reach it
echo "[prometheusclientaddr].onion:descriptor:x25519:DBQW3GP5FCN2KQBDKTDKDAQUQWBEGBZ5TFYJE4KTJFBUOJPKYZBQ" > /var/lib/tor/auth_keys/prometheus_server.auth_private
chmod 400 -R /var/lib/tor/onion #set restrictive file permissions
vi /etc/tor/torrc #edit the torrc file to add content
cat /etc/tor/torrc
AutomapHostsSuffixes .onion,.exit
DataDirectory /var/lib/tor
SOCKSPort 127.0.0.1:9050 IsolateDestAddr
HiddenServiceDir /var/lib/tor/onion/grafana
HiddenServicePort 80 127.0.0.1:3000
ClientOnionAuthDir /var/lib/tor/auth_keys
tor-client-auth-gen
private_key=descriptor:x25519:YCPURSYN4FL4QKQSXFTGLYNBHOVVRCQYRZLFHMZFCUFU5R6DCRMQ
public_key=descriptor:x25519:UUQW4LIO447WRQOSRSNDXEW5NZMSR3CYOP65ZIFWH6G2PUKWV5WQ
echo "YCPURSYN4FL4QKQSXFTGLYNBHOVVRCQYRZLFHMZFCUFU5R6DCRMQ" > ~/mygrafana_auth_key
echo "descriptor:x25519:UUQW4LIO447WRQOSRSNDXEW5NZMSR3CYOP65ZIFWH6G2PUKWV5WQ" > /var/lib/tor/onion/grafana/0.auth
chown debian-tor:debian-tor -R /var/lib/tor # make tor owner of this folder
systemctl start tor #restart tor
systemctl status tor #check that everything works
</code></pre>
And that's all you'll need! one hidden service for grafana. <br> You'll find your hostname in /var/lib/tor/onion/grafana/hostname.
<h2>Prometheus server configuration on the central monitoring server</h2>
Clean and simple: we scrape our server every 10s for new data, configure a proxy URL so scraping happens over tor, using our socksport and configure ou scraping targets.
<br>
<pre><code class="nim">
vi /etc/prometheus/prometheus.yml
cat /etc/prometheus/prometheus.yml
alerting:
alertmanagers: []
global:
scrape_interval: 10s
remote_read: []
remote_write: []
scrape_configs:
- job_name: remote-nodes
proxy_url: socks5h://localhost:9050
static_configs:
- labels: {}
targets:
- <b>[clientaddr].onion:9100</b>
- job_name: local-node
static_configs:
- labels: {}
targets:
- localhost:9100
</code></pre>
<br>
This configuration will make the central monitoring server behave in the following way:
<ul>
<li>Scrap itself directly to collect its own data (prometheus is only exposed on loopback for this)</li>
<li>Scrap the target monitored server through tor via the socks proxy</li>
</ul>
<h2>Grafana configuration on the central monitoring server</h2>
Let's start grafana and make it available: as root, as tor is already configured we can then access it through our torbrowser
<br>
<pre><code class="nim">
docker run -d -p 127.0.0.1:3000:3000 --name=grafana grafana/grafana
</code></pre>
</p>
</div>
</pre></code>
<p>And now from there the server should be monitored as intended.</p>
</div>
</div><!-- /row -->
</div> <!-- /container -->
</div><!-- /grey -->
</div><!-- /white -->
<div id="anon2">
<!-- +++++ Second Post +++++ -->
<div id="anon1">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2">
<p>
<h2><b>Visualizing the result</b></h2> </br> </br>
<p>Now that the remote server's node-exporter is reachable via it's onion domain, we can see that the resource usage started to appear in the dashboard that we setup earlier:</p>
<h1> <b>Connecting to our grafana instance</b></h1>
On your monitoring server you can find your hostname at /var/lib/tor/grafana/hostname. Use it in the tor browser to reach your instance. You will be prompted for your private key, you can find it where you saved it earlier ~/mygrafana_auth_key, paste the part after
<img src="grafana_login.png"/>
<br>
<h1><b>Configuring the data sources</b></h1>
Next we need to tell grafana to use prometheus as a data source: <br>
<img src="add_datasource.png"/>
<br>
<br>
Now, let's configure it (specifying localhost:9090 as the API port)<br>
<br>
<img src="datasource_config.png"/>
<br>
<br>
<br>
And Voila! we have simple system monitoring over tor in a dashboard: <br>
<img src="example_dashboard.png"/>
</p>
</div>
<img src="20.png" class="imgRz">
<p>In this case, in the event of the remote server being seized, the adversary would only be able to see that it has been queried to from a tor exit node IP. This is to make sure that your anonymous infrastructure is able to sustain having each of it's individual servers being taken down, as long as they are not all taken down at once.</p>
<p>And that's it ! We can now monitor our own remote servers, while maintaining serverside anonymity at the same time. </p>
</div>
</div><!-- /row -->
</div> <!-- /container -->
</div><!-- /grey -->
<div id="anon3">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2">
<h1><b>Conclusion</b></h1>
In this article we saw why and how you need to implement anonymous server monitoring for your infrastructure. If you are running hidden services with any form of sensitive data stored on them, having them under constant monitoring is a must because you need to detect downtimes quickly, without compromising your identity or the rest of your infrastructure.
</div>
</div><!-- /row -->
</div> <!-- /container -->
</div><!-- /grey -->
</div><!-- /white -->
<!-- +++++ Footer Section +++++ -->
@ -404,9 +441,8 @@ Let's start grafana and make it available: as root, as tor is already configured
</div><!-- /col-lg-4 -->
<div class="col-lg-4">
<h4>About Mulligan Security</h4>
<p style="word-wrap: break-word;"><u>Donate XMR:</u><br>86NCojqYmjwim4NGZzaoLS2ozbLkMaQTnd3VVa9MdW1jVpQbseigSfiCqYGrM1c5rmZ173mrp8RmvPsvspG8jGr99yK3PSs</p></br><p><u>Contact:</u> mulligansecurity@riseup.net <br><a href="http://msec2nnqtbwh5c5yxpiswzwnqperok5k33udj7t6wmqcleu3ifj34sqd.onion">website</a><br><a href="https://simplex.chat/contact#/?v=2-7&smp=smp%3A%2F%2FiZJOs1BYKxD2nEndBtacHlBP-bNKv3gywICYPZZjXXE%3D%40chatnedvznvcnsovrm3e6jrgt6pkpai5i3rgslrrxlnv352ardboebid.onion%2FtT5R0tQWBzJPAkjvH-wai4PnpfTor89R%23%2F%3Fv%3D1-3%26dh%3DMCowBQYDK2VuAyEA_7oNMJAjBrt210CSc2LEIZJh5BFizPx7JUYFCmj8p1k%253D">SimpleX</a></p>
<h4>About nihilist</h4>
<p style="word-wrap: break-word;"><u>Donate XMR:</u> 8AUYjhQeG3D5aodJDtqG499N5jXXM71gYKD8LgSsFB9BUV1o7muLv3DXHoydRTK4SZaaUBq4EAUqpZHLrX2VZLH71Jrd9k8</p></br>
</div><!-- /col-lg-4 -->
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB