Merge pull request #20 from adator85/dev

README Update
This commit is contained in:
adator
2024-08-21 00:50:31 +02:00
committed by GitHub
5 changed files with 225 additions and 87 deletions

View File

@@ -2,70 +2,54 @@
Defender est un service IRC basé sur la sécurité des réseaux IRC ( UnrealIRCD ) Defender est un service IRC basé sur la sécurité des réseaux IRC ( UnrealIRCD )
Il permet d'ajouter une sécurité supplémentaire pour vérifier les users connectés au réseau Il permet d'ajouter une sécurité supplémentaire pour vérifier les users connectés au réseau
en demandant aux user un code de validation. en demandant aux user un code de validation.
Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux utilisateurs des outils d'interaction et de décision collective.
Pré-requis : # Fonctionnalités principales
Commandes opérateurs complètes:
Kick: Expulser un utilisateur du canal.
Ban: Interdire définitivement l'accès au canal.
Unban: Lever une interdiction.
Op/Deop: Attribuer ou retirer les droits d'opérateur.
Halfop/Dehalfop: Attribuer ou retirer les droits
Voice/Devoice: Attribuer ou retirer les droits de voix.
- Python version >= 3.10 Système de quarantaine:
- Pip de python installé sur la machine Mise en quarantaine: Isoler temporairement un utilisateur dans un canal privé.
- Python librairies psutil & sqlalchemy & requests Libération: Permettre à un utilisateur de quitter la quarantaine en entrant un code spécifique.
- IRC Serveur Version >= UnrealIRCd-6.1.2.2
Lancement de Defender : Système de vote:
Kick: Les utilisateurs peuvent voter pour expulser un membre du canal.
Autres actions: Possibilité d'étendre le système de vote à d'autres actions (ban, etc.).
- Installer les librairies python : psutil & sqlalchemy & requests # Installation et utilisation
- pip3 install psutil sqlalchemy requests ou pip install psutil sqlalchemy requests Prérequis:
- Ne pas lancer Defender en tant que root - Python version >= 3.10
- Créer plutot un service qui lancera Defender en tant qu'utilisateur non root - Pip de python installé sur la machine
- Un fichier PID sera crée. - Python librairies psutil & sqlalchemy & requests
- IRC Serveur Version >= UnrealIRCd-6.1.2.2
# TO DO LIST Installation:
- Optimiser le systeme de réputation: Cloner le dépôt:
- lorsque les users ce connectent, Ils entrent dans un salon puis une fraction de seconde le service les bans Bash
git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
Utilisez ce code avec précaution.
# VERSION 1 Configuration (configuration.json):
Le fichier configuration.json permet de personnaliser le service:
Serveur IRC: Adresse du serveur IRC.
Port: Port du serveur IRC.
Canal: Canal auquel se connecter.
Nom du Service: Nom d'utilisateur du bot sur le serveur.
Mot de passe: Mot de passe du link (si nécessaire).
Préfixes de commandes: Caractères utilisés pour déclencher les commandes.
Et bien d'autres...
[02.01.2024] Extension:
- Rajout de l'activation de la commande flood Le code est modulaire et conçu pour être facilement étendu. Vous pouvez ajouter de nouvelles commandes, de nouvelles fonctionnalités (mods/mod_test.py est un exemple pour bien demarrer la création de son module).
- Les deux variables RESTART et INIT ont été déplacées vers le module Irc
- Nouvelle class Install:
- Le programme va vérifier si les 3 librairies sont installées (SQLAlchemy & requests & psutil)
- Une fois la vérification, il va mêtre a jour pip puis installera les dépendances
[28.12.2023] Contributions:
- Changement de méthode pour récuperer la version actuelle de python Les contributions sont les bienvenues ! N'hésitez pas à ouvrir des issues ou des pull requests.
- Ajout de la réponse a une PING de la part d'un utilisateur
- Installation automatique des packages sqlalchemy, requests et psutil
# BUG FIX Avertissement:
Ce bot est fourni "tel quel" sans aucune garantie. Utilisez-le à vos risques et périls.
[29.12.2023]
- Correction des messages de receptions trop longs > 4070 caractéres;
- la méthode boucle et incrémente la réponse tant que le nombre de caractére reçu est supérieur a 4072
- Rajout du protocol MTAGS a la connexion du service
- Impact majeur dans la lecture des messages reçu du serveur ( PRIVMSG, SLOGS, UID, QUIT, NICK, PONG, SJOIN)
# ALREADY IMPLEMENTED
- Connexion en tant que service
- Gestion des messages reçus/envoyés par le serveur
- Gestion des caractéres spéciaux
- Gestion des logs (salon, fichiers et console)
- Mode debug : gestion des logs coté console
- Création du systeme de gestion de commandes
- Defender reconnait les commandes qui commence par le suffix définit dans la configuration
- Defender reconnait aussi reconnaitre les commandes qui viennent de /msg Defender [commande]
- Identifications
- Systéme d'identification [OK]
- Systéme de changement d'information [OK]
- Suppression d'un admin
- Systéme de groupe d'accés [OK]
Reputation security
- Activation ou désaction du systéme --> OK | .reputation ON/off
- Le user sera en mesure de changer la limite de la réputation --> OK | .reputation set limit 120
- Defender devra envoyer l'utilisateur dans un salon définit dans la configuration --> OK
- Defender bannira l'utilisateur de la totalité des salons, il le bannira aussi lorsqu'il souhaitera accéder a de nouveau salon --> OK
- Defender devra envoyer un message du type "Merci de taper cette comande /msg {nomdudefender} code {un code générer aléatoirement} --> OK
- Defender devra reconnaitre le code --> OK
- Defender devra liberer l'utilisateur et l'envoyer vers un salon définit dans la configuration --> OK

View File

@@ -56,9 +56,9 @@ class Base:
} }
if token == '': if token == '':
response = requests.get(json_url) response = requests.get(json_url, timeout=self.Config.API_TIMEOUT)
else: else:
response = requests.get(json_url, headers=headers) response = requests.get(json_url, headers=headers, timeout=self.Config.API_TIMEOUT)
response.raise_for_status() # Vérifie si la requête a réussi response.raise_for_status() # Vérifie si la requête a réussi
json_response:dict = response.json() json_response:dict = response.json()
@@ -120,6 +120,16 @@ class Base:
currentdate = datetime.now().strftime('%d-%m-%Y %H:%M:%S') currentdate = datetime.now().strftime('%d-%m-%Y %H:%M:%S')
return currentdate return currentdate
def get_all_modules(self) -> list:
all_files = os.listdir('mods/')
all_modules: list = []
for module in all_files:
if module.endswith('.py') and not module == '__init__.py':
all_modules.append(module.replace('.py', '').lower())
return all_modules
def create_log(self, log_message: str) -> None: def create_log(self, log_message: str) -> None:
"""Enregiste les logs """Enregiste les logs

View File

@@ -1208,16 +1208,31 @@ class Irc:
case 'show_modules': case 'show_modules':
self.Base.logs.debug(self.loaded_classes) self.Base.logs.debug(self.loaded_classes)
all_modules = self.Base.get_all_modules()
results = self.Base.db_execute_query(f'SELECT module FROM {self.Config.table_module}') results = self.Base.db_execute_query(f'SELECT module FROM {self.Config.table_module}')
results = results.fetchall() results = results.fetchall()
if len(results) == 0: # if len(results) == 0:
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :Aucun module chargé") # self.send2socket(f":{dnickname} NOTICE {fromuser} :There is no module loaded")
return False # return False
for r in results: found = False
self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :Le module {r[0]} chargé")
for module in all_modules:
for loaded_mod in results:
if module == loaded_mod[0]:
found = True
if found:
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.CONFIG_COLOR['verte']}Loaded{self.Config.CONFIG_COLOR['nogc']}")
else:
self.send2socket(f":{dnickname} NOTICE {fromuser} :{module} - {self.Config.CONFIG_COLOR['rouge']}Not Loaded{self.Config.CONFIG_COLOR['nogc']}")
found = False
# for r in results:
# self.send2socket(f":{dnickname} NOTICE {fromuser} :{r[0]} - {self.Config.CONFIG_COLOR['verte']}Loaded{self.Config.CONFIG_COLOR['nogc']}")
case 'show_timers': case 'show_timers':

View File

@@ -80,7 +80,7 @@ class Defender():
self.commands_level = { self.commands_level = {
0: ['code'], 0: ['code'],
1: ['join','part', 'info'], 1: ['join','part', 'info'],
2: ['q', 'dq', 'o', 'do', 'h', 'dh', 'v', 'dv', 'b', 'ub','k', 'kb'], 2: ['owner', 'deowner', 'op', 'deop', 'halfop', 'dehalfop', 'voice', 'devoice', 'ban', 'unban','kick', 'kickban'],
3: ['reputation','proxy_scan', 'flood', 'status', 'timer','show_reputation', 'show_users', 'sentinel'] 3: ['reputation','proxy_scan', 'flood', 'status', 'timer','show_reputation', 'show_users', 'sentinel']
} }
self.__set_commands(self.commands_level) # Enrigstrer les nouvelles commandes dans le code self.__set_commands(self.commands_level) # Enrigstrer les nouvelles commandes dans le code
@@ -1224,6 +1224,23 @@ class Defender():
jail_chan = self.Config.SALON_JAIL # Salon pot de miel jail_chan = self.Config.SALON_JAIL # Salon pot de miel
jail_chan_mode = self.Config.SALON_JAIL_MODES # Mode du salon "pot de miel" jail_chan_mode = self.Config.SALON_JAIL_MODES # Mode du salon "pot de miel"
if len(fullcmd) >= 3:
fromchannel = str(fullcmd[2]).lower() if self.Base.Is_Channel(str(fullcmd[2]).lower()) else None
else:
fromchannel = None
if len(cmd) >= 2:
sentchannel = str(cmd[1]).lower() if self.Base.Is_Channel(str(cmd[1]).lower()) else None
else:
sentchannel = None
if not fromchannel is None:
channel = fromchannel
elif not sentchannel is None:
channel = sentchannel
else:
channel = None
match command: match command:
case 'timer': case 'timer':
@@ -1606,97 +1623,209 @@ class Defender():
except IndexError as ie: except IndexError as ie:
self.Logs.error(f'{ie}') self.Logs.error(f'{ie}')
case 'op' | 'o': case 'op':
# /mode #channel +o user # /mode #channel +o user
# .op #channel user # .op #channel user
# /msg dnickname op #channel user
# [':adator', 'PRIVMSG', '#services', ':.o', '#services', 'dktmb'] # [':adator', 'PRIVMSG', '#services', ':.o', '#services', 'dktmb']
try: try:
print(cmd) if channel is None:
channel = cmd[1] self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} +o {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} +o {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +o {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} +o {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd OP: {str(e)}') self.Logs.warning(f'_hcmd OP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} op [#SALON] [NICKNAME]')
case 'deop' | 'do': case 'deop':
# /mode #channel -o user # /mode #channel -o user
# .deop #channel user # .deop #channel user
try: try:
channel = cmd[1] if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} -o {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} -o {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -o {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} -o {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEOP: {str(e)}') self.Logs.warning(f'_hcmd DEOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deop [#SALON] [NICKNAME]')
case 'owner' | 'q': case 'owner':
# /mode #channel +q user # /mode #channel +q user
# .owner #channel user # .owner #channel user
try: try:
channel = cmd[1] if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} +q {fromuser}")
return True
# owner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} +q {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +q {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} +q {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd OWNER: {str(e)}') self.Logs.warning(f'_hcmd OWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} owner [#SALON] [NICKNAME]')
case 'deowner' | 'dq': case 'deowner':
# /mode #channel -q user # /mode #channel -q user
# .deowner #channel user # .deowner #channel user
try: try:
if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} -q {fromuser}")
return True
# deowner nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} -q {nickname}")
return True
channel = cmd[1] channel = cmd[1]
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -q {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} -q {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEOWNER: {str(e)}') self.Logs.warning(f'_hcmd DEOWNER: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} deowner [#SALON] [NICKNAME]')
case 'halfop' | 'h': case 'halfop':
# /mode #channel +h user # /mode #channel +h user
# .halfop #channel user # .halfop #channel user
try: try:
channel = cmd[1] if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} +h {fromuser}")
return True
# deop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} +h {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +h {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} +h {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd halfop: {str(e)}') self.Logs.warning(f'_hcmd halfop: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} halfop [#SALON] [NICKNAME]')
case 'dehalfop' | 'dh': case 'dehalfop':
# /mode #channel -h user # /mode #channel -h user
# .dehalfop #channel user # .dehalfop #channel user
try: try:
channel = cmd[1] if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} -h {fromuser}")
return True
# dehalfop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} -h {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -h {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} -h {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEHALFOP: {str(e)}') self.Logs.warning(f'_hcmd DEHALFOP: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} dehalfop [#SALON] [NICKNAME]')
case 'voice' | 'v': case 'voice':
# /mode #channel +v user # /mode #channel +v user
# .voice #channel user # .voice #channel user
try: try:
channel = cmd[1] if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} +v {fromuser}")
return True
# voice nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} +v {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} +v {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} +v {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd VOICE: {str(e)}') self.Logs.warning(f'_hcmd VOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} voice [#SALON] [NICKNAME]')
case 'devoice' | 'dv': case 'devoice':
# /mode #channel -v user # /mode #channel -v user
# .devoice #channel user # .devoice #channel user
try: try:
channel = cmd[1] if channel is None:
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]')
return False
if len(cmd) == 1:
self.Irc.send2socket(f":{service_id} MODE {channel} -v {fromuser}")
return True
# dehalfop nickname
if len(cmd) == 2:
nickname = cmd[1]
self.Irc.send2socket(f":{service_id} MODE {channel} -v {nickname}")
return True
nickname = cmd[2] nickname = cmd[2]
self.Irc.send2socket(f":{service_id} MODE {channel} -v {nickname}") self.Irc.send2socket(f":{service_id} MODE {channel} -v {nickname}")
except IndexError as e: except IndexError as e:
self.Logs.warning(f'_hcmd DEVOICE: {str(e)}') self.Logs.warning(f'_hcmd DEVOICE: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} devoice [#SALON] [NICKNAME]')
case 'ban' | 'b': case 'ban':
# .ban #channel nickname # .ban #channel nickname
try: try:
channel = cmd[1] channel = cmd[1]
@@ -1708,7 +1837,7 @@ class Defender():
self.Logs.warning(f'_hcmd BAN: {str(e)}') self.Logs.warning(f'_hcmd BAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} ban [#SALON] [NICKNAME]')
case 'unban' | 'ub': case 'unban':
# .unban #channel nickname # .unban #channel nickname
try: try:
channel = cmd[1] channel = cmd[1]
@@ -1720,7 +1849,7 @@ class Defender():
self.Logs.warning(f'_hcmd UNBAN: {str(e)}') self.Logs.warning(f'_hcmd UNBAN: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} unban [#SALON] [NICKNAME]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} unban [#SALON] [NICKNAME]')
case 'kick' | 'k': case 'kick':
# .kick #channel nickname reason # .kick #channel nickname reason
try: try:
channel = cmd[1] channel = cmd[1]
@@ -1738,7 +1867,7 @@ class Defender():
self.Logs.warning(f'_hcmd KICK: {str(e)}') self.Logs.warning(f'_hcmd KICK: {str(e)}')
self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kick [#SALON] [NICKNAME] [REASON]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Right command : /msg {dnickname} kick [#SALON] [NICKNAME] [REASON]')
case 'kickban' | 'kb': case 'kickban':
# .kickban #channel nickname reason # .kickban #channel nickname reason
try: try:
channel = cmd[1] channel = cmd[1]

View File

@@ -1,3 +1,3 @@
{ {
"version": "5.0.4" "version": "5.0.5"
} }