Categories
Linux Python Weather

Python service to consume Ambient Weather API data

Python service to consume Ambient Weather API data

Continuing from the last post (Handling data from Ambient Weather WS-2902C to MQTT), we have a working script that reads the data coming from the Ambient Weather WS-2902 base station (Ambient Weather API) and sends it to a MQTT broker. In this post, we will turn that script into a Linux service that starts at boot and runs forever. This type of thing is perfect for Linux. Non-server Windows versions aren’t really appropriate for this since they reboot often with updates and such. If you want to run Windows Server, more power to you, you probably don’t need this guide. We will thus be focusing on Linux for consuming the Ambient Weather API. A Raspberry Pi is a perfect device for this (plenty of power, as big as a credit card, and less than $100).

Linux Services

Linux is made up of many individual components. Each component is designed to handle a single task (more or less). This differs from Windows where there are large executables/processes that handle many tasks. We will be taking advantage of Linux’s single task theory to add a new service that will run the Python script for ingesting and consuming the Ambient Weather API. In this instance for Austin’s Nerdy Things, the weather data is being provided by the Ambient Weather WS-2902C weather station.

Creating the service

For Ubuntu/Debian based distributions, service files live under /etc/systemd/system. Here is a list of services on the container I’m utilizing.

List command:

ls -1 /etc/systemd/system
dbus-org.freedesktop.resolve1.service
dbus-org.freedesktop.timesync1.service
default.target.wants
getty.target.wants
graphical.target.wants
multi-user.target.wants
socket.target.wants
sockets.target.wants
sshd.service
sys-kernel-debug.mount
sysinit.target.wants
syslog.service
timers.target.wants

Since this is a LXC container, there aren’t many services. On a standard Raspbian or Ubuntu full install, there will be 100+.

We will be creating a new service file using the nano text editor:

nano /etc/systemd/system/ambient-weather-api.service

In this file we need to define our service. The After lines mean don’t start this up until the listed services are running. The rest is pretty straight forward. I’m not 100% sure what the WantedBy line is for but it’s present in most of my service files. The contents of ambient-weather-api.service are as follows:

[Unit]
Description=Python script to ingest Ambient Weather API data
After=syslog.target
After=network.target

[Service]
Type=simple
User=root
Group=root
ExecStart=/usr/bin/python3 /srv/ambient-weather-api/main.py
ExecStartPre=/bin/sleep 5
Restart=always
RestartSec=5s
# Give the script some time to startup
TimeoutSec=10

[Install]
WantedBy=multi-user.target

Save the file. The service definition is looking for Python at /usr/bin/python3 and the python script at /srv/ambient-weather-api/main.py. You will probably be fine with the Python executable, but be sure to mv or cp the main.py file to /srv/ambient-weather-api/main.py.

We will need to reload the service definitions:

systemctl daemon-reload

Now we can start the service:

systemctl start ambient-weather-api.service

And verify it is running:

systemctl status ambient-weather-api.service
Ambient Weather API service status
Ambient Weather API service status

The above screenshot shows it is indeed running and active. It is still showing the print messages in the log as well, which we should disable by commenting out the lines by adding a # in front of the print line. In this case, it is coming from line 56 (the status check in the publish function).

#print(f"Sent {msg} to topic {topic}")

 

 

 

 

Categories
Blog Admin Linux

Securing this WordPress blog from evil hackers!

In my introduction post, I said I would write about topics in order of interest. Securing WordPress blogs from hackers isn’t exactly fun or interesting but it is very necessary in this day and age. Hackers are constantly probing sites on the internet for insecurities. They’re constantly trying to log into WordPress sites with easily guessed passwords (hint: don’t use ‘password’ as your password). Here are some hints on how to secure WordPress blogs from hackers.

If you prefer a video version, check out my first ever YouTube video (!) covering this same content here – https://youtu.be/wKgm_684acM.

When I set this site up, the first 24 hours were pretty quiet. After that, the attacks started ramping up. I decided to take action and lock down access. There are three main things I did to secure this WordPress blog installation and VPS it is hosted on:

  1. Disable password-based SSH authentication for logins
  2. Install and enable Fail2Ban
  3. Install WordPress specific Fail2Ban filters

#1 – Disable password-based SSH authentication

Step 0 – Enable SSH Key Authentication

Before you disable password-based authentication, you need to enable SSH key based authentication. I have posted a SSH key tutorial here – SSH Key Tutorial.

Password-based SSH authentication

SSH stands for secure shell. It is how 99% of Linux/Unix servers on the public internet and private intranets are administered. There are two main methods of logging in with SSH: 1) password and 2) key. Password is pretty straight-forward and is what most people are familiar with. You have a username and password. If you enter the right password for the username, you get in. Hackers are constantly testing common usernames (root, admin, user, guest) with common passwords (password, password1, password123, test, etc.). Further – they aren’t testing just one combination of user/pass at a time, they keep trying passwords until they give up or are banned. I had my VPS for a few weeks before activating austinsnerdythings.com on it and here is a random sample starting a minute after midnight for about six minutes:
$sudo head -n 100 /var/log/auth.log.1

Feb 28 00:01:52 austinsnerdythings.com sshd[2265571]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=222.184.14.90 user=root
Feb 28 00:01:54 austinsnerdythings.com sshd[2265571]: Failed password for root from 222.184.14.90 port 45182 ssh2
Feb 28 00:01:54 austinsnerdythings.com sshd[2265571]: Received disconnect from 222.184.14.90 port 45182:11: Bye Bye [preauth]
Feb 28 00:01:54 austinsnerdythings.com sshd[2265571]: Disconnected from authenticating user root 222.184.14.90 port 45182 [preauth]
<snip>
Feb 28 00:04:59 austinsnerdythings.com sshd[2265587]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=139.198.121.63 user=root
Feb 28 00:05:02 austinsnerdythings.com sshd[2265587]: Failed password for root from 139.198.121.63 port 53437 ssh2
Feb 28 00:05:04 austinsnerdythings.com sshd[2265587]: Connection closed by authenticating user root 139.198.121.63 port 53437 [preauth]
Feb 28 00:06:06 austinsnerdythings.com sshd[2265591]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=212.64.38.8 user=root
Feb 28 00:06:07 austinsnerdythings.com sshd[2265591]: Failed password for root from 212.64.38.8 port 37354 ssh2
Feb 28 00:06:08 austinsnerdythings.com sshd[2265591]: Received disconnect from 212.64.38.8 port 37354:11: Bye Bye [preauth]
Feb 28 00:06:08 austinsnerdythings.com sshd[2265591]: Disconnected from authenticating user root 212.64.38.8 port 37354 [preauth]
<snip>
Feb 28 00:06:48 austinsnerdythings.com sshd[2265595]: Received disconnect from 49.88.112.118 port 37056:11: [preauth]
Feb 28 00:06:48 austinsnerdythings.com sshd[2265595]: Disconnected from 49.88.112.118 port 37056 [preauth]
Feb 28 00:06:56 austinsnerdythings.com sshd[2265589]: Connection reset by 49.88.112.118 port 53318 [preauth]
Feb 28 00:08:00 austinsnerdythings.com sshd[2265597]: Received disconnect from 49.88.112.118 port 61081:11: [preauth]
Feb 28 00:08:00 austinsnerdythings.com sshd[2265597]: Disconnected from authenticating user root 49.88.112.118 port 61081 [preauth]

Each login attempt is 3-4 lines, so that’s 10 attempts in 6 minutes. Also notice the repeating IP addresses – 49.88.112.118 tried 4 separate times to log in across 6 minutes!

Hackers try user/pass logins because they’re relatively easy. And they get lucky often enough it is worth it.

Key-based SSH authentication

The other method to logging in with SSH is via public/private key. How this works is you generate a public/private keypair. Then you put the contents of the public key on the server you want to log in to. When logging in, your SSH client says “hello, I am user austin and I have a key to login and here it is”! The public key that’s copied to the remote server looks like this:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuzcK6yIyqJabWprjaZZI9mXpVaSewoGZROcYTf/iB6OvJklIYmM/j/YHPWq1fV30QcGPpUBwKFk8DrJNn5bIk3fow67TVC0Wr2tWy7DDweTNUpk7L01MBRhjLG2xpO9RU9F4hDyzAFI4NcrSOb6J9FL6ItrfQS/LZ7H3IrmBGIjp4OooQOhR4iw5KFEdgvNgs8rAaxSl2FziTRrxhISTzkQY0BUMBkUNjsJid4x3rTXJ9UyUDYwN2/WMfzf9aGJdRzPLIiNKsxbDeTzC3vd8TCfFOUJ+hmS8gSOY0vhLS/1wQp91jR10FF4d67z9FTwAyh+o6uKJfmvNpTXIhN austin@EARTH

And the private key (that should never be shared! this a throwaway key) looks like this:

-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA5DZd/sSEHr9/Z74KF6t832ggMNc5I6aCm/2BV9HvU2P5tqeZ
uw8qU6keLLO8BCCqcmiCjJXT/9ZRxoaRCP8Zde9Aq/ElhmXbbpUuGRLicbt9HYcu
VCJmGP2GNhpAXdFlLU+efG3Alqu13Eynyhj47PB/JMj0BCWMASqkAI8h7ORNUbeQ
NHiSTPRdXBVEHa5CW/wvqOzH+sHdrclQNCWAWfOn7wRsS78YH2Dw9yK3bD4RKxqO
aIQ+UfKOl889/b/5E56I7Yup+VeN2o7PyV7+TOGrx+aKV7ax0ePBbrFjT1mXbQ/r
OkOEyFC4r1S5pduxjwDWA6mbHM+v6xl/V65YzwIDAQABAoIBABmDloioMdk6MaVI
ktpImuJjQs4TEdlRgWKtOeu2ldot4DoyjLZkIKhPzQbUZV3UxRmbY5USHyyIKoZW
fxqRYqhTwlg20qou8xRu60N0YAq1GmzVszFG00FR/tJHpxCWG4iwURi6MIDn26Iw
k8W9ev8KeDyFlvprtDZhLQq+9d0FByjLjD6mmeyhHTQXN7f+agUzQjx1pQMcecnV
9OuOqSgav0pmerzWGDWJDYtisIIyByoj1kMyYLKAAvBd2xtwv8rg9iA+TxKnvMCY
fFDMeRQ45F+SLEMqARDxeRGCqPZ0hFswLeVs/uYiAyhoY7eBYtvyIlx9b9tAWb3t
K+B0TDkCgYEA+a5iYXzTECk7xs6KbwI6Runo5n8J104yGLebaQkfA60LzuidaMqw
aPL2DEb9wbHK4oCqwIOXOPY2L4qO8+7VJeMl+g5oxelmHhkTMecjmWmnCO1gmwBq
1mNLEg0nc89GZsUJm24y7FsPk2hspZmEnKpnViFqcZ17PWohWNcZtaUCgYEA6fzk
bp1F0xX0IWT2jo62ioAcdaaxluOBB0zoUAC6gQ0P1uN4nkYn1sXKsalSsvahrILZ
ud9z9ceaf/OLAgDOjQtWzn0mwhFP98wzb3CDF/83ZqMXJflnuwuPSTgcPqI6kr28
+KMwjbIoZgE38fN+p7I3qKvIkO2yHqPOi2uBkmMCgYAUqwPH0B5kmxUwqs44zDVo
w1odImz9HqL0+tXphvDDTCLLGORW1VhvB5WohIPi8cW6pC3+S6ZL982ad9zHgoCw
ZzIwldrEb0KdwTOekOSYgW9rRMMXcZxmbMe9EcuvQXwxa6QU8rVSbWNHr4A24RNi
KJTvQ0rdZszZ05w5D204ZQKBgQCCS46wged16c2uItig/ZtseHZglVhi24DoHc1n
b2BrqGhfkv+BszNQB4gdclpYybmxpJO1S1b5UBMamPWZQfXC2MOX7Fz+yEEtjYo+
zfpSDI4/GyYywTUgFQnPDe28ev3+5KUsF0NcRA727krG8n5ex4Dy7eWbvqDnKvRC
8rSOXQKBgF9ZG5xbV96I9ThMLRSwschlCo9EN3UjC1o3k3b4mzJLfc16c/I/tIdQ
oDcMK6KbysVvUF1124vfSOv/V5GmCQZeaT7XgG4WlUcbO6ktdkwCnyE3xnyFnQpB
zeeTrkPYBqkJO29lZcPkUNX4uANtjtdNiniew5UCVhrDjMgjnofS
-----END RSA PRIVATE KEY-----

As you might imagine, it’s a lot harder to guess that key than it is a password. In fact, cracking a 2048 bit key like the one above would take 300 trillion years with a quantum supercomputer (which doesn’t yet exist)! Source. The universe is 15 billion years old. That means it would require 300 trillion / 15 billion = 20,000 universe lifetimes to crack.

Before you disable password-authentication, you need to be 100% sure that key-based authentication is working or else you will lock yourself out of your server!

To disable password-based authentication, you need to edit /etc/ssh/sshd_config, find PasswordAuthentication and put no after it. If it is commented out (there is a # at the front of the line) delete the #. It will look like this when finished:

disable sshd password authentication
disable sshd password authentication

Then you need to restart the SSH daemon (service) for the change to take effect:sudo systemctl restart ssh.service. Now you password-based SSH authentication has been disabled!

My failed authentication attempts dropped dramatically after disabling password-based SSH authentication. Below is the same general timeframe from the morning of when this post was written:

Mar 13 00:00:24 austinsnerdythings.com sshd[108357]: Invalid user ftpuser from 167.99.34.31 port 59060
Mar 13 00:00:24 austinsnerdythings.com sshd[108357]: Received disconnect from 167.99.34.31 port 59060:11: Normal Shutdown, Thank you for playing [preauth]
Mar 13 00:00:24 austinsnerdythings.com sshd[108357]: Disconnected from invalid user ftpuser 167.99.34.31 port 59060 [preauth]
Mar 13 00:03:09 austinsnerdythings.com sshd[108549]: Received disconnect from 24.8.45.4 port 5402:11: disconnected by user
Mar 13 00:03:09 austinsnerdythings.com sshd[108549]: Disconnected from user austin 24.8.45.4 port 5402
Mar 13 00:03:09 austinsnerdythings.com sshd[108438]: pam_unix(sshd:session): session closed for user austin
Mar 13 00:12:33 austinsnerdythings.com sshd[108934]: Invalid user postgres from 167.99.34.31 port 46444
Mar 13 00:12:33 austinsnerdythings.com sshd[108934]: Received disconnect from 167.99.34.31 port 46444:11: Normal Shutdown, Thank you for playing [preauth]
Mar 13 00:12:33 austinsnerdythings.com sshd[108934]: Disconnected from invalid user postgres 167.99.34.31 port 46444 [preauth]
Mar 13 00:12:44 austinsnerdythings.com sshd[108941]: Received disconnect from 222.187.232.213 port 11758:11: [preauth]
Mar 13 00:12:44 austinsnerdythings.com sshd[108941]: Disconnected from authenticating user root 222.187.232.213 port 11758 [preauth]
Mar 13 00:17:40 austinsnerdythings.com sshd[109097]: Received disconnect from 221.131.165.23 port 32827:11: [preauth]
Mar 13 00:17:40 austinsnerdythings.com sshd[109097]: Disconnected from authenticating user root 221.131.165.23 port 32827 [preauth]
Mar 13 00:24:51 austinsnerdythings.com sshd[109322]: Invalid user postgres from 167.99.34.31 port 33830
Mar 13 00:24:52 austinsnerdythings.com sshd[109322]: Received disconnect from 167.99.34.31 port 33830:11: Normal Shutdown, Thank you for playing [preauth]
Mar 13 00:24:52 austinsnerdythings.com sshd[109322]: Disconnected from invalid user postgres 167.99.34.31 port 33830 [preauth]

Most of these are just disconnects. The hackers see that my server is not accepting passwords and they just disconnect – they don’t even try to log in.

#2 – Install Fail2Ban

Fail2Ban is a helpful tool that monitors various logs and if it sees too many failed attempts, it will issue a ban on the offending IP address.

It is simple enough to install. First, update your package cache. On Ubuntu/Debian, this is done with apt:sudo apt update.
Then install fail2ban:sudo apt install -y fail2ban. This automatically enables Fail2ban so that it starts on boot. It has a bunch of out-of-the-box rules and will handle many services without any additional configuration. This is what my Fail2ban log looks like as of right now. This is all SSH bans. Notice that the duration is increasing for IP 167.172.170.218. The default ban duration is 10 minutes and I have it configured to double (plus some randomness) every extra attempt.fail2ban log

#3 – Add WordPress specific Fail2ban jails and plugin

Attempts to log into WordPress look like normal web traffic in web logs. Failed logins aren’t recorded specifically. We can change that by adding a plugin to WordPress that writes to /var/log/auth.log for a number of activities. Fail2ban monitors /var/log/auth.log for failed logins so it can act appropriately. I am using WP-Fail2Ban-Redux which does exactly what it says and without any nonsense. To finish the install, I copied the files from wp-content/plugins/wp-fail2ban-redux/config/filters and /jail to my fail2ban filter.d/ and jail.d/ folders:

cp /var/www/wordpress/wp-content/plugins/wp-fail2ban-redux/config/filters/wordpress-hard.conf /etc/fail2ban/filter.d/wordpress-hard.conf
cp /var/www/wordpress/wp-content/plugins/wp-fail2ban-redux/config/filters/wordpress-soft.conf /etc/fail2ban/filter.d/wordpress-soft.conf
cp /var/www/wordpress/wp-content/plugins/wp-fail2ban-redux/config/jail/wordpress.conf /etc/fail2ban/jail.d/wordpress.conf

Restart fail2ban so the changes take effect:
sudo systemctl restart fail2ban
View all the bans in your log! Congrats, you’ve now applied some top notch security practices to your blog.

fail2ban wordpress bans
fail2ban wordpress bans

#4 – ALWAYS KEEP YOUR WORDPRESS INSTALL UPDATED

That is the entirety of #4.

#5 – To disable XMLRPC or not, that is the question

I haven’t disabled XML-RPC yet. XML-RPC is a way to programmatically interact with WordPress blogs. Hackers can use it to rapidly try user/password combinations and other things like that. Installing the WordPress specific Fail2Ban components will effectively ban offenders while still allowing access to the underlying services.

In conclusion

It isn’t too hard to make these three changes to secure your WordPress blog and doing so will increase the security drastically. If you would like assistance doing this on your site, please use the contact form to get in touch with me. Lastly, always keep your WordPress install up to date. Every so often, security researchers find holes in the base WordPress code. Automatic updates will prevent your site from being a target.