i@mn0t.dev:~#

ssh control (discord bot) 10/05/22 - 23h30

An oob discord bot to control ingress trafic of ssh port.

You can keep iptables dropping ssh port.

Add iptables rules with simple one discord bot command.

Why ?

Because I dont like to let ssh port open to Internets.

Even when we change the default port, our machine are
scanned, ssh exotic port is detected and engines like censys, shodan, etc add it into their databases. The server is now easily findable by everyone.

Your first reaction will probably be:"Bro, just iptables our ip to access to ssh and that's it !".

Yes !

But if our ip change ?

If we change our location ?

There is also the port knocking technique, after many month of utilisation i'm a bit disappointed about it. Sometimes the port knocking doesn't works, it fails with timeout and, after 30min of try, ACCESS GRANTED. Nice, half an hour to access to the machine, maybe we can find a better way.

How ?

404 no real solution was found so I decide to try to make my own.

The most important problematic was the second question, "If we change our location ?"

At this time I knew an other thing, I dont want to open an other port. So I need to make the server getting the information of what ip he need to add into accepted rules.

Now an other thing appear, the endpoint where the server will get the information has to be up all the time, with easy access, simple manipulation.

Ok, I think to one tool that we commonly use, the chat app ! The one that I use for IT stuff is Discord.

Like other app there is the ability to create bot (-> create discord bot).

From discord bot it is possible to define a command with prefixed char like '!'. Cool, I will use this. What else I need, the machine's hostname (in case of multiple machines) and the source ip to accept, my ip.

something like this:

!<command> srv_hostname src_ip

Code !

Ok, let's go into serious things !

There is an python module to wrap arround the discord api.


python3 -m pip install discord

Then import it.


import discord from discord.ext import commands from discord.utils import get

Like I want to "up" the ssh port, I choose the command keyword "up". the command will be invoked by !up


@client.command(name='up', pass_context=True)
async def infos(ctx,message):
  hostname = os.uname()[1]
  member_id = ctx.message.author.id
  if member_id != 1839494782394905893:
    print('id non authorise a utiliser la command !infos')
    return
          

I start by getting the hostname (it will be the hostname of the machine who run the script) and the id of the discord user, like that, only the discord user with the id in the test is able the add an ip, here 1839494782394905893.



  msg = message.split()
    print(msg)
    print(hostname)
  if msg[0] == hostname:
    print('hostname ok')
    ip = re.findall(r'(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})',msg[1])[0]
    if ip:
      print(ip)
      FLAG=True
  else:
    print('msg is not for me')
          

Now I parse the "msg" aka the informations send with the !up command so the hostname and the ip. !up "poc-01 89.65.76.154"

Information need to be surrounded by quotes or it stop at the first space, after hostname.

Then the hostname is checked, like that the script doesn't execute next task when he dont match the hostname, and the ip to be sure that is a good ip format.

If tests are ok, FLAG is set to True Let's open the firewall



  if FLAG:
    fw_cmd = 'iptables -I INPUT -p tcp -s %s --dport 22 -j ACCEPT' % msg[1]
    subprocess.call(fw_cmd, shell=True)
    print(fw_cmd)
  await ctx.message.delete()

Finally subprocess is called to execute the iptable rule iptables -I INPUT -p tcp -s 89.65.76.154 --dport 22 -j ACCEPT and the full message is deleted cause I dont want to have full texted chan.

Place to the full code.



#!/usr/bin/python3

import os
import subprocess
import re
import discord
from discord.ext import commands
from discord.utils import get

TOKEN='DISCORD BOT TOKEN'

intents=discord.Intents.all()
intents.members=True
client=commands.Bot(command_prefix ='!', intents=intents)

FLAG=False

@client.command(name='up', pass_context=True)
async def infos(ctx,message):
    hostname=os.uname()[1]
    member_id=ctx.message.author.id
    if member_id != 797130906513965058:
        print('id non authorise a utiliser la command !infos')
        return
    msg=message.split()
    print(msg)
    print(hostname)
    if msg[0] == hostname:
        print('hostname ok')
        ip=re.findall(r'(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})',msg[1])[0]
        if ip:
            print(ip)
            FLAG=True
    else:
        print('msg is not for me')

    if FLAG:
        fw_cmd='iptables -I INPUT -p tcp -s %s --dport 22 -j ACCEPT' % msg[1]
        subprocess.call(fw_cmd, shell=True)
        print(fw_cmd)
    await ctx.message.delete()

@client.command(name='down', pass_context=True)
async def infos(ctx,message):
    hostname=os.uname()[1]
    member_id=ctx.message.author.id
    if member_id != 797130906513965058:
        print('id non authorise a utiliser la command !infos')
        return
    msg=message.split()
    print(msg)
    print(hostname)
    if msg[0] == hostname:
        print('hostname ok')
        ip=re.findall(r'(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})\.(?:[\d]{1,3})',msg[1])[0]
        if ip:
            print(ip)
            FLAG=True
    else:
        print('msg is not for me')

    if FLAG:
        fw_cmd='iptables -D INPUT -p tcp -s %s --dport 22 -j ACCEPT' % msg[1]
        subprocess.call(fw_cmd, shell=True)
        print(fw_cmd)
    await ctx.message.delete()

client.run(TOKEN)
          

The final script with the !down command to disable the ip.

Service

To finish, I want to get control on my script and start it at boot.

I put the script in /usr/sbin/ssh_control.py and create systemd service file in /etc/systemd/system/ssh_controld.service


[Unit] Description=Control ssh access iptables from discord After=network-online.target [Service] Type=simple User=root Group=root ExecStart=python3 /usr/sbin/ssh_control.py # Configures the time to wait before service is stopped forcefully. TimeoutStopSec=300 [Install] WantedBy=multi-user.target

Reload systemd daemon to take consideration of the new service file.


systemctl daemon-reload

And enable the service.


systemctl enable ssh_controld.service

Set iptables rules for the ssh port, here the standard port 22


iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 22 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT iptables -A INPUT -p tcp -m tcp --dport 22 -j DROP

Start the new service.


systemctl start ssh_controld.service

iptables is set to drop all the connexions.

Finish, I have control on my ssh port from discord !! I can be everywhere, if I have discord, I can open my ssh port :)