<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss/styles.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>aucubin.de</title><description>Random articles about self-hosting and programming</description><link>https://aucubin.de/</link><item><title>Making my services available from the outside</title><link>https://aucubin.de/blog/making-my-services-available-from-the-outside/</link><guid isPermaLink="true">https://aucubin.de/blog/making-my-services-available-from-the-outside/</guid><pubDate>Sun, 18 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been selfhosting my services for a couple of years with dedicated servers from different hosting providers, but lately I decided to move all my services on a machine inside my server rack at home. As I didn&apos;t want to make my IP publicly known I decided against opening ports on my router and instead used a setup consisting of a VPS and an VPN.&lt;/p&gt;
&lt;p&gt;I also didn&apos;t want to use Cloudflare as I wanted to be independent of the provider that is used to make everything available outside. With the setup I have now I can basically switch the VPS and only need to do some small setup to get it working again.&lt;/p&gt;
&lt;h2&gt;What is needed?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A VM or physical machine where your services will be hosted&lt;/li&gt;
&lt;li&gt;A VPS from an hosting provider. I use a CAX11 Cloud Server from Hetzner.&lt;/li&gt;
&lt;li&gt;Wireguard as VPN to connect the VM and the VPS together.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Setting up the VPS&lt;/h2&gt;
&lt;p&gt;I bought the CAX11 and selected Debian as operation system. Afterwards I did my normal base setup by installing fail2ban, switching iptables with nftables and creating a dedicated user instead of using root to log into ssh.&lt;/p&gt;
&lt;h2&gt;Setting up the VPN&lt;/h2&gt;
&lt;p&gt;As VPN I&apos;m using Wireguard as it is very easy to install and configure.&lt;/p&gt;
&lt;p&gt;I installed wg-quick by running &lt;code&gt;sudo apt install wireguard-tools&lt;/code&gt; on the VPS. In order to connect two machines with wireguard you need to create three things on each machine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;public key&lt;/li&gt;
&lt;li&gt;private key&lt;/li&gt;
&lt;li&gt;pre-shared key&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The keys will be used to authenticate the wireguard clients with each other and encrypt the traffic between them. They will need to be created on both clients, which in my case is the VPS and the service machine.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;wg genkey | (umask 0077 &amp;amp;&amp;amp; tee peer_VPS.key) | wg pubkey &amp;gt; peer_VPS.pub&lt;/code&gt; to generate both public (peer_A.pub) and private (peer_A.key) key.&lt;/p&gt;
&lt;p&gt;Then run &lt;code&gt;wg genpsk &amp;gt; peer_VPS-peer_service.psk&lt;/code&gt; to generate the pre-shared key. After creating the keys on both machines you should have the following files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;peer_VPS.key&lt;/li&gt;
&lt;li&gt;peer_VPS.pub&lt;/li&gt;
&lt;li&gt;peer_service.key&lt;/li&gt;
&lt;li&gt;peer_service.pub&lt;/li&gt;
&lt;li&gt;peer_VPS-peer_service.psk&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The next step is to create an wireguard config file for the VPS under &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt;. Mine looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Interface]
Address=192.168.10.1/24
ListenPort=51820
PrivateKey=PRIVATE_KEY &amp;lt;-- copy the content of peer_VPS.key here
PreUp=sysctl -w net.ipv4.ip_forward=1
PreUp=sysctl -w net.ipv6.conf.all.forwarding=1
PostDown=sysctl -w net.ipv4.ip_forward=0
PostDown=sysctl -w net.ipv6.conf.all.forwarding=0
MTU=1280

# service
[Peer]
PublicKey=PUBLIC_KEY &amp;lt;-- copy the content of peer_service.pub here
PresharedKey=PRESHARED_KEY &amp;lt;-- copy the content of peer_VPS-peer_service.psk here
AllowedIPs=192.168.10.2/32
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should then be able to start the VPN by running &lt;code&gt;systemctl enable --now wg-quick@wg0&lt;/code&gt;. After running &lt;code&gt;wg&lt;/code&gt; in the command line you should seen an output like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface: wg0
  public key: PUBLIC_KEY &amp;lt;-- should be the value of peer_VPS.pub
  private key: (hidden)
  listening port: 51820

peer: PUBLIC_KEY &amp;lt;-- should be the value of peer_service.pub
  preshared key: (hidden)
  endpoint: your-ip
  allowed ips: 192.168.10.2/32
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As the other side of the VPN is not yet running you will not be able to ping the other side, so we still need to set it up.&lt;/p&gt;
&lt;p&gt;Create the Wireguard configuration in &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt; on the other client (in my case the service machine) like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Interface]
ListenPort = 51821
PrivateKey = PRIVATE_KEY &amp;lt;-- should be the value of peer_service.key
Address = 192.168.10.2/24

[Peer]
PublicKey = PUBLIC_KEY &amp;lt;-- should be the value of peer_VPS.pub
PresharedKey = PRESHARED_KEY &amp;lt;-- copy the content of peer_VPS-peer_service.psk here
AllowedIPs = 192.168.10.1/32
Endpoint = your-ip:51820 &amp;lt;-- your-ip should be the ip address of the VPS
PersistentKeepalive = 25
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see that this configuration file has an endpoint and PersistentKeepalive configured. In order for wireguard to work the machines need to exchange traffic with each other, which normally happens something gets routed over the wg0 interface.&lt;/p&gt;
&lt;p&gt;As the service machine is not reachable from the outside, it needs to connect to the VPS. For that reason we use endpoint to specify to which ip and port the service machine needs to connect and &lt;code&gt;PersistentKeepalive=25&lt;/code&gt; to have the machine send a keep-alive message every 25s to the vps, in order to keep the connection running.&lt;/p&gt;
&lt;p&gt;Now you can start wireguard on the service machine by running &lt;code&gt;systemctl enable --now wg-quick@wg0&lt;/code&gt; and you should be able to ping the machines after some seconds. Your wg output should also now look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface: wg0
  public key: PUBLIC_KEY &amp;lt;-- should be the value of peer_VPS.pub
  private key: (hidden)
  listening port: 51820

peer: PUBLIC_KEY &amp;lt;-- should be the value of peer_service.pub
  preshared key: (hidden)
  endpoint: your-ip
  allowed ips: 192.168.10.2/32
  latest handshake: 20 seconds ago
  transfer: 900.22 MiB recived, 211.6 MiB sent
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you can&apos;t ping the machines check if the ip range used (here 192.168.10.0/24) is in use anywhere or a firewall is enabled on the machines. To debug you can ping on one machine and run &lt;code&gt;tcpdump -i wg0&lt;/code&gt; on the other machine to check if traffic even arrives on the other machine.&lt;/p&gt;
&lt;p&gt;If it doesn&apos;t it&apos;s also possible that your keys don&apos;t match - try to regenerate them in this case.&lt;/p&gt;
&lt;h2&gt;Making the services available from the outside&lt;/h2&gt;
&lt;p&gt;In my case I run my services in docker compose on the service machine and use traefik, so my docker-compose.yml of traefik looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: &quot;3.3&quot;

services:
  traefik:
    image: &quot;traefik:latest&quot;
    container_name: &quot;traefik&quot;
    restart: always
    network_mode: &quot;host&quot;
    command:
      - &quot;--providers.docker=true&quot;
      - &quot;--providers.docker.exposedbydefault=false&quot;
      - &quot;--entrypoints.websecure.address=:443&quot;
      - &quot;--certificatesresolvers.le.acme.tlschallenge=true&quot;
      - &quot;--certificatesresolvers.le.acme.storage=/acme.json&quot;
    volumes:
      - &quot;$PWD/acme.json:/acme.json&quot;
      - &quot;/var/run/docker.sock:/var/run/docker.sock:ro&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An simple docker-compose.yml of string-is looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: &apos;3&apos;

services:
  string-is:
    image: daveperrett/string-is
    restart: always
    container_name: string-is
    labels:
      - &quot;traefik.enable=true&quot;
      - &quot;traefik.http.routers.string-is.rule=Host(`string-is.aucubin.de`)&quot;
      - &quot;traefik.http.routers.string-is.entrypoints=websecure&quot;
      - &quot;traefik.http.routers.string-is.tls.certresolver=le&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Traefik handles TLS and certificates, so the only thing open is to connect the 443/tcp port of the VPS with the 443/tcp port of the service machine. You could forward the ports directly with nftables (or iptables), but I decided against it and use HAProxy in order to have better logging and be able use load-balancing if needed.&lt;/p&gt;
&lt;p&gt;The first thing is to install haproxy on the VPS by running &lt;code&gt;sudo apt install haproxy&lt;/code&gt;. There will be a default haproxy.cfg shipped under &lt;code&gt;/etc/haproxy/haproxy.cfg&lt;/code&gt; to which I added the following block:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;frontend main_https_listen
	bind	:443 v4v6
	bind	:::443 v6only
         mode	tcp
	option	tcplog
         default_backend service

backend service
	mode	tcp
	balance	source
	option	tcp-check
	server	service 192.168.10.2:443 check
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will basically do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The frontend block tells HAProxy to listen on 443/tcp (both IPv4 and IPv6) in TCP Proxy mode. This means HAProxy will just forward TCP traffic to the backend, other than the HTTPS mode where HAProxy would run HTTP and you can adjust headers or enable TLS on it.&lt;/li&gt;
&lt;li&gt;The backend block tells HAProxy that the backend service has 192.168.10.2:443 as endpoint to forward to and to check whether its available by trying to open a TCP connection on the backend.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After adjusting the configuration file you can restart haproxy with &lt;code&gt;systemctl restart haproxy&lt;/code&gt; and after some seconds you should see that the service is up when running &lt;code&gt;journalctl -b -u haproxy&lt;/code&gt;. You then just need to wait until Traefik could fetch TLS Certificates from Let&apos;s Encrypt and you are ready to go. You should now be able to connect to your services without being in your internal network.&lt;/p&gt;
</content:encoded></item><item><title>Building a weather station with an ESP8266</title><link>https://aucubin.de/blog/weather-station-with-esp8266/</link><guid isPermaLink="true">https://aucubin.de/blog/weather-station-with-esp8266/</guid><pubDate>Sat, 28 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Some time ago I decided that I wanted to track some weather data like temperature, humidity and CO2 in my apartment. The data should be imported into a InfluxDB so that I can use Grafana in order to create diagrams based on this data. After looking over the pre-built options I decided to just built the sensor from scratch with a ESP8266 and a couple of sensors.&lt;/p&gt;
&lt;h2&gt;What is needed?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ESP8266&lt;br /&gt;
I decided to use a &lt;a href=&quot;https://components101.com/development-boards/nodemcu-esp8266-pinout-features-and-datasheet&quot;&gt;NodeMCU&lt;/a&gt; as it comes with a USB port that has a serial programmer directly embedded.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.haoyuelectronics.com/Attachment/AM2301/AM2301.pdf&quot;&gt;DHT21 (AM2301)&lt;/a&gt; Temperature + Humidity Sensor&lt;br /&gt;
This sensor is used to track the temperature and the humidity in the room.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.adafruit.com/product/3709&quot;&gt;SGP30&lt;/a&gt; CO2 Sensor&lt;br /&gt;
This sensor is used to calculate the CO2 and TVOC level in the room.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Preparation&lt;/h2&gt;
&lt;h3&gt;SGP 30&lt;/h3&gt;
&lt;p&gt;The first thing to do is to solder the header pins onto the SGP30. Once that is done the first thing to do is to connect the SGP30 to the ESP8266 and use the test programm from Adafruit in order to calibrate the co2 sensor by calculating the baseline. I used the following pins for the connection:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SGP 30 Pin&lt;/th&gt;
&lt;th&gt;ESP8266 Pin&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VIN&lt;/td&gt;
&lt;td&gt;3V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SCL&lt;/td&gt;
&lt;td&gt;D1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SDA&lt;/td&gt;
&lt;td&gt;D2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;There are two options to do the calibration:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use the Arduino IDE to install the sgp30test library, run it and then use the serial monitor to get the right data out. This is described in the article from &lt;a href=&quot;https://learn.adafruit.com/adafruit-sgp30-gas-tvoc-eco2-mox-sensor?view=all#arduino-code&quot;&gt;Adafruit&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Download the example from the &lt;a href=&quot;https://github.com/adafruit/Adafruit_SGP30/blob/master/examples/sgp30test/sgp30test.ino&quot;&gt;SGP30 Github&lt;/a&gt; and use your preferred tooling to upload it to the ESP8266.&lt;br /&gt;
As I will use PlatformIO for the code anyways I will show the second option.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;PlatformIO&lt;/h4&gt;
&lt;p&gt;An installation guide for PlatformIO can be found on their &lt;a href=&quot;https://platformio.org/install/ide?install=vscode&quot;&gt;page&lt;/a&gt;. I will use visual studio code so it basically comes down to installing the PlatformIO extension from the visual studio code store.&lt;/p&gt;
&lt;p&gt;The first thing is to create a new Project in PlatformIO with the following settings:&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;The next step is to add the Adafruit SGP30 library from the Library Page:&lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;You will directly find the sgp30test example in the library page so you can copy it from there (if not get it from the github link above) and replace the src/main.cpp with the sgp30test example. Then you can connect your NodeMCU to your computer and flash the software by using the &quot;PlatformIO: Upload icon&quot; in the bottom bar of visual studio code .&lt;br /&gt;
Before you can then use the Serial Monitor you need to adjust the Serial Monitor Speed to 115200 baud. That can be done by adding &lt;code&gt;monitor_speed = 115200&lt;/code&gt; to the platformio.ini inside the project. Afterwards you can click on the &quot;PlatformIO: Serial Monitor&quot; icon and should be able to see data flooding in:&lt;br /&gt;
&lt;br /&gt;
Now you just need to open a window and wait for 10-15 minutes while the baseline gets calibrates. After this time note down the baseline values that get printed (in the screenshot they are 0x8E42 and 0x9265).&lt;/p&gt;
&lt;h3&gt;DHT21&lt;/h3&gt;
&lt;p&gt;The DHT21 does not need any solder and can directly be connected to the NodeMCU. I used the following pins:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DHT21 Pin&lt;/th&gt;
&lt;th&gt;ESP8266 Pin&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VDD&lt;/td&gt;
&lt;td&gt;3V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data&lt;/td&gt;
&lt;td&gt;D6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;MQTT&lt;/h3&gt;
&lt;p&gt;To push the data from the NodeMCU into InfluxDB I decided to use MQTT as I can attach Telegraf to MQTT and also use MQTT to display the data on other devices. In order to publish data to MQTT a MQTT broker like &lt;a href=&quot;https://mosquitto.org/&quot;&gt;Eclipse Mosquitto&lt;/a&gt; is needed. As I use Docker for all my self-hosting needs I used the following docker-compose.yml to create a instance of Eclipse Mosquitto on my server:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: &quot;3&quot;

services:
  mosquitto:
    image: eclipse-mosquitto:latest
    container_name: mosquitto
    volumes:
      - $PWD/config:/mosquitto/config
      - mosquitto-data:/mosquitto/data
    ports:
      - 1883:1883
    restart: always

volumes:
    mosquitto-config:
    mosquitto-data:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default mosquitto does not use authentication. To fix this add &lt;code&gt;password_file /mosquitto/config/password_file&lt;/code&gt; to config/mosquitto.conf inside the folder where the docker file is defined. You can then add users with &lt;code&gt;docker exec -it mosquitto mosquitto_passwd /mosquitto/config/password_file &amp;lt;username&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Flashing&lt;/h2&gt;
&lt;p&gt;Now that everything is set up you can get the &lt;a href=&quot;https://github.com/aucubin/esp8266-weatherstation&quot;&gt;code&lt;/a&gt; from the repository. A couple of things need to be changed.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There is a cred.h.template in the include folder.&lt;br /&gt;
This file contains secrets like the wifi password for the ESP8266 and the credentials for the MQTT broker.&lt;br /&gt;
Rename the file to cred.h and enter the values for your wifi and MQTT broker.&lt;/li&gt;
&lt;li&gt;The setup method of the src/main.cpp contains a call to sgp.setIAQBaseline. The arguments are the baseline values that you gathered from the sgp30test util in the preparation.&lt;/li&gt;
&lt;li&gt;If you want to check the topic names for the data in MQTT you need to change the second argument of the Adafruit_MQTT_Publish objects on top of src/main.cpp.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Afterwards you can flash the code on the ESP8266. You can use the serial monitor to check if the ESP can correctly connect to your wifi and mqtt broker.&lt;/p&gt;
&lt;h2&gt;InfluxDB&lt;/h2&gt;
&lt;p&gt;The MQTT data can easily be imported into InfluxDB by using &lt;a href=&quot;https://www.influxdata.com/time-series-platform/telegraf/&quot;&gt;Telegraf&lt;/a&gt; and using the &lt;a href=&quot;https://github.com/influxdata/telegraf/tree/master/plugins/inputs/mqtt_consumer&quot;&gt;mqtt_consumer&lt;/a&gt; input plugin.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[[inputs.mqtt_consumer]]
  servers = [&quot;tcp://broker_url:1883&quot;]

  topics = [
    &quot;weather/aucubin/temp&quot;,
    &quot;weather/aucubin/humidity&quot;,
    &quot;weather/aucubin/tvoc&quot;,
    &quot;weather/aucubin/eco2&quot;
  ]

  client_id = &quot;client_name&quot;

  username = &quot;username&quot;
  password = &quot;password&quot;

  data_format = &quot;value&quot;
  data_type = &quot;float&quot;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item></channel></rss>