mirror of
https://github.com/iio612/DEFENDER.git
synced 2026-02-13 19:24:23 +00:00
Merge pull request #21 from adator85/dev
New Installation file created for unix system
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,4 +3,5 @@ db/
|
|||||||
logs/
|
logs/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
configuration.json
|
configuration.json
|
||||||
|
install.log
|
||||||
test.py
|
test.py
|
||||||
98
README.md
98
README.md
@@ -21,35 +21,85 @@ Il permet aux opérateurs de gérer efficacement un canal, tout en offrant aux u
|
|||||||
Kick: Les utilisateurs peuvent voter pour expulser un membre du canal.
|
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.).
|
Autres actions: Possibilité d'étendre le système de vote à d'autres actions (ban, etc.).
|
||||||
|
|
||||||
# Installation et utilisation
|
# Installation automatique sur une machine Debian/Ubuntu
|
||||||
Prérequis:
|
|
||||||
- Python version >= 3.10
|
|
||||||
- Pip de python installé sur la machine
|
|
||||||
- Python librairies psutil & sqlalchemy & requests
|
|
||||||
- IRC Serveur Version >= UnrealIRCd-6.1.2.2
|
|
||||||
|
|
||||||
Installation:
|
Prérequis:
|
||||||
|
- Système d'exploitation Linux (Windows non supporté)
|
||||||
|
- Droits d'administrateur (root) pour l'exécution du script
|
||||||
|
- Python version 3.10 ou supérieure
|
||||||
|
|
||||||
Cloner le dépôt:
|
Bash
|
||||||
Bash
|
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
|
||||||
git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
|
- Renommer le fichier exemple_configuration.json en configuration.json
|
||||||
Utilisez ce code avec précaution.
|
- Configurer le fichier configuration.json
|
||||||
|
$ sudo python3 install.py
|
||||||
|
|
||||||
Configuration (configuration.json):
|
Si votre configuration est bonne, votre service est censé etre connecté a votre réseau IRC
|
||||||
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...
|
|
||||||
|
|
||||||
Extension:
|
# Installation manuelle:
|
||||||
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).
|
Bash
|
||||||
|
$ git clone https://github.com/adator85/IRC_DEFENDER_MODULES.git
|
||||||
|
$ cd IRC_DEFENDER_MODULES
|
||||||
|
$ python3 -m venv .pyenv
|
||||||
|
$ source .pyenv/bin/activate
|
||||||
|
- Créer un service nommé "Defender.service" pour votre service et placer le dans "/etc/systemd/system/"
|
||||||
|
$ sudo systemctl start Defender
|
||||||
|
|
||||||
Contributions:
|
# Configuration
|
||||||
|
|
||||||
|
SERVEUR (Serveur)
|
||||||
|
SERVEUR_IP: Adresse IP du serveur IRC à rejoindre.
|
||||||
|
SERVEUR_HOSTNAME: Nom d'hôte du serveur IRC à rejoindre (optionnel).
|
||||||
|
SERVEUR_LINK: Lien vers le serveur IRC (optionnel).
|
||||||
|
SERVEUR_PORT: Port de connexion au serveur IRC.
|
||||||
|
SERVEUR_PASSWORD: Mot de passe d'enregistrement du service sur le serveur IRC.
|
||||||
|
SERVEUR_ID: Identifiant unique du service.
|
||||||
|
SERVEUR_SSL: Active la connexion SSL sécurisée au serveur IRC (true/false).
|
||||||
|
SERVICE (Service)
|
||||||
|
SERVICE_NAME: Nom du service IRC.
|
||||||
|
SERVICE_NICKNAME: Surnom utilisé par le service sur le serveur IRC.
|
||||||
|
SERVICE_REALNAME: Nom réel du service affiché sur le serveur IRC.
|
||||||
|
SERVICE_USERNAME: Nom d'utilisateur utilisé par le service pour se connecter au serveur IRC.
|
||||||
|
SERVICE_HOST: Nom d'hôte du service affiché sur le serveur IRC (optionnel).
|
||||||
|
SERVICE_INFO: Description du service.
|
||||||
|
SERVICE_CHANLOG: Canal utilisé pour la journalisation des actions du service.
|
||||||
|
SERVICE_SMODES: Modes serveur appliqués aux canaux rejoints par le service.
|
||||||
|
SERVICE_CMODES: Modes de canal appliqués aux canaux rejoints par le service.
|
||||||
|
SERVICE_UMODES: Modes utilisateur appliqués au service.
|
||||||
|
SERVICE_PREFIX: Caractère utilisé comme préfixe des commandes du service.
|
||||||
|
COMPTE (Compte)
|
||||||
|
OWNER: Nom d'utilisateur possédant les droits d'administration du service.
|
||||||
|
PASSWORD: Mot de passe de l'administrateur du service.
|
||||||
|
CANAUX (Canaux)
|
||||||
|
SALON_JAIL: Canal utilisé comme prison pour les utilisateurs sanctionnés.
|
||||||
|
SALON_JAIL_MODES: Modes appliqués au canal de prison.
|
||||||
|
SALON_LIBERER: Canal utilisé pour la libération des utilisateurs sanctionnés.
|
||||||
|
API (API)
|
||||||
|
API_TIMEOUT: Durée maximale d'attente d'une réponse de l'API en secondes.
|
||||||
|
SCANNER (Scanner)
|
||||||
|
PORTS_TO_SCAN: Liste des ports à scanner pour détecter des serveurs potentiellement malveillants.
|
||||||
|
SÉCURITÉ (Sécurité)
|
||||||
|
WHITELISTED_IP: Liste d'adresses IP autorisées à contourner certaines restrictions.
|
||||||
|
GLINE_DURATION: Durée de bannissement temporaire d'un utilisateur en minutes.
|
||||||
|
DEBUG (Debug)
|
||||||
|
DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus grand est le nombre, plus il y a d'informations).
|
||||||
|
COULEURS (Couleurs)
|
||||||
|
CONFIG_COLOR: Dictionnaire contenant des codes de couleurs IRC pour un meilleur affichage des messages.
|
||||||
|
|
||||||
|
Modification de la configuration
|
||||||
|
|
||||||
|
Vous devez modifier le fichier config.json en remplaçant les valeurs par défaut avec vos propres informations. Assurez-vous de bien lire la description de chaque paramètre pour une configuration optimale du service.
|
||||||
|
|
||||||
|
Attention
|
||||||
|
|
||||||
|
Le mot de passe de l'administrateur et le mot de passe du service doivent être modifiés pour des raisons de sécurité.
|
||||||
|
Ne partagez pas vos informations de connexion au serveur IRC avec des tiers.
|
||||||
|
|
||||||
|
#Extension:
|
||||||
|
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).
|
||||||
|
|
||||||
|
# Contributions:
|
||||||
Les contributions sont les bienvenues ! N'hésitez pas à ouvrir des issues ou des pull requests.
|
Les contributions sont les bienvenues ! N'hésitez pas à ouvrir des issues ou des pull requests.
|
||||||
|
|
||||||
Avertissement:
|
# Avertissement:
|
||||||
Ce bot est fourni "tel quel" sans aucune garantie. Utilisez-le à vos risques et périls.
|
Ce bot est fourni "tel quel" sans aucune garantie. Utilisez-le à vos risques et périls.
|
||||||
@@ -38,8 +38,10 @@ class User:
|
|||||||
|
|
||||||
for record in self.UID_DB:
|
for record in self.UID_DB:
|
||||||
if record.uid == newUser.uid:
|
if record.uid == newUser.uid:
|
||||||
|
# If the user exist then return False and do not go further
|
||||||
exist = True
|
exist = True
|
||||||
self.log.debug(f'{record.uid} already exist')
|
self.log.debug(f'{record.uid} already exist')
|
||||||
|
return result
|
||||||
|
|
||||||
if not exist:
|
if not exist:
|
||||||
self.UID_DB.append(newUser)
|
self.UID_DB.append(newUser)
|
||||||
@@ -65,9 +67,11 @@ class User:
|
|||||||
|
|
||||||
for record in self.UID_DB:
|
for record in self.UID_DB:
|
||||||
if record.uid == uid:
|
if record.uid == uid:
|
||||||
|
# If the user exist then update and return True and do not go further
|
||||||
record.nickname = newNickname
|
record.nickname = newNickname
|
||||||
result = True
|
result = True
|
||||||
self.log.debug(f'UID ({record.uid}) has been updated with new nickname {newNickname}')
|
self.log.debug(f'UID ({record.uid}) has been updated with new nickname {newNickname}')
|
||||||
|
return result
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
self.log.critical(f'The new nickname {newNickname} was not updated, uid = {uid}')
|
self.log.critical(f'The new nickname {newNickname} was not updated, uid = {uid}')
|
||||||
@@ -87,9 +91,11 @@ class User:
|
|||||||
|
|
||||||
for record in self.UID_DB:
|
for record in self.UID_DB:
|
||||||
if record.uid == uid:
|
if record.uid == uid:
|
||||||
|
# If the user exist then remove and return True and do not go further
|
||||||
self.UID_DB.remove(record)
|
self.UID_DB.remove(record)
|
||||||
result = True
|
result = True
|
||||||
self.log.debug(f'UID ({record.uid}) has been deleted')
|
self.log.debug(f'UID ({record.uid}) has been deleted')
|
||||||
|
return result
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
self.log.critical(f'The UID {uid} was not deleted')
|
self.log.critical(f'The UID {uid} was not deleted')
|
||||||
@@ -179,8 +185,10 @@ class Admin:
|
|||||||
|
|
||||||
for record in self.UID_ADMIN_DB:
|
for record in self.UID_ADMIN_DB:
|
||||||
if record.uid == newAdmin.uid:
|
if record.uid == newAdmin.uid:
|
||||||
|
# If the admin exist then return False and do not go further
|
||||||
exist = True
|
exist = True
|
||||||
self.log.debug(f'{record.uid} already exist')
|
self.log.debug(f'{record.uid} already exist')
|
||||||
|
return result
|
||||||
|
|
||||||
if not exist:
|
if not exist:
|
||||||
self.UID_ADMIN_DB.append(newAdmin)
|
self.UID_ADMIN_DB.append(newAdmin)
|
||||||
@@ -191,37 +199,41 @@ class Admin:
|
|||||||
self.log.critical(f'The User Object was not inserted {newAdmin}')
|
self.log.critical(f'The User Object was not inserted {newAdmin}')
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def update(self, uid: str, newNickname: str) -> bool:
|
def update(self, uid: str, newNickname: str) -> bool:
|
||||||
|
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
for record in self.UID_ADMIN_DB:
|
for record in self.UID_ADMIN_DB:
|
||||||
if record.uid == uid:
|
if record.uid == uid:
|
||||||
|
# If the admin exist, update and do not go further
|
||||||
record.nickname = newNickname
|
record.nickname = newNickname
|
||||||
result = True
|
result = True
|
||||||
self.log.debug(f'UID ({record.uid}) has been updated with new nickname {newNickname}')
|
self.log.debug(f'UID ({record.uid}) has been updated with new nickname {newNickname}')
|
||||||
|
return result
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
self.log.critical(f'The new nickname {newNickname} was not updated, uid = {uid}')
|
self.log.critical(f'The new nickname {newNickname} was not updated, uid = {uid}')
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def delete(self, uid: str) -> bool:
|
def delete(self, uid: str) -> bool:
|
||||||
|
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
for record in self.UID_ADMIN_DB:
|
for record in self.UID_ADMIN_DB:
|
||||||
if record.uid == uid:
|
if record.uid == uid:
|
||||||
|
# If the admin exist, delete and do not go further
|
||||||
self.UID_ADMIN_DB.remove(record)
|
self.UID_ADMIN_DB.remove(record)
|
||||||
result = True
|
result = True
|
||||||
self.log.debug(f'UID ({record.uid}) has been created')
|
self.log.debug(f'UID ({record.uid}) has been created')
|
||||||
|
return result
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
self.log.critical(f'The UID {uid} was not deleted')
|
self.log.critical(f'The UID {uid} was not deleted')
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_Admin(self, uidornickname: str) -> Union[AdminModel, None]:
|
def get_Admin(self, uidornickname: str) -> Union[AdminModel, None]:
|
||||||
|
|
||||||
Admin = None
|
Admin = None
|
||||||
@@ -273,15 +285,23 @@ class Channel:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def insert(self, newChan: ChannelModel) -> bool:
|
def insert(self, newChan: ChannelModel) -> bool:
|
||||||
|
"""This method will insert a new channel and if the channel exist it will update the user list (uids)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
newChan (ChannelModel): The channel model object
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if new channel, False if channel exist (However UID could be updated)
|
||||||
|
"""
|
||||||
result = False
|
result = False
|
||||||
exist = False
|
exist = False
|
||||||
|
|
||||||
for record in self.UID_CHANNEL_DB:
|
for record in self.UID_CHANNEL_DB:
|
||||||
if record.name == newChan.name:
|
if record.name == newChan.name:
|
||||||
|
# If the channel exist, update the user list and do not go further
|
||||||
exist = True
|
exist = True
|
||||||
self.log.debug(f'{record.name} already exist')
|
self.log.debug(f'{record.name} already exist')
|
||||||
|
|
||||||
for user in newChan.uids:
|
for user in newChan.uids:
|
||||||
record.uids.append(user)
|
record.uids.append(user)
|
||||||
|
|
||||||
@@ -289,9 +309,11 @@ class Channel:
|
|||||||
del_duplicates = list(set(record.uids))
|
del_duplicates = list(set(record.uids))
|
||||||
record.uids = del_duplicates
|
record.uids = del_duplicates
|
||||||
self.log.debug(f'Updating a new UID to the channel {record}')
|
self.log.debug(f'Updating a new UID to the channel {record}')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
if not exist:
|
if not exist:
|
||||||
|
# If the channel don't exist, then create it
|
||||||
self.UID_CHANNEL_DB.append(newChan)
|
self.UID_CHANNEL_DB.append(newChan)
|
||||||
result = True
|
result = True
|
||||||
self.log.debug(f'New Channel Created: ({newChan})')
|
self.log.debug(f'New Channel Created: ({newChan})')
|
||||||
@@ -307,9 +329,12 @@ class Channel:
|
|||||||
|
|
||||||
for record in self.UID_CHANNEL_DB:
|
for record in self.UID_CHANNEL_DB:
|
||||||
if record.name == name:
|
if record.name == name:
|
||||||
|
# If the channel exist, then remove it and return True.
|
||||||
|
# As soon as the channel found, return True and stop the loop
|
||||||
self.UID_CHANNEL_DB.remove(record)
|
self.UID_CHANNEL_DB.remove(record)
|
||||||
result = True
|
result = True
|
||||||
self.log.debug(f'Channel ({record.name}) has been created')
|
self.log.debug(f'Channel ({record.name}) has been created')
|
||||||
|
return result
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
self.log.critical(f'The Channel {name} was not deleted')
|
self.log.critical(f'The Channel {name} was not deleted')
|
||||||
@@ -325,13 +350,13 @@ class Channel:
|
|||||||
for user_id in record.uids:
|
for user_id in record.uids:
|
||||||
if self.Base.clean_uid(user_id) == uid:
|
if self.Base.clean_uid(user_id) == uid:
|
||||||
record.uids.remove(user_id)
|
record.uids.remove(user_id)
|
||||||
self.log.debug(f'uid {uid} has been removed, here is the new object: {record}')
|
self.log.debug(f'The UID {uid} has been removed, here is the new object: {record}')
|
||||||
result = True
|
result = True
|
||||||
|
|
||||||
for record in self.UID_CHANNEL_DB:
|
for record in self.UID_CHANNEL_DB:
|
||||||
if not record.uids:
|
if not record.uids:
|
||||||
self.UID_CHANNEL_DB.remove(record)
|
self.UID_CHANNEL_DB.remove(record)
|
||||||
self.log.debug(f'Channel {record.name} has been removed, here is the new object: {record}')
|
self.log.debug(f'The Channel {record.name} has been removed, here is the new object: {record}')
|
||||||
|
|
||||||
return result
|
return result
|
||||||
except ValueError as ve:
|
except ValueError as ve:
|
||||||
@@ -347,13 +372,3 @@ class Channel:
|
|||||||
self.log.debug(f'Search {name} -- result = {Channel}')
|
self.log.debug(f'Search {name} -- result = {Channel}')
|
||||||
|
|
||||||
return Channel
|
return Channel
|
||||||
|
|
||||||
def get_mode(self, name:str) -> Union[str, None]:
|
|
||||||
|
|
||||||
mode = None
|
|
||||||
for record in self.UID_CHANNEL_DB:
|
|
||||||
if record.name == name:
|
|
||||||
mode = record.mode
|
|
||||||
|
|
||||||
self.log.debug(f'The mode of the channel {name} has been found: {mode}')
|
|
||||||
return mode
|
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ class Base:
|
|||||||
if thread.getName() != 'heartbeat':
|
if thread.getName() != 'heartbeat':
|
||||||
if not thread.is_alive():
|
if not thread.is_alive():
|
||||||
self.running_threads.remove(thread)
|
self.running_threads.remove(thread)
|
||||||
self.logs.debug(f"Thread {str(thread.getName())} {str(thread.native_id)} removed")
|
self.logs.info(f"Thread {str(thread.getName())} {str(thread.native_id)} removed")
|
||||||
|
|
||||||
# print(threading.enumerate())
|
# print(threading.enumerate())
|
||||||
except AssertionError as ae:
|
except AssertionError as ae:
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
from importlib.util import find_spec
|
from importlib.util import find_spec
|
||||||
from subprocess import check_call, run
|
from subprocess import check_call, run, CalledProcessError
|
||||||
from platform import python_version
|
from platform import python_version
|
||||||
from sys import exit
|
from sys import exit
|
||||||
|
import os
|
||||||
|
|
||||||
class Install:
|
class Install:
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.PYTHON_MIN_VERSION = '3.10'
|
self.PYTHON_MIN_VERSION = '3.10'
|
||||||
|
|
||||||
|
self.venv_folder_name = '.pyenv'
|
||||||
|
self.cmd_venv_command = ['python3', '-m', 'venv', self.venv_folder_name]
|
||||||
self.module_to_install = ['sqlalchemy','psutil','requests']
|
self.module_to_install = ['sqlalchemy','psutil','requests']
|
||||||
|
|
||||||
if not self.checkPythonVersion():
|
if not self.checkPythonVersion():
|
||||||
@@ -38,6 +42,17 @@ class Install:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def run_subprocess(self, command:list) -> None:
|
||||||
|
|
||||||
|
print(command)
|
||||||
|
try:
|
||||||
|
check_call(command)
|
||||||
|
print("La commande s'est terminée avec succès.")
|
||||||
|
except CalledProcessError as e:
|
||||||
|
print(f"La commande a échoué avec le code de retour :{e.returncode}")
|
||||||
|
print(f"Try to install dependencies ...")
|
||||||
|
exit(5)
|
||||||
|
|
||||||
def checkDependencies(self) -> None:
|
def checkDependencies(self) -> None:
|
||||||
"""### Verifie les dépendances si elles sont installées
|
"""### Verifie les dépendances si elles sont installées
|
||||||
- Test si les modules sont installés
|
- Test si les modules sont installés
|
||||||
@@ -46,6 +61,11 @@ class Install:
|
|||||||
"""
|
"""
|
||||||
do_install = False
|
do_install = False
|
||||||
|
|
||||||
|
# Check if virtual env exist
|
||||||
|
if not os.path.exists(f'{self.venv_folder_name}'):
|
||||||
|
self.run_subprocess(self.cmd_venv_command)
|
||||||
|
do_install = True
|
||||||
|
|
||||||
for module in self.module_to_install:
|
for module in self.module_to_install:
|
||||||
if find_spec(module) is None:
|
if find_spec(module) is None:
|
||||||
do_install = True
|
do_install = True
|
||||||
@@ -70,3 +90,10 @@ class Install:
|
|||||||
print(f"====> Module {module} installé")
|
print(f"====> Module {module} installé")
|
||||||
else:
|
else:
|
||||||
print(f"==> {module} already installed")
|
print(f"==> {module} already installed")
|
||||||
|
|
||||||
|
print(f"#"*12)
|
||||||
|
print("Installation complete ...")
|
||||||
|
print("You must change environment using the command below")
|
||||||
|
print(f"source {self.venv_folder_name}{os.sep}bin{os.sep}activate")
|
||||||
|
print(f"#"*12)
|
||||||
|
exit(1)
|
||||||
@@ -803,10 +803,10 @@ class Irc:
|
|||||||
|
|
||||||
get_uid_or_nickname = str(cmd[0].replace(':',''))
|
get_uid_or_nickname = str(cmd[0].replace(':',''))
|
||||||
if len(cmd) == 6:
|
if len(cmd) == 6:
|
||||||
if cmd[1] == 'PRIVMSG' and cmd[3] == ':auth':
|
if cmd[1] == 'PRIVMSG' and str(cmd[3]).replace('.','') == ':auth':
|
||||||
cmd_copy = cmd.copy()
|
cmd_copy = cmd.copy()
|
||||||
cmd_copy[5] = '**********'
|
cmd_copy[5] = '**********'
|
||||||
self.Base.logs.debug(cmd_copy)
|
self.Base.logs.info(cmd_copy)
|
||||||
else:
|
else:
|
||||||
self.Base.logs.info(cmd)
|
self.Base.logs.info(cmd)
|
||||||
else:
|
else:
|
||||||
|
|||||||
263
install.py
Normal file
263
install.py
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
from subprocess import check_call, run, CalledProcessError, PIPE
|
||||||
|
from platform import python_version
|
||||||
|
from sys import exit
|
||||||
|
import os, logging, shutil, pwd
|
||||||
|
|
||||||
|
|
||||||
|
class Install:
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
|
||||||
|
# Python required version
|
||||||
|
self.python_min_version = '3.10'
|
||||||
|
self.log_file = 'install.log'
|
||||||
|
self.ServiceName = 'Defender'
|
||||||
|
self.venv_name = '.pyenv'
|
||||||
|
self.venv_dependencies: list[str] = ['sqlalchemy','psutil','requests']
|
||||||
|
self.install_folder = os.getcwd()
|
||||||
|
self.osname = os.name
|
||||||
|
self.cmd_linux_requirements: list[str] = ['apt', 'install', '-y', 'python3', 'python3-pip', 'python3-venv']
|
||||||
|
self.venv_pip_full_path = os.path.join(self.venv_name, f'bin{os.sep}pip')
|
||||||
|
self.venv_python_full_path = os.path.join(self.venv_name, f'bin{os.sep}python')
|
||||||
|
self.systemd_folder = '/etc/systemd/system/'
|
||||||
|
|
||||||
|
# Init log system
|
||||||
|
self.init_log_system()
|
||||||
|
|
||||||
|
# Exclude Windows OS
|
||||||
|
if self.osname == 'nt':
|
||||||
|
print('/!\\ Windows OS is not supported by this automatic installation /!\\')
|
||||||
|
self.Logs.critical('/!\\ Windows OS is not supported by this automatic install /!\\')
|
||||||
|
exit(5)
|
||||||
|
|
||||||
|
if not self.is_root():
|
||||||
|
exit(5)
|
||||||
|
|
||||||
|
# Get the current user
|
||||||
|
self.system_username: str = input(f'What is the user ro run defender with ? [{os.getlogin()}] : ')
|
||||||
|
if str(self.system_username).strip() == '':
|
||||||
|
self.system_username = os.getlogin()
|
||||||
|
|
||||||
|
self.get_user_information(self.system_username)
|
||||||
|
|
||||||
|
self.Logs.debug(f'The user selected is: {self.system_username}')
|
||||||
|
self.Logs.debug(f'Operating system: {self.osname}')
|
||||||
|
|
||||||
|
# Install linux dependencies
|
||||||
|
self.install_linux_dependencies()
|
||||||
|
|
||||||
|
# Check python version
|
||||||
|
self.checkPythonVersion()
|
||||||
|
|
||||||
|
# Create systemd service file
|
||||||
|
self.create_service_file()
|
||||||
|
|
||||||
|
# Check if Env Exist | install environment | Install python dependencies
|
||||||
|
self.check_venv()
|
||||||
|
|
||||||
|
# Create and start service
|
||||||
|
if self.osname != 'nt':
|
||||||
|
self.run_subprocess(['systemctl','daemon-reload'])
|
||||||
|
self.run_subprocess(['systemctl','start', self.ServiceName])
|
||||||
|
self.run_subprocess(['systemctl','status', self.ServiceName])
|
||||||
|
|
||||||
|
# Clean the Installation
|
||||||
|
self.clean_installation()
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def is_installed(self) -> bool:
|
||||||
|
|
||||||
|
is_installed = False
|
||||||
|
|
||||||
|
# Check logs folder
|
||||||
|
if os.path.exists('logs'):
|
||||||
|
is_installed = True
|
||||||
|
|
||||||
|
# Check db folder
|
||||||
|
if os.path.exists('db'):
|
||||||
|
is_installed = True
|
||||||
|
|
||||||
|
return is_installed
|
||||||
|
|
||||||
|
def is_root(self) -> bool:
|
||||||
|
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
print('/!\\ user must run install.py as root /!\\')
|
||||||
|
self.Logs.critical('/!\\ user must run install.py as root /!\\')
|
||||||
|
return False
|
||||||
|
elif os.geteuid() == 0:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_user_information(self, system_user: str) -> None:
|
||||||
|
|
||||||
|
try:
|
||||||
|
username: tuple = pwd.getpwnam(system_user)
|
||||||
|
self.system_uid = username.pw_uid
|
||||||
|
self.system_gid = username.pw_gid
|
||||||
|
return None
|
||||||
|
|
||||||
|
except KeyError as ke:
|
||||||
|
self.Logs.critical(f"This user [{system_user}] doesn't exist: {ke}")
|
||||||
|
print(f"This user [{system_user}] doesn't exist: {ke}")
|
||||||
|
exit(5)
|
||||||
|
|
||||||
|
def init_log_system(self) -> None:
|
||||||
|
|
||||||
|
# Init logs object
|
||||||
|
self.Logs = logging
|
||||||
|
self.Logs.basicConfig(level=logging.DEBUG,
|
||||||
|
filename=self.log_file,
|
||||||
|
encoding='UTF-8',
|
||||||
|
format='%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(funcName)s - %(message)s')
|
||||||
|
|
||||||
|
self.Logs.debug('#################### STARTING INSTALLATION ####################')
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def clean_installation(self) -> None:
|
||||||
|
|
||||||
|
# Chown the Python Env to non user privilege
|
||||||
|
self.run_subprocess(['chown','-R', f'{self.system_username}:{self.system_username}',
|
||||||
|
f'{os.path.join(self.install_folder, self.venv_name)}'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Chown the installation log file
|
||||||
|
self.run_subprocess(['chown','-R', f'{self.system_username}:{self.system_username}',
|
||||||
|
f'{os.path.join(self.install_folder, self.log_file)}'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def run_subprocess(self, command:list) -> None:
|
||||||
|
|
||||||
|
try:
|
||||||
|
run_command = check_call(command)
|
||||||
|
self.Logs.debug(f'{command} - {run_command}')
|
||||||
|
print(f'{command} - {run_command}')
|
||||||
|
|
||||||
|
except CalledProcessError as e:
|
||||||
|
print(f"Command failed :{e.returncode}")
|
||||||
|
self.Logs.critical(f"Command failed :{e.returncode}")
|
||||||
|
exit(5)
|
||||||
|
|
||||||
|
def checkPythonVersion(self) -> bool:
|
||||||
|
"""Test si la version de python est autorisée ou non
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True si la version de python est autorisé sinon False
|
||||||
|
"""
|
||||||
|
python_required_version = self.python_min_version.split('.')
|
||||||
|
python_current_version = python_version().split('.')
|
||||||
|
|
||||||
|
self.Logs.debug(f'The current python version is: {python_version()}')
|
||||||
|
|
||||||
|
if int(python_current_version[0]) < int(python_required_version[0]):
|
||||||
|
print(f"## Your python version must be greather than or equal to {self.python_min_version} ##")
|
||||||
|
self.Logs.critical(f'Your python version must be greather than or equal to {self.python_min_version}')
|
||||||
|
return False
|
||||||
|
elif int(python_current_version[1]) < int(python_required_version[1]):
|
||||||
|
print(f"### Your python version must be greather than or equal to {self.python_min_version} ###")
|
||||||
|
self.Logs.critical(f'Your python version must be greather than or equal to {self.python_min_version}')
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"===> Version of python : {python_version()} ==> OK")
|
||||||
|
self.Logs.debug(f'Version of python : {python_version()} ==> OK')
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_packages(self, package_name) -> bool:
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Run a command in the virtual environment's Python to check if the package is installed
|
||||||
|
run([self.venv_python_full_path, '-c', f'import {package_name}'], check=True, stdout=PIPE, stderr=PIPE)
|
||||||
|
return True
|
||||||
|
except CalledProcessError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_venv(self) -> bool:
|
||||||
|
|
||||||
|
if os.path.exists(self.venv_name):
|
||||||
|
|
||||||
|
# Installer les dependances
|
||||||
|
self.install_dependencies()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.run_subprocess(['python3', '-m', 'venv', self.venv_name])
|
||||||
|
self.Logs.debug(f'Python Virtual env installed {self.venv_name}')
|
||||||
|
print(f'Python Virtual env installed {self.venv_name}')
|
||||||
|
|
||||||
|
self.install_dependencies()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_service_file(self) -> None:
|
||||||
|
|
||||||
|
if self.systemd_folder is None:
|
||||||
|
# If Windows, do not install systemd
|
||||||
|
return None
|
||||||
|
|
||||||
|
if os.path.exists(f'{self.systemd_folder}{os.sep}{self.ServiceName}.service'):
|
||||||
|
print(f'/!\\ Service already created in the system /!\\')
|
||||||
|
self.Logs.warning('/!\\ Service already created in the system /!\\')
|
||||||
|
print(f'The service file will be regenerated')
|
||||||
|
self.Logs.warning('The service file will be regenerated')
|
||||||
|
|
||||||
|
|
||||||
|
contain = f'''[Unit]
|
||||||
|
Description={self.ServiceName} IRC Service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User={self.system_username}
|
||||||
|
ExecStart={os.path.join(self.install_folder, self.venv_python_full_path)} {os.path.join(self.install_folder, 'main.py')}
|
||||||
|
WorkingDirectory={self.install_folder}
|
||||||
|
SyslogIdentifier={self.ServiceName}
|
||||||
|
Restart=on-failure
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
'''
|
||||||
|
|
||||||
|
with open(f'{self.ServiceName}.service.generated', 'w+') as servicefile:
|
||||||
|
servicefile.write(contain)
|
||||||
|
servicefile.close()
|
||||||
|
print('Service file generated with current configuration')
|
||||||
|
self.Logs.debug('Service file generated with current configuration')
|
||||||
|
|
||||||
|
source = f'{self.install_folder}{os.sep}{self.ServiceName}.service.generated'
|
||||||
|
self.run_subprocess(['chown','-R', f'{self.system_username}:{self.system_username}', source])
|
||||||
|
destination = f'{self.systemd_folder}'
|
||||||
|
shutil.copy(source, destination)
|
||||||
|
os.rename(f'{self.systemd_folder}{os.sep}{self.ServiceName}.service.generated', f'{self.systemd_folder}{os.sep}{self.ServiceName}.service')
|
||||||
|
print(f'Service file moved to systemd folder {self.systemd_folder}')
|
||||||
|
self.Logs.debug(f'Service file moved to systemd folder {self.systemd_folder}')
|
||||||
|
|
||||||
|
def install_linux_dependencies(self) -> None:
|
||||||
|
|
||||||
|
self.run_subprocess(self.cmd_linux_requirements)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def install_dependencies(self) -> None:
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.run_subprocess([self.venv_pip_full_path, 'cache', 'purge'])
|
||||||
|
self.run_subprocess([self.venv_python_full_path, '-m', 'pip', 'install', '--upgrade', 'pip'])
|
||||||
|
|
||||||
|
if self.check_packages('greenlet') is None:
|
||||||
|
self.run_subprocess(
|
||||||
|
[self.venv_pip_full_path, 'install', '--only-binary', ':all:', 'greenlet']
|
||||||
|
)
|
||||||
|
|
||||||
|
for module in self.venv_dependencies:
|
||||||
|
if not self.check_packages(module):
|
||||||
|
### Trying to install missing python packages ###
|
||||||
|
self.run_subprocess([self.venv_pip_full_path, 'install', module])
|
||||||
|
else:
|
||||||
|
self.Logs.debug(f'{module} already installed')
|
||||||
|
print(f"==> {module} already installed")
|
||||||
|
|
||||||
|
except CalledProcessError as cpe:
|
||||||
|
self.Logs.critical(f'{cpe}')
|
||||||
|
|
||||||
|
Install()
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"version": "5.0.5"
|
"version": "5.0.6"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user