From f9662c488e3c61676ab80b1a536b2bf03d324007 Mon Sep 17 00:00:00 2001 From: Sebastian Serfling Date: Tue, 11 Feb 2025 12:39:12 +0100 Subject: [PATCH] first Upload --- Firmen-Logo.png | Bin 0 -> 15972 bytes docker_sync.py | 2 + main.py | 362 ++++++++++++ main.py_old | 229 ++++++++ pdf.py | 64 +++ rport-installer.sh | 1348 ++++++++++++++++++++++++++++++++++++++++++++ run.py | 10 + 7 files changed, 2015 insertions(+) create mode 100644 Firmen-Logo.png create mode 100644 docker_sync.py create mode 100644 main.py create mode 100644 main.py_old create mode 100644 pdf.py create mode 100644 rport-installer.sh create mode 100644 run.py diff --git a/Firmen-Logo.png b/Firmen-Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..af919a86daf788e2031354e78c888064a88deb31 GIT binary patch literal 15972 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 literal 0 HcmV?d00001 diff --git a/docker_sync.py b/docker_sync.py new file mode 100644 index 0000000..0a2bdad --- /dev/null +++ b/docker_sync.py @@ -0,0 +1,2 @@ +import os +os.system(f"docker exec -u www-data app-server /bin/bash -c 'php occ files:scan --path=karstenstoecker/files/Stines\ GmbH/1.\ Verwaltung/4.\ Verträge/2.\ Zulieferer\ -\ Dienstleister/MS\ -\ SPLA/Insight/Reports/'") \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..e18903f --- /dev/null +++ b/main.py @@ -0,0 +1,362 @@ +import csv +import os +import mysql.connector +import pandas as pd +from datetime import datetime, timedelta +import openpyxl +import calendar +import http.client, urllib +# from reportlab.lib.pagesizes import letter +# from reportlab.pdfgen import canvas +from openpyxl.styles import Alignment, Font +import subprocess + +date = datetime.now() - timedelta(1) +month = datetime.now().strftime("%m") +year = datetime.now().strftime("%Y") +start_date_00 = date.replace(hour=0, minute=0, second=0, microsecond=0) +start_date_23 = start_date_00.replace(hour=23, minute=59, second=59, microsecond=0) +start_date_str = start_date_00.strftime("01.%m") +last_date_str = start_date_00.strftime(f"{calendar.monthrange(int(year),int(month))[1]}.%m") + +mydb = mysql.connector.connect( + host="172.17.1.21", + port="3306", + user="root", + password="N53yBCswuawzBzS445VNAhWVMs3N59Gb9szEsrzXRBzarDqpdETpQeyt5v5CGe", + database="Stines-GmbH", + auth_plugin='mysql_native_password', +) +mydb.connect() +cursor = mydb.cursor() + +cursor.execute("SELECT Datenbank FROM Kunden GROUP by Datenbank") +kunden = cursor.fetchall() + +main_path = fr"/docker/app_data/data/karstenstoecker/files/Stines GmbH/1. Verwaltung/4. Verträge/2. Zulieferer - Dienstleister/MS - SPLA/Insight/Reports/{year}" +for i in kunden: + kunde = i[0].replace("-"," ").replace("ae","ä") + if os.path.exists(fr"{main_path}/{i}/{start_date_str} - {last_date_str}/RAW"): + next + else: + try: + os.mkdir(fr"{main_path}/{kunde}") + except: + next + try: + os.mkdir(fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}") + except: + next + try: + os.mkdir(fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW") + except: + next + # RAW EXPORT + ## AD - User Export START ## + with open(fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/Active-Directory-User - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv', 'w', newline='', encoding='utf-8') as csvfile: + csv_writer = csv.writer(csvfile,delimiter=";") + cursor.execute(f'SELECT * FROM `{i[0]}`.`Active-Directory-User` where importdate BETWEEN {start_date_00.timestamp()} and {start_date_23.timestamp()}') + csv_writer.writerow([i[0] for i in cursor.description]) + csv_writer.writerows(cursor.fetchall()) + ## AD - User Export ENDE ## + ## SERVER - Export START ## + with open(fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/SERVER - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv', 'w', newline='', encoding='utf-8') as csvfile: + csv_writer = csv.writer(csvfile,delimiter=";") + # print(f"SELECT Kundennummer,Name,`Server-Name`,`IP-Adresse`,CreateDate,`Windows-Key`,CPU,RAM,Speicher,Prozessor FROM `Stines-GmbH`.`Kunden-Server` where Name='{i[0]}' AND `Windows-Key` IS NOT NULL") + cursor.execute(f"SELECT Kundennummer,Name,`Server-Name`,`IP-Adresse`,CreateDate,`Windows-Key`,CPU,RAM,Speicher,Prozessor FROM `Stines-GmbH`.`Kunden-Server` where Name='{i[0]}' AND `Windows-Key` IS NOT NULL") + csv_writer.writerow([i[0] for i in cursor.description]) + csv_writer.writerows(cursor.fetchall()) + ## SERVER - Export ENDE ## + ## RDS - User Export START ## + with open(fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/RDS-User - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv', 'w', newline='', encoding='utf-8') as csvfile: + csv_writer = csv.writer(csvfile,delimiter=";") + try: + cursor.execute(f"SELECT id FROM `{i[0]}`.`Exchange-User` LIMIT 1") + except: + next + table_exists = cursor.fetchone() is not None + if table_exists: + cursor.execute(f'SELECT db1.importdate as "time",db1.CreateTimeStamp,db1.SamAccountName,db1.DisplayName, db1.EmailAddress, db1.extensionAttribute1 AS "RDS Deaktviert am", db1.extensionAttribute2 AS "Exchange Deaktviert am", db1.Description,db1.Deleted,db1.LastLogonDate,db1.City FROM `{i[0]}`.`Active-Directory-User` db1 INNER JOIN `{i[0]}`.`Active-Directory-RDS-User` db2 ON db1.SamAccountName = db2.SamAccountName WHERE db1.importdate BETWEEN {start_date_00.timestamp()} and {start_date_23.timestamp()} AND db2.SamAccountName NOT LIKE "%test%" AND db2.SamAccountName NOT LIKE "%admin%"') + else: + cursor.execute(f'SELECT db1.importdate as "time",db1.CreateTimeStamp,db1.SamAccountName,db1.DisplayName, db1.EmailAddress, db1.extensionAttribute1 AS "RDS Deaktviert am", db1.Description,db1.Deleted,db1.LastLogonDate,db1.City FROM `{i[0]}`.`Active-Directory-User` db1 INNER JOIN `{i[0]}`.`Active-Directory-RDS-User` db2 ON db1.SamAccountName = db2.SamAccountName WHERE db1.importdate BETWEEN {start_date_00.timestamp()} and {start_date_23.timestamp()} AND db2.SamAccountName NOT LIKE "%test%" AND db2.SamAccountName NOT LIKE "%admin%"') + csv_writer.writerow([i[0] for i in cursor.description]) + csv_writer.writerows(cursor.fetchall()) + if table_exists: + with open(fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/Exchange-User - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv', 'w', newline='', encoding='utf-8') as csvfile: + csv_writer = csv.writer(csvfile,delimiter=";") + cursor.execute(f'SELECT importdate as "time",SamAccountName,WindowsEmailAddress,CustomAttribute2 as "Description",WhenCreated FROM `{i[0]}`.`Exchange-User` WHERE RecipientTypeDetails = "UserMailbox" AND importdate BETWEEN {start_date_00.timestamp()} and {start_date_23.timestamp()} AND SamAccountName NOT LIKE "%test%" AND SamAccountName NOT LIKE "%admin%" AND SamAccountName NOT LIKE "%journal%" ') + csv_writer.writerow([i[0] for i in cursor.description]) + csv_writer.writerows(cursor.fetchall()) + ## RDS - User Export ENDE ## + ## RAW EXPORT ENDE ## + ## CREATE Excel Sheet from csv Export ## + ## EXPORT to AD- User Excel-Sheet ## + csv_datei = fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/RDS-User - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv' + df = pd.read_csv(csv_datei, sep=";", encoding="utf-8") + # Leere Felder mit "-" füllen + df = df.fillna("-") + df["time"] = pd.to_datetime(df["time"], unit='s') + # Excel-Datei erstellen und Daten schreiben + excel_datei = fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}/{kunde}_User_Export Stand({last_date_str}).xlsx" + if os.path.exists(excel_datei): + mode = 'a' + else: + mode = 'w' + with pd.ExcelWriter(excel_datei, engine='openpyxl', mode=f'{mode}') as writer: + df.to_excel(writer, index=False, sheet_name=f'{start_date_00.strftime("%d-%m")}') + workbook = writer.book + worksheet = writer.sheets[f'{start_date_00.strftime("%d-%m")}'] + worksheet.auto_filter.ref = "A1:J1" + # Spaltenbreite festlegen + for column in worksheet.columns: + max_length = 0 + column_letter = column[0].column_letter + for cell in column: + try: + if len(str(cell.value)) > max_length: + max_length = len(cell.value) + except: + pass + adjusted_width = (max_length + 2) * 1.2 + worksheet.column_dimensions[column_letter].width = adjusted_width + ## EXPORT to AD- User Excel-Sheet ## + csv_datei = fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/SERVER - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv' + df = pd.read_csv(csv_datei, sep=";", encoding="utf-8") + # Leere Felder mit "-" füllen + df = df.fillna("-") + # df["time"] = pd.to_datetime(df["time"], unit='s') + # Excel-Datei erstellen und Daten schreiben + excel_datei = fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}/{kunde}_Server_Export Stand({last_date_str}).xlsx" + if os.path.exists(excel_datei): + mode = 'a' + else: + mode = 'w' + with pd.ExcelWriter(excel_datei, engine='openpyxl', mode=f'{mode}') as writer: + df.to_excel(writer, index=False, sheet_name=f'{start_date_00.strftime("%d-%m")}') + workbook = writer.book + worksheet = writer.sheets[f'{start_date_00.strftime("%d-%m")}'] + worksheet.auto_filter.ref = "A1:J1" + # Spaltenbreite festlegen + for column in worksheet.columns: + max_length = 0 + column_letter = column[0].column_letter + for cell in column: + try: + if len(str(cell.value)) > max_length: + max_length = len(cell.value) + except: + pass + adjusted_width = (max_length + 2) * 1.2 + worksheet.column_dimensions[column_letter].width = adjusted_width + if table_exists: + csv_datei = fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/Exchange-User - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv' + df = pd.read_csv(csv_datei, sep=";", encoding="utf-8") + # Leere Felder mit "-" füllen + df = df.fillna("-") + df["time"] = pd.to_datetime(df["time"], unit='s') + # Excel-Datei erstellen und Daten schreiben + excel_datei = fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}/{kunde}_Mailbox_Export Stand({last_date_str}).xlsx" + if os.path.exists(excel_datei): + mode = 'a' + else: + mode = 'w' + with pd.ExcelWriter(excel_datei, engine='openpyxl', mode=f'{mode}') as writer: + df.to_excel(writer, index=False, sheet_name=f'{start_date_00.strftime("%d-%m")}') + workbook = writer.book + worksheet = writer.sheets[f'{start_date_00.strftime("%d-%m")}'] + worksheet.auto_filter.ref = "A1:E1" + # Spaltenbreite festlegen + for column in worksheet.columns: + max_length = 0 + column_letter = column[0].column_letter + for cell in column: + try: + if len(str(cell.value)) > max_length: + max_length = len(cell.value) + except: + pass + adjusted_width = (max_length + 2) * 1.2 + worksheet.column_dimensions[column_letter].width = adjusted_width + + ## CREATE Worksheet first Page + try: + os.remove(fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}/{kunde}_Komplett.xlsx") + except: + next + excel_datei = fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}/{kunde}_Komplett.xlsx" + + workbook = openpyxl.Workbook() + worksheet = workbook.active + worksheet.title = "Übersicht" + font = Font(size=20) + + font_small = Font(size=16) + + + worksheet.merge_cells('C5:G6') + worksheet.merge_cells('C7:G7') + worksheet.merge_cells('C8:E8') + worksheet.merge_cells('C10:E10') + worksheet.merge_cells('C12:E12') + worksheet.merge_cells('C14:E14') + + worksheet['C5'] = "Übersicht" + worksheet['C5'].font = font + + worksheet['C7'] = f"{start_date_str} - {last_date_str}" + worksheet['C7'].font = font_small + + worksheet['C10'] = "RDS-User" + worksheet['C10'].font = font_small + worksheet['G10'] = "=COUNTIF('RDS - User'!G2:G50;\"-\")" + + if table_exists: + worksheet['C12'] = "Exchange-User" + worksheet['C12'].font = font_small + worksheet['G12'] = "=COUNTIF('Exchange - User'!D2:D50;\"<>\"&\"*Exchange - Deaktiviert*\")+1" + else: + next + + worksheet['C14'] = "Server-Cores" + worksheet['C14'].font = font_small + worksheet['G14'] = "=COUNTIF('Server'!J2:J50)*8/2" + + workbook.save(excel_datei) + + csv_datei = fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/RDS-User - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv' + df = pd.read_csv(csv_datei, sep=";", encoding="utf-8") + # Leere Felder mit "-" füllen + df = df.fillna("-") + df["time"] = pd.to_datetime(df["time"], unit='s') + # Excel-Datei erstellen und Daten schreiben + excel_datei = fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}/{kunde}_Komplett.xlsx" + if os.path.exists(excel_datei): + mode = 'a' + else: + mode = 'w' + with pd.ExcelWriter(excel_datei, engine='openpyxl', mode=f'{mode}') as writer: + df.to_excel(writer, index=False, sheet_name=f'RDS - User') + workbook = writer.book + worksheet = writer.sheets[f'RDS - User'] + worksheet.auto_filter.ref = "A1:J1" + # Spaltenbreite festlegen + for column in worksheet.columns: + max_length = 0 + column_letter = column[0].column_letter + for cell in column: + try: + if len(str(cell.value)) > max_length: + max_length = len(cell.value) + except: + pass + adjusted_width = (max_length + 2) * 1.2 + worksheet.column_dimensions[column_letter].width = adjusted_width + + if table_exists: + csv_datei = fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/Exchange-User - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv' + df = pd.read_csv(csv_datei, sep=";", encoding="utf-8") + # Leere Felder mit "-" füllen + df = df.fillna("-") + df["time"] = pd.to_datetime(df["time"], unit='s') + # Excel-Datei erstellen und Daten schreiben + excel_datei = fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}/{kunde}_Komplett.xlsx" + if os.path.exists(excel_datei): + mode = 'a' + else: + mode = 'w' + with pd.ExcelWriter(excel_datei, engine='openpyxl', mode=f'{mode}') as writer: + df.to_excel(writer, index=False, sheet_name=f'Exchange - User') + workbook = writer.book + worksheet = writer.sheets[f'Exchange - User'] + worksheet.auto_filter.ref = "A1:E1" + # Spaltenbreite festlegen + for column in worksheet.columns: + max_length = 0 + column_letter = column[0].column_letter + for cell in column: + try: + if len(str(cell.value)) > max_length: + max_length = len(cell.value) + except: + pass + adjusted_width = (max_length + 2) * 1.2 + worksheet.column_dimensions[column_letter].width = adjusted_width + csv_datei = fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/SERVER - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv' + df = pd.read_csv(csv_datei, sep=";", encoding="utf-8") + # Leere Felder mit "-" füllen + df = df.fillna("-") + # df["time"] = pd.to_datetime(df["time"], unit='s') + # Excel-Datei erstellen und Daten schreiben + excel_datei = fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}/{kunde}_Komplett.xlsx" + if os.path.exists(excel_datei): + mode = 'a' + else: + mode = 'w' + with pd.ExcelWriter(excel_datei, engine='openpyxl', mode=f'{mode}') as writer: + df.to_excel(writer, index=False, sheet_name=f'Server') + workbook = writer.book + worksheet = writer.sheets[f'Server'] + worksheet.auto_filter.ref = "A1:J1" + # Spaltenbreite festlegen + for column in worksheet.columns: + max_length = 0 + column_letter = column[0].column_letter + for cell in column: + try: + if len(str(cell.value)) > max_length: + max_length = len(cell.value) + except: + pass + adjusted_width = (max_length + 2) * 1.2 + worksheet.column_dimensions[column_letter].width = adjusted_width +# # os.mkdir("") +# # +# # print(table_exists) +# # +# # c = canvas.Canvas(fr"{i}/{start_date_str} - {last_date_str}/PDF_Export Stand({last_date_str}).pdf", pagesize=letter) +# # # Hier können Sie IhrenCode zum Hinzufügen von Daten zur PDF einfügen +# # c.setFont("Helvetica", 10) +# # c.drawString(50, 750, f"Create Date - {datetime.now().strftime("%d-%M-%Y")}") +# # c.drawImage("Firmen-Logo.png", x=450, y=730, width=150, height=40) +# # c.setFont("Helvetica", 12) +# # c.drawString(100, 680, "REPORT der RDP / Exchange User der Firma bla") +# # +# # k = 10 +# # n = 650 +# # x = 100 +# # t = 0 +# # if n < 50: +# # if t == 1: +# # x = 400 +# # n = 650 +# # print(x) +# # print("insede") +# # else: +# # x = 250 +# # n = 650 +# # t += 1 +# # c.setFont("Helvetica", 8) +# # c.drawString(x, n, f"Zeitraum: {i}") +# # c.drawString(x, n - 10, f"Aktive RDS-User: {i}") +# # c.drawString(x, n - 20, f"Deaktivierte RDS-User: {i}") +# # if table_exists: +# # c.drawString(x, n - 30, f"Aktive EX-User: {i}") +# # c.drawString(x, n - 40, f"Deaktivierte EX-User: {i}") +# # n = n - 60 +# # c.save() +# # subprocess.Popen(["start", "output.pdf"], shell=True) +cursor.close() +mydb.close() +os.system(f'chmod 777 -R "/docker/app_data/data/karstenstoecker/files/Stines GmbH/1. Verwaltung/4. Verträge/2. Zulieferer - Dienstleister/MS - SPLA/Insight/Reports/{year}"') +os.system(f"/bin/bash -c 'docker exec -u www-data app-server /bin/bash -c 'php occ files:scan --path=karstenstoecker/files/Stines\ GmbH/1.\ Verwaltung/4.\ Verträge/2.\ Zulieferer\ -\ Dienstleister/MS\ -\ SPLA/Insight/Reports/''") + +## SEND Pushover ## +conn = http.client.HTTPSConnection("api.pushover.net:443") +conn.request("POST", "/1/messages.json", + urllib.parse.urlencode({ + "token": "avzcexyjeu7y71pcskwshyx8ytmq8i", + "user": "uo2sf2pmrtjvt8auu786fviabimimr", + "message": "Reporting was running!", + }), { "Content-type": "application/x-www-form-urlencoded" }) +conn.getresponse() + diff --git a/main.py_old b/main.py_old new file mode 100644 index 0000000..91005e4 --- /dev/null +++ b/main.py_old @@ -0,0 +1,229 @@ +import csv +import os +import mysql.connector +import pandas as pd +from datetime import datetime, timedelta +import openpyxl +# from reportlab.lib.pagesizes import letter +# from reportlab.pdfgen import canvas +import subprocess + +mydb = mysql.connector.connect( + host="172.17.1.21", + port="3306", + user="root", + password="N53yBCswuawzBzS445VNAhWVMs3N59Gb9szEsrzXRBzarDqpdETpQeyt5v5CGe", + database="Stines-GmbH", + auth_plugin='mysql_native_password', +) +mydb.connect() +cursor = mydb.cursor() + +lastdate_month=29 +month=2 + +for k in range(0,lastdate_month): + heute = datetime.now() + start_date = datetime(2024, month, 1).replace(hour=0, minute=0, second=0, microsecond=0) + start_date_00 = start_date + timedelta(days=k) + start_date_23 = start_date_00.replace(hour=23, minute=59, second=59, microsecond=0) + last_date = datetime(2024, month, lastdate_month) + year = last_date.strftime("%Y") + + cursor.execute("SELECT Datenbank FROM Kunden GROUP by Datenbank") + kunden = cursor.fetchall() + start_date_str = start_date.strftime("%d.%m") + last_date_str = last_date.strftime("%d.%m") + + main_path = fr"/docker/app_data/data/karstenstoecker/files/Stines GmbH/1. Verwaltung/4. Verträge/2. Zulieferer - Dienstleister/Microsoft Service Provider License Agreement/Insight Technology Solutions GmbH/Reports/{year}" + + for i in kunden: + kunde = i[0].replace("-"," ").replace("ae","ä") + if os.path.exists(fr"{main_path}/{i}/{start_date_str} - {last_date_str}/RAW"): + next + else: + try: + os.mkdir(fr"{main_path}/{kunde}") + except: + next + try: + os.mkdir(fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}") + except: + next + try: + os.mkdir(fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW") + except: + next + # RAW EXPORT + ## AD - User Export START ## + with open(fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/Active-Directory-User - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv', 'w', newline='', encoding='utf-8') as csvfile: + csv_writer = csv.writer(csvfile,delimiter=";") + cursor.execute(f'SELECT * FROM `{i[0]}`.`Active-Directory-User` where importdate BETWEEN {start_date_00.timestamp()} and {start_date_23.timestamp()}') + csv_writer.writerow([i[0] for i in cursor.description]) + csv_writer.writerows(cursor.fetchall()) + + ## AD - User Export ENDE ## + ## SERVER - Export START ## + with open(fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/SERVER - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv', 'w', newline='', encoding='utf-8') as csvfile: + csv_writer = csv.writer(csvfile,delimiter=";") + # print(f"SELECT Kundennummer,Name,`Server-Name`,`IP-Adresse`,CreateDate,`Windows-Key`,CPU,RAM,Speicher,Prozessor FROM `Stines-GmbH`.`Kunden-Server` where Name='{i[0]}' AND `Windows-Key` IS NOT NULL") + cursor.execute(f"SELECT Kundennummer,Name,`Server-Name`,`IP-Adresse`,CreateDate,`Windows-Key`,CPU,RAM,Speicher,Prozessor FROM `Stines-GmbH`.`Kunden-Server` where Name='{i[0]}' AND `Windows-Key` IS NOT NULL") + csv_writer.writerow([i[0] for i in cursor.description]) + csv_writer.writerows(cursor.fetchall()) + ## SERVER - Export ENDE ## + ## RDS - User Export START ## + with open(fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/RDS-User - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv', 'w', newline='', encoding='utf-8') as csvfile: + csv_writer = csv.writer(csvfile,delimiter=";") + try: + cursor.execute(f"SELECT id FROM `{i[0]}`.`Exchange-User` LIMIT 1") + except: + next + table_exists = cursor.fetchone() is not None + if table_exists: + cursor.execute(f'SELECT db1.importdate as "time",db1.CreateTimeStamp,db1.SamAccountName,db1.DisplayName, db1.EmailAddress, db1.extensionAttribute1 AS "RDS Deaktviert am", db1.extensionAttribute2 AS "Exchange Deaktviert am", db1.Description,db1.Deleted,db1.LastLogonDate,db1.City FROM `{i[0]}`.`Active-Directory-User` db1 INNER JOIN `{i[0]}`.`Active-Directory-RDS-User` db2 ON db1.SamAccountName = db2.SamAccountName WHERE db1.importdate BETWEEN {start_date_00.timestamp()} and {start_date_23.timestamp()} AND db2.SamAccountName NOT LIKE "%test%" AND db2.SamAccountName NOT LIKE "%admin%"') + else: + cursor.execute(f'SELECT db1.importdate as "time",db1.CreateTimeStamp,db1.SamAccountName,db1.DisplayName, db1.EmailAddress, db1.extensionAttribute1 AS "RDS Deaktviert am", db1.Description,db1.Deleted,db1.LastLogonDate,db1.City FROM `{i[0]}`.`Active-Directory-User` db1 INNER JOIN `{i[0]}`.`Active-Directory-RDS-User` db2 ON db1.SamAccountName = db2.SamAccountName WHERE db1.importdate BETWEEN {start_date_00.timestamp()} and {start_date_23.timestamp()} AND db2.SamAccountName NOT LIKE "%test%" AND db2.SamAccountName NOT LIKE "%admin%"') + csv_writer.writerow([i[0] for i in cursor.description]) + csv_writer.writerows(cursor.fetchall()) + if table_exists: + with open(fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/Exchange-User - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv', 'w', newline='', encoding='utf-8') as csvfile: + csv_writer = csv.writer(csvfile,delimiter=";") + cursor.execute(f'SELECT importdate as "time",SamAccountName,WindowsEmailAddress,CustomAttribute2 as "Description",WhenCreated FROM `{i[0]}`.`Exchange-User` WHERE RecipientTypeDetails = "UserMailbox" AND importdate BETWEEN {start_date_00.timestamp()} and {start_date_23.timestamp()} AND SamAccountName NOT LIKE "%test%" AND SamAccountName NOT LIKE "%admin%" AND SamAccountName NOT LIKE "%journal%" ') + csv_writer.writerow([i[0] for i in cursor.description]) + csv_writer.writerows(cursor.fetchall()) + ## RDS - User Export ENDE ## + ## RAW EXPORT ENDE ## + ## CREATE Excel Sheet from csv Export ## + ## EXPORT to AD- User Excel-Sheet ## + csv_datei = fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/RDS-User - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv' + df = pd.read_csv(csv_datei, sep=";", encoding="utf-8") + # Leere Felder mit "-" füllen + df = df.fillna("-") + df["time"] = pd.to_datetime(df["time"], unit='s') + # Excel-Datei erstellen und Daten schreiben + excel_datei = fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}/{kunde}_User_Export Stand({last_date_str}).xlsx" + if os.path.exists(excel_datei): + mode = 'a' + else: + mode = 'w' + with pd.ExcelWriter(excel_datei, engine='openpyxl', mode=f'{mode}') as writer: + df.to_excel(writer, index=False, sheet_name=f'{start_date_00.strftime("%d-%m")}') + workbook = writer.book + worksheet = writer.sheets[f'{start_date_00.strftime("%d-%m")}'] + + worksheet.auto_filter.ref = "A1:J1" + # Spaltenbreite festlegen + for column in worksheet.columns: + max_length = 0 + column_letter = column[0].column_letter + for cell in column: + try: + if len(str(cell.value)) > max_length: + max_length = len(cell.value) + except: + pass + adjusted_width = (max_length + 2) * 1.2 + worksheet.column_dimensions[column_letter].width = adjusted_width + + ## EXPORT to AD- User Excel-Sheet ## + csv_datei = fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/SERVER - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv' + df = pd.read_csv(csv_datei, sep=";", encoding="utf-8") + # Leere Felder mit "-" füllen + df = df.fillna("-") + # df["time"] = pd.to_datetime(df["time"], unit='s') + # Excel-Datei erstellen und Daten schreiben + excel_datei = fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}/{kunde}_Server_Export Stand({last_date_str}).xlsx" + if os.path.exists(excel_datei): + mode = 'a' + else: + mode = 'w' + with pd.ExcelWriter(excel_datei, engine='openpyxl', mode=f'{mode}') as writer: + df.to_excel(writer, index=False, sheet_name=f'{start_date_00.strftime("%d-%m")}') + workbook = writer.book + worksheet = writer.sheets[f'{start_date_00.strftime("%d-%m")}'] + + worksheet.auto_filter.ref = "A1:J1" + # Spaltenbreite festlegen + for column in worksheet.columns: + max_length = 0 + column_letter = column[0].column_letter + for cell in column: + try: + if len(str(cell.value)) > max_length: + max_length = len(cell.value) + except: + pass + adjusted_width = (max_length + 2) * 1.2 + worksheet.column_dimensions[column_letter].width = adjusted_width + + if table_exists: + csv_datei = fr'{main_path}/{kunde}/{start_date_str} - {last_date_str}/RAW/Exchange-User - {kunde} - {start_date_00.strftime("%Y-%m-%d")}.csv' + df = pd.read_csv(csv_datei, sep=";", encoding="utf-8") + # Leere Felder mit "-" füllen + df = df.fillna("-") + df["time"] = pd.to_datetime(df["time"], unit='s') + # Excel-Datei erstellen und Daten schreiben + excel_datei = fr"{main_path}/{kunde}/{start_date_str} - {last_date_str}/{kunde}_Mailbox_Export Stand({last_date_str}).xlsx" + if os.path.exists(excel_datei): + mode = 'a' + else: + mode = 'w' + with pd.ExcelWriter(excel_datei, engine='openpyxl', mode=f'{mode}') as writer: + df.to_excel(writer, index=False, sheet_name=f'{start_date_00.strftime("%d-%m")}') + workbook = writer.book + worksheet = writer.sheets[f'{start_date_00.strftime("%d-%m")}'] + + worksheet.auto_filter.ref = "A1:E1" + # Spaltenbreite festlegen + for column in worksheet.columns: + max_length = 0 + column_letter = column[0].column_letter + for cell in column: + try: + if len(str(cell.value)) > max_length: + max_length = len(cell.value) + except: + pass + adjusted_width = (max_length + 2) * 1.2 + worksheet.column_dimensions[column_letter].width = adjusted_width + + # os.mkdir("") + # + # print(table_exists) + # + # c = canvas.Canvas(fr"{i}/{start_date_str} - {last_date_str}/PDF_Export Stand({last_date_str}).pdf", pagesize=letter) + # # Hier können Sie IhrenCode zum Hinzufügen von Daten zur PDF einfügen + # c.setFont("Helvetica", 10) + # c.drawString(50, 750, f"Create Date - {datetime.now().strftime("%d-%M-%Y")}") + # c.drawImage("Firmen-Logo.png", x=450, y=730, width=150, height=40) + # c.setFont("Helvetica", 12) + # c.drawString(100, 680, "REPORT der RDP / Exchange User der Firma bla") + # + # k = 10 + # n = 650 + # x = 100 + # t = 0 + # if n < 50: + # if t == 1: + # x = 400 + # n = 650 + # print(x) + # print("insede") + # else: + # x = 250 + # n = 650 + # t += 1 + # c.setFont("Helvetica", 8) + # c.drawString(x, n, f"Zeitraum: {i}") + # c.drawString(x, n - 10, f"Aktive RDS-User: {i}") + # c.drawString(x, n - 20, f"Deaktivierte RDS-User: {i}") + # if table_exists: + # c.drawString(x, n - 30, f"Aktive EX-User: {i}") + # c.drawString(x, n - 40, f"Deaktivierte EX-User: {i}") + # n = n - 60 + # c.save() + # subprocess.Popen(["start", "output.pdf"], shell=True) + +cursor.close() +mydb.close() + diff --git a/pdf.py b/pdf.py new file mode 100644 index 0000000..1ec6b75 --- /dev/null +++ b/pdf.py @@ -0,0 +1,64 @@ +import openpyxl +from reportlab.pdfgen import canvas +from reportlab.pdfbase import pdfmetrics +from reportlab.pdfbase.pdfmetrics import stringWidth +from reportlab.pdfbase.ttfonts import TTFont +import os +from PyPDF2 import PdfFileReader, PdfFileMerger + +pdfmetrics.registerFont(TTFont('Arial', 'Arial.ttf')) +wb = openpyxl.load_workbook('Excel_Export Stand(31.08).xlsx') +sheet = wb.get_sheet_by_name("01-08") + +# print(sheet.cell(2,2).value) + +page_width = 2156 +page_height = 3050 +start = 200 +start_2 = 700 +spread = 80 +categories = ["Roll Number: ", "Name: ", "Gender: ", "Age: ", "Board: ", "City: ", "Email: "] + +university = "NED University Of Engineering And Technology" + + +def create_data(): + for i in range(2, 14): + std_id = sheet.cell(row=i, column=1).value + std_name = sheet.cell(row=i, column=2).value + std_gender = sheet.cell(row=i, column=3).value + std_age = sheet.cell(row=i, column=4).value + std_board = sheet.cell(row=i, column=5).value + std_city = sheet.cell(row=i, column=6).value + std_email = sheet.cell(row=i, column=8).value + + data = [std_id, std_name, std_gender, std_age, std_board, std_city, std_email] + + c = canvas.Canvas(str(std_name) + '.pdf') + c.setPageSize((page_width, page_height)) + c.setFont('Arial', 80) + text_width = stringWidth(university, 'Arial', 80) + c.drawString((page_width - text_width) / 2, 2900, university) + + y = 2500 + + for x in range(0, 7): + c.setFont('Arial', 40) + c.drawString(start, y, categories[x]) + c.drawString(start_2, y, str(data[x])) + y -= spread + + c.save() + + +def merge_data(): + files_dir = 'pytopdf' + pdf_files = [f for f in os.listdir(files_dir) if f.endswith('.pdf')] + merger = PdfFileMerger() + for filename in pdf_files: + merger.append(PdfFileReader(os.path.join(files_dir, filename, 'rb'))) + merger.write(os.path.join(files_dir, 'merged_data.pdf')) + + +create_data() +merge_data() \ No newline at end of file diff --git a/rport-installer.sh b/rport-installer.sh new file mode 100644 index 0000000..f66aa10 --- /dev/null +++ b/rport-installer.sh @@ -0,0 +1,1348 @@ +#!/bin/sh -e +MY_COMMAND="$0 $*" +exit_trap() { + # shellcheck disable=SC2181 + if [ $? -eq 0 ]; then + return 0 + fi + echo "" + echo "An error occurred." + echo "Try running in debug mode with 'sh -x ${MY_COMMAND}'" + echo "Ask for help on https://github.com/openrport/openrport-pairing/discussions/categories/help-needed " + echo "" +} +trap exit_trap EXIT + +# BEGINNING of templates/header.txt ----------------------------------------------------------------------------------| + +## +## This is the RPort client installer script. +## It helps you to quickly install the rport client on a variety of Linux distributions. +## The scripts creates a initial configuration and connects the client to your server. +## +## For any inquiries use our GitHub forum on +## https://github.com/openrport/rport-pairing/discussions/ +## +## Copyright cloudradar GmbH, Potsdam Germany, 2022 +## Maintainer openrport, Lille, France, 2023 +## Released under the MIT open-source license. +## https://github.com/openrport/rport-pairing/blob/main/LICENSE +## +# END of templates/header.txt ----------------------------------------------------------------------------------------| + +## BEGINNING of rendered template templates/linux/installer_vars.sh +# +# Dynamically inserted variables +# +FINGERPRINT="05:7f:44:a8:62:02:72:e6:e0:ab:56:f3:42:01:0a:8f" +CONNECT_URL="http://rport.stines.de:8000" +CLIENT_ID="stines" +PASSWORD="WdwCpNJGcnsuriu" + +# +# Global static installer vars +# +TMP_FOLDER=/tmp/rport-install +FORCE=1 +USE_ALTERNATIVE_MACHINEID=0 +LOG_DIR=/var/log/rport +LOG_FILE=${LOG_DIR}/rport.log +## END of rendered template templates/linux/installer_vars.sh + + +# BEGINNING of templates/linux/vars.sh -------------------------------------------------------------------------------| + +# +# Global Variables for installation and update +# +CONF_DIR=/etc/rport +CONFIG_FILE=${CONF_DIR}/rport.conf +USER=rport +ARCH=$(uname -m | sed s/"armv\(6\|7\)l"/'armv\1'/ | sed s/aarch64/arm64/) +# END of templates/linux/vars.sh -------------------------------------------------------------------------------------| + + +# BEGINNING of templates/linux/functions.sh --------------------------------------------------------------------------| + +set -e +if which tput >/dev/null 2>&1; then + true +else + alias tput=true +fi + +throw_fatal() { + echo 2>&1 "[!] $1" + echo "[=] Fatal Exit. Don't give up. Good luck with the next try." + false +} + +throw_hint() { + echo "[>] $1" +} + +throw_info() { + echo "$(tput setab 2 2>/dev/null)$(tput setaf 7 2>/dev/null)[*]$(tput sgr 0 2>/dev/null) $1" +} + +throw_warning() { + echo "[:] $1" +} + +throw_debug() { + echo "$(tput setab 4 2>/dev/null)$(tput setaf 7 2>/dev/null)[-]$(tput sgr 0 2>/dev/null) $1" +} + +wait_for_rport() { + i=0 + while [ "$i" -lt 40 ]; do + pidof rport >/dev/null 2>&1 && return 0 + echo "$i waiting for rport process to come up ..." + sleep 0.2 + i=$((i + 1)) + done + return 1 +} + +is_rport_subprocess() { + if [ -n "$1" ]; then + SEARCH_PID=$1 + else + SEARCH_PID=$$ + fi + PARENT_PID=$(ps -o ppid= -p "$SEARCH_PID" | tr -d ' ') + PARENT_NAME=$(ps -p "$PARENT_PID" -o comm=) + if [ "$PARENT_NAME" = "rport" ]; then + return 0 + elif [ "$PARENT_PID" -eq 1 ]; then + return 1 + fi + is_rport_subprocess "$PARENT_PID" +} +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: is_available +# DESCRIPTION: Check if a command is available on the system. +# PARAMETERS: command name +# RETURNS: 0 if available, 1 otherwise +#---------------------------------------------------------------------------------------------------------------------- +is_available() { + if command -v "$1" >/dev/null 2>&1; then + return 0 + else + return 1 + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: uninstall +# DESCRIPTION: Uninstall everything and remove the user +#---------------------------------------------------------------------------------------------------------------------- +uninstall() { + if pgrep rportd >/dev/null; then + echo 1>&2 "You are running the rportd server on this machine. Uninstall manually." + exit 0 + fi + stop_rport >/dev/null 2>&1 || true + rc-service rport stop >/dev/null 2>&1 || true + pkill -9 rport >/dev/null 2>&1 || true + rport --service uninstall >/dev/null 2>&1 || true + FILES="/usr/local/bin/rport + /usr/local/bin/rport + /etc/systemd/system/rport.service + /etc/sudoers.d/rport-update-status + /etc/sudoers.d/rport-all-cmd + /usr/local/bin/tacoscript + /etc/init.d/rport + /var/run/rport.pid + /etc/runlevels/default/rport + /etc/apt/sources.list.d/rport.list" + for FILE in $FILES; do + if [ -e "$FILE" ]; then + rm -f "$FILE" && echo " [ DELETED ] File $FILE" + fi + done + if id rport >/dev/null 2>&1; then + if is_available deluser; then + deluser --remove-home rport >/dev/null 2>&1 || true + deluser --only-if-empty --group rport >/dev/null 2>&1 || true + elif is_available userdel; then + userdel -r -f rport >/dev/null 2>&1 + fi + if is_available groupdel; then + groupdel -f rport >/dev/null 2>&1 || true + fi + echo " [ DELETED ] User rport" + fi + FOLDERS="/etc/rport + /var/log/rport + /var/lib/rport" + for FOLDER in $FOLDERS; do + if [ -e "$FOLDER" ]; then + rm -rf "$FOLDER" && echo " [ DELETED ] Folder $FOLDER" + fi + done + if dpkg -l 2>&1 | grep -q "rport.*Remote access"; then + apt-get -y remove --purge rport + fi + echo "RPort client successfully uninstalled." +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: print_distro +# DESCRIPTION: print name of the distro +#---------------------------------------------------------------------------------------------------------------------- +print_distro() { + if [ -e /etc/os-release ]; then + # shellcheck source=/dev/null + . /etc/os-release 2>/dev/null || true + echo "Detected Linux Distribution: ${PRETTY_NAME}" + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: has_sudo +# DESCRIPTION: Check if sudo is installed and sudo rules can be managed as separated files +# RETURNS: 0 (success, sudo os present), 1 (fail, sudo can't be used by rport) +#---------------------------------------------------------------------------------------------------------------------- +has_sudo() { + if ! which sudo >/dev/null 2>&1; then + return 1 + fi + if [ -e /etc/sudoers.d/ ]; then + return 0 + fi + return 1 +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: create_sudoers_all +# DESCRIPTION: create a sudoers file to grant full sudo right to the rport user +#---------------------------------------------------------------------------------------------------------------------- +create_sudoers_all() { + SUDOERS_FILE=/etc/sudoers.d/rport-all-cmd + if [ -e "$SUDOERS_FILE" ]; then + throw_info "You already have a $SUDOERS_FILE. Not changing." + return 1 + fi + + if has_sudo; then + echo "# +# This file has been auto-generated during the installation of the rport client. +# Change to your needs or delete. +# +${USER} ALL=(ALL) NOPASSWD:ALL +" >$SUDOERS_FILE + echo "A $SUDOERS_FILE has been created. Please review and change to your needs." + else + echo "You don't have sudo installed. No sudo rules created. RPort will not be able to get elevated right." + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: create_sudoers_updates +# DESCRIPTION: create a sudoers file to allow rport supervise the update status +#---------------------------------------------------------------------------------------------------------------------- +create_sudoers_updates() { + SUDOERS_FILE=/etc/sudoers.d/rport-update-status + if [ -e "$SUDOERS_FILE" ]; then + throw_info "You already have a $SUDOERS_FILE. Not changing." + return 0 + fi + + if has_sudo; then + echo '# +# This file has been auto-generated during the installation of the rport client. +# Change to your needs. +#' >$SUDOERS_FILE + if is_available apt-get; then + echo "${USER} ALL=NOPASSWD: SETENV: /usr/bin/apt-get update -o Debug\:\:NoLocking=true" >>$SUDOERS_FILE + fi + #if is_available yum;then + # echo 'rport ALL=NOPASSWD: SETENV: /usr/bin/yum *'>>$SUDOERS_FILE + #fi + #if is_available dnf;then + # echo 'rport ALL=NOPASSWD: SETENV: /usr/bin/dnf *'>>$SUDOERS_FILE + #fi + if is_available zypper; then + echo "${USER} ALL=NOPASSWD: SETENV: /usr/bin/zypper refresh *" >>$SUDOERS_FILE + fi + #if is_available apk;then + # echo 'rport ALL=NOPASSWD: SETENV: /sbin/apk *'>>$SUDOERS_FILE + #fi + echo "A $SUDOERS_FILE has been created. Please review and change to your needs." + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: abort +# DESCRIPTION: Exit the script with an error message. +#---------------------------------------------------------------------------------------------------------------------- +abort() { + echo >&2 "$1 Exit!" + clean_up + exit 1 +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: confirm +# DESCRIPTION: Print a success message. +#---------------------------------------------------------------------------------------------------------------------- +confirm() { + echo "Success: $1" +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: check_prerequisites +# DESCRIPTION: Check if prerequisites are fulfilled. +#---------------------------------------------------------------------------------------------------------------------- + +check_prerequisites() { + if [ "$(id -u)" -ne 0 ]; then + abort "Execute as root or use sudo." + fi + + if command -v sed >/dev/null 2>&1; then + true + else + abort "sed command missing. Make sure sed is in your path." + fi + + if command -v tar >/dev/null 2>&1; then + true + else + abort "tar command missing. Make sure tar is in your path." + fi +} + +is_terminal() { + if echo "$TERM" | grep -q "^xterm"; then + return 0 + else + echo 1>&2 "You are not on a terminal. Please use command line switches to avoid interactive questions." + return 1 + fi +} + +update_tacoscript() { + TACO_VERSION=$(/usr/local/bin/tacoscript --version | grep -o "Version:.*" | awk '{print $2}') + cd /tmp + test -e tacoscript.tar.gz && rm -f tacoscript.tar.gz + curl -LSso tacoscript.tar.gz "https://download.rport.io/tacoscript/${RELEASE}/?arch=Linux_${ARCH}>=$TACO_VERSION" + if tar xzf tacoscript.tar.gz 2>/dev/null; then + echo "" + throw_info "Updating Tacoscript from ${TACO_VERSION} to latest ${RELEASE} $(./tacoscript --version | grep -o "Version:.*")" + mv -f /tmp/tacoscript /usr/local/bin/tacoscript + else + throw_info "Nothing to do. Tacoscript is on the latest version ${TACO_VERSION}." + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: install_tacoscript +# DESCRIPTION: install Tacoscript on Linux +#---------------------------------------------------------------------------------------------------------------------- +install_tacoscript() { + if [ -e /usr/local/bin/tacoscript ]; then + throw_info "Tacoscript already installed. Checking for updates ..." + update_tacoscript + return 0 + fi + cd /tmp + test -e tacoscript.tar.gz && rm -f tacoscript.tar.gz + curl -Ls "https://download.rport.io/tacoscript/${RELEASE}/?arch=Linux_${ARCH}" -o tacoscript.tar.gz + tar xvzf tacoscript.tar.gz -C /usr/local/bin/ tacoscript + rm -f tacoscript.tar.gz + echo "Tacoscript installed $(/usr/local/bin/tacoscript --version)" +} + +version_to_int() { + echo "$1" | + awk -v 'maxsections=3' -F'.' 'NF < maxsections {printf("%s",$0);for(i=NF;i= maxsections {print}' | + awk -v 'maxdigits=3' -F'.' '{print $1*10^(maxdigits*2)+$2*10^(maxdigits)+$3}' +} + +runs_with_selinux() { + if command -v getenforce >/dev/null 2>&1 && getenforce | grep -q Enforcing; then + return 0 + else + return 1 + fi +} + +enable_file_reception() { + if [ "$(version_to_int "$TARGET_VERSION")" -lt 6005 ]; then + # Version does not handle file reception yet. + return 0 + fi + if [ "$ENABLE_FILEREC" -eq 0 ]; then + echo "File reception disabled." + FILEREC_CONF="false" + else + echo "File reception enabled." + FILEREC_CONF="true" + fi + if grep -q '\[file-reception\]' "$CONFIG_FILE"; then + echo "File reception already configured" + else + cat <>"$CONFIG_FILE" + + +[file-reception] + ## Receive files pushed by the server, enabled by default + # enabled = true + ## The rport client will reject writing files to any of the following folders and its subfolders. + ## https://oss.rport.io/docs/no18-file-reception.html + ## Wildcards (glob) are supported. + ## Linux defaults + # protected = ['/bin', '/sbin', '/boot', '/usr/bin', '/usr/sbin', '/dev', '/lib*', '/run'] + ## Windows defaults + # protected = ['C:\Windows\', 'C:\ProgramData'] + +EOF + fi + toml_set "$CONFIG_FILE" file-reception enabled $FILEREC_CONF + # Clean up from pre-releases + test -e /etc/sudoers.d/rport-filepush && rm -f /etc/sudoers.d/rport-filepush + if [ "$ENABLE_FILEREC_SUDO" -eq 0 ]; then + # File receptions sudo rules not desired, end this function here + return 0 + fi + # Create a sudoers file + FILERCV_SUDO="/etc/sudoers.d/rport-filereception" + if [ -e $FILERCV_SUDO ]; then + echo "Sudo rule $FILERCV_SUDO already exists" + else + cat <$FILERCV_SUDO +# The following rule allows the rport client to change the ownership of any file retrieved from the rport server +rport ALL=NOPASSWD: /usr/bin/chown * /var/lib/rport/filepush/*_rport_filepush + +# The following rules allows the rport client to move copied files to any folder +rport ALL=NOPASSWD: /usr/bin/mv /var/lib/rport/filepush/*_rport_filepush * + +EOF + fi +} + +enable_lan_monitoring() { + if [ "$(version_to_int "$TARGET_VERSION")" -lt 5008 ]; then + # Version does not handle network interfaces yet. + return 0 + fi + if grep "^\s*net_[wl]" "$CONFIG_FILE"; then + # Network interfaces already configured + return 0 + fi + echo "Enabling Network monitoring" + for IFACE in /sys/class/net/*; do + IFACE=$(basename "${IFACE}") + [ "$IFACE" = 'lo' ] && continue + if ip addr show "$IFACE" | grep -E -q "inet (10|192\.168|172\.16)\."; then + # Private IP + NET_LAN="$IFACE" + else + # Public IP + NET_WAN="$IFACE" + fi + done + if [ -n "$NET_LAN" ]; then + sed -i "/^\[monitoring\]/a \ \ net_lan = ['${NET_LAN}' , '1000' ]" "$CONFIG_FILE" + fi + if [ -n "$NET_WAN" ]; then + sed -i "/^\[monitoring\]/a \ \ net_wan = ['${NET_WAN}' , '1000' ]" "$CONFIG_FILE" + fi +} + +detect_interpreters() { + if [ "$(version_to_int "$TARGET_VERSION")" -lt 5008 ]; then + # Version does not handle interpreters yet. + return 0 + fi + if grep -q "\[interpreter\-aliases\]" "$CONFIG_FILE"; then + # Config already updated + true + else + echo "Updating config with new interpreter-aliases ..." + echo '[interpreter-aliases]' >>"$CONFIG_FILE" + fi + SEARCH="bash zsh ksh csh python3 python2 perl pwsh fish" + for ITEM in $SEARCH; do + FOUND=$(command -v "$ITEM" 2>/dev/null || true) + if [ -z "$FOUND" ]; then + continue + fi + echo "Interpreter '$ITEM' found in '$FOUND'" + if grep -q -E "^\s*$ITEM =" "$CONFIG_FILE"; then + echo "Interpreter '$ITEM' already registered." + continue + fi + # Append the found interpreter to the config + sed -i "/^\[interpreter-aliases\]/a \ \ $ITEM = \"$FOUND\"" "${CONFIG_FILE}" + done +} + +toml_set() { + TOML_FILE="$1" + BLOCK="$2" + KEY="$3" + VALUE="$4" + if [ -w "$TOML_FILE" ]; then + true + else + echo 2>&1 "$TOML_FILE does not exist or is not writable." + return 1 + fi + if grep -q "\[$BLOCK\]" "$TOML_FILE"; then + true + else + echo 2>&1 "$TOML_FILE has no block [$BLOCK]" + return 1 + fi + LINE=$(grep -n -A100 "\[$BLOCK\]" "$TOML_FILE" | grep "${KEY} = ") + if [ -z "$LINE" ]; then + echo 2>&1 "Key $KEY not found in block $BLOCK" + return 1 + fi + LINE_NO=$(echo "$LINE" | cut -d'-' -f1) + sed -i "${LINE_NO}s/.*/ ${KEY} = ${VALUE}/" "$TOML_FILE" +} + +gen_uuid() { + if [ -e /proc/sys/kernel/random/uuid ]; then + cat /proc/sys/kernel/random/uuid + return 0 + fi + if which uuidgen >/dev/null 2>&1; then + uuidgen + return 0 + fi + if which dbus-uuidgen >/dev/null 2>&1; then + dbus-uuidgen + return 0 + fi + # Use a internet-based fallback + curl -s https://www.uuidtools.com/api/generate/v4 | tr -d '"[]' +} + +get_ip_from_fqdn() { + if which getent >/dev/null; then + getent hosts "$1" | awk '{ print $1 }' + return 0 + fi + ping "$1" -c 1 -q 2>&1 | grep -Po "(\d{1,3}\.){3}\d{1,3}" +} + +start_rport() { + if is_available systemctl; then + systemctl daemon-reload + systemctl start rport + systemctl enable rport + elif [ -e /etc/init/rport.conf ]; then + # We are on an upstart system + start rport + elif is_available service; then + service rport start + fi + if pidof rport >/dev/null 2>&1; then + return 0 + else + return 1 + fi +} + +stop_rport() { + if is_available systemctl; then + systemctl stop rport + elif [ -e /etc/init/rport.conf ]; then + # We are on an upstart system + stop rport + elif is_available service; then + service rport stop + fi +} + +backup_config() { + if [ -z "$CONFIG_FILE" ]; then + throw_fatal "backup_config() \$CONFIG_FILE undefined." + fi + CONFIG_BACKUP="/tmp/.rport-conf.$(date +%s)" + cp "$CONFIG_FILE" "$CONFIG_BACKUP" + throw_debug "Configuration file copied to $CONFIG_BACKUP" +} + +clean_up_legacy_installation() { + # If this is a migration from the old none deb-based installation, clean up + if [ -e /etc/systemd/system/rport.service ]; then + throw_info "Removing old systemd service /etc/systemd/system/rport.service" + rm -f /etc/systemd/system/rport.service + systemctl daemon-reload + fi + if [ -e /usr/local/bin/rport ]; then + throw_info "Removing old version /usr/local/bin/rport" + rm -f /usr/local/bin/rport + fi +} + +install_via_deb_repo() { + if [ -z "$RELEASE" ]; then + throw_fatal "install_via_deb_repo() \$RELEASE undefined" + fi + validate_custom_user + if [ -e /etc/apt/trusted.gpg.d/rport.gpg ] && dpkg -l | grep -q rport; then + throw_info "System is already using the rport deb repo." + else + throw_info "RPort will use Debian package ..." + # shellcheck source=/dev/null + . /etc/os-release + if [ -n "$UBUNTU_CODENAME" ]; then + CODENAME=$UBUNTU_CODENAME + else + CODENAME=$VERSION_CODENAME + fi + curl -sf https://repo.openrport.io/dearmor.gpg >/etc/apt/trusted.gpg.d/openrport.gpg + echo "deb [signed-by=/etc/apt/trusted.gpg.d/openrport.gpg] https://repo.openrport.io/deb ${CODENAME} ${RELEASE}" >/etc/apt/sources.list.d/rport.list + fi + apt-get update + if dpkg -s rport >/dev/null 2>&1 && ! [ -e /etc/rport/rport.conf ]; then + throw_warning "Broken DEB package installation found." + throw_debug "Will remove old package first." + apt-get -y --purge remove rport + fi + DEBIAN_FRONTEND=noninteractive apt-get --yes -o Dpkg::Options::="--force-confold" install rport + TARGET_VERSION=$(rport --version | cut -d" " -f2) + clean_up_legacy_installation +} + +install_via_rpm_repo() { + if [ -z "$RELEASE" ]; then + throw_fatal "install_via_rpm_repo() \$RELEASE undefined" + fi + validate_custom_user + if [ -e /etc/yum.repos.d/openrport.repo ] && rpm -qa | grep -q rport; then + throw_info "System is already using the rport yum repo." + else + throw_info "RPort will use RPM package ..." + rpm --import https://repo.openrport.io/key.gpg + cat </etc/yum.repos.d/rport.repo +[rport-stable] +name=RPort $RELEASE +baseurl=https://repo.openrport.io/rpm/$RELEASE/ +enabled=1 +gpgcheck=1 +gpgkey=https://repo.openrport.io/key.gpg +EOF + fi + dnf -y install rport --refresh + TARGET_VERSION=$(rport --version | cut -d" " -f2) + clean_up_legacy_installation +} + +validate_custom_user() { + if [ "$USER" != "rport" ]; then + throw_fatal "RPM/DEB packages cannot be used with a custom user. Try '-p'" + fi +} + +# Check if it's a supported debian system +is_debian() { + if [ "$NO_REPO" -eq 1 ]; then + return 1 + fi + if which apt-get >/dev/null 2>&1 && test -e /etc/apt/sources.list.d/; then + true + else + return 1 + fi + DIST_SUPPORTED="jammy focal bionic bullseye buster bookworm" + for DIST in $DIST_SUPPORTED; do + if grep -qi "CODENAME.*$DIST" /etc/os-release; then + return 0 + fi + done + return 1 +} + +is_rhel() { + if [ "$NO_REPO" -eq 1 ]; then + return 1 + fi + if grep -q "VERSION=.[6-7]" /etc/os-release; then + throw_info "RHEL/CentOS too old for RPM installation. Switching to tar.gz package." + return 1 + fi + + if which rpm >/dev/null 2>&1 && test -e /etc/yum.repos.d; then + return 0 + fi + return 1 +} + +validate_pkg_url() { + if echo "${PKG_URL}" | grep -q -E "https*:\/\/.*_linux_$(uname -m)\.(tar\.gz|deb|rpm)$"; then + true + else + throw_fatal "Invalid PKG_URL '$PKG_URL'." + fi +} + +download_pkg_url() { + DL_AUTH="" + if [ -n "$RPORT_INSTALLER_DL_USERNAME" ] && [ -n "$RPORT_INSTALLER_DL_PASSWORD" ]; then + DL_AUTH="-u ${RPORT_INSTALLER_DL_USERNAME}:${RPORT_INSTALLER_DL_PASSWORD}" + throw_info "Download will use HTTP basic authentication" + fi + throw_info "Downloading from ${PKG_URL} ..." + PKG_DOWNLOAD=$(mktemp) + # shellcheck disable=SC2086 + curl -LSs "${PKG_URL}" ${DL_AUTH} >${PKG_DOWNLOAD} + if [ -n "$(find "${PKG_DOWNLOAD}" -empty)" ]; then + rm -f "${PKG_DOWNLOAD}" + throw_fatal "Download to ${PKG_DOWNLOAD} failed" + fi + throw_info "Download to ${PKG_DOWNLOAD} completed" +} + +install_from_deb_download() { + validate_pkg_url + if echo "${PKG_URL}" | grep -q "deb$"; then + true + else + throw_fatal "URL not pointing to a debian package" + fi + download_pkg_url + mv "${PKG_DOWNLOAD}" "${PKG_DOWNLOAD}".deb + PKG_DOWNLOAD=${PKG_DOWNLOAD}.deb + chmod 0644 "${PKG_DOWNLOAD}" + throw_info "Installing debian package ${PKG_DOWNLOAD}" + DEBIAN_FRONTEND=noninteractive apt-get --yes -o Dpkg::Options::="--force-confold" install "${PKG_DOWNLOAD}" + rm -f "${PKG_DOWNLOAD}" + clean_up_legacy_installation +} + +install_from_rpm_download() { + validate_pkg_url + if echo "${PKG_URL}" | grep -q "rpm$"; then + true + else + throw_fatal "URL not pointing to an rpm package" + fi + download_pkg_url + throw_info "Installing rpm package" + rpm -U "${PKG_DOWNLOAD}" + rm -f "${PKG_DOWNLOAD}" + clean_up_legacy_installation +} + +abort_on_rport_subprocess() { + if is_rport_subprocess; then + throw_hint "Execute the rport update in a process decoupled from its parent, e.g." + throw_hint ' nohup sh -c "curl -s https://pairing.openrport.io/update|sh" >/tmp/rport-update.log 2>&1 &' + throw_fatal "You cannot update rport from an rport subprocess." + fi +} + +# END of templates/linux/functions.sh --------------------------------------------------------------------------------| + + +# BEGINNING of templates/linux/install.sh ----------------------------------------------------------------------------| + +set -e +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: prepare +# DESCRIPTION: Create a temporary folder and prepare the system to execute the installation +#---------------------------------------------------------------------------------------------------------------------- +prepare() { + test -e "${TMP_FOLDER}" && rm -rf "${TMP_FOLDER}" + mkdir "${TMP_FOLDER}" + cd "${TMP_FOLDER}" +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: cleanup +# DESCRIPTION: Remove the temporary folder and cleanup any leftovers after script has ended +#---------------------------------------------------------------------------------------------------------------------- +clean_up() { + cd /tmp + rm -rf "${TMP_FOLDER}" +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: test_connection +# DESCRIPTION: Check if the RPort server is reachable or abort. +#---------------------------------------------------------------------------------------------------------------------- +test_connection() { + CONN_TEST=$(curl -vIs -m5 "${CONNECT_URL}" 2>&1 || true) + if echo "${CONN_TEST}" | grep -q "Connected to"; then + confirm "${CONNECT_URL} is reachable. All good." + else + echo "$CONN_TEST" + echo "" + echo "Testing the connection to the RPort server on ${CONNECT_URL} failed." + echo "* Check your internet connection and firewall rules." + echo "* Check if a transparent HTTP proxy is sniffing and blocking connections." + echo "* Check if a virus scanner is inspecting HTTP connections." + abort "FATAL: No connection to the RPort server." + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: download_and_extract +# DESCRIPTION: Download the package from Github and unpack to the temp folder +# https://downloads.openrport.io/ acts a redirector service +# returning the real download URL of GitHub in a more handy fashion +#---------------------------------------------------------------------------------------------------------------------- +download_and_extract() { + cd "${TMP_FOLDER}" + # Download the tar.gz package + if is_available curl; then + curl -LSs "https://downloads.openrport.io/rport/${RELEASE}/latest.php?arch=Linux_${ARCH}" -o rport.tar.gz + elif is_available wget; then + wget -q "https://downloads.openrport.io/rport/${RELEASE}/latest.php?arch=Linux_${ARCH}" -O rport.tar.gz + else + abort "No download tool found. Install curl or wget." + fi + # Unpack + tar xzf rport.tar.gz +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: download_and_extract_from_url +# DESCRIPTION: Download the package from any URL and unpack to the temp folder +#---------------------------------------------------------------------------------------------------------------------- +download_and_extract_from_url() { + cd "${TMP_FOLDER}" + ARCH=$(uname -m) + DL_AUTH="" + DL="rport.tar.gz" + # Use a specific version + if echo "$PKG_URL" | grep -q -E "^https?:\/\/.*\_linux_${ARCH}.tar.gz"; then + DOWNLOAD_URL="$PKG_URL" + else + echo "PKG_URL does not match 'http(s)://... _linux_${ARCH}.tar.gz'" + abort "Invalid download URL." + fi + if [ -n "$RPORT_INSTALLER_DL_USERNAME" ] && [ -n "$RPORT_INSTALLER_DL_PASSWORD" ]; then + DL_AUTH="-u ${RPORT_INSTALLER_DL_USERNAME}:${RPORT_INSTALLER_DL_PASSWORD}" + confirm "Download will use HTTP basic authentication" + fi + echo "Downloading from ${DOWNLOAD_URL}" + [ -e "${DL}" ] && rm -f "${DL}" + # shellcheck disable=SC2086 + curl -LSs "${DOWNLOAD_URL}" -o "${DL}" ${DL_AUTH} + echo "Verifying download" + FILES_IN_TAR=$(tar tzf "${DL}") + confirm "Package contains $(echo "$FILES_IN_TAR" | wc -w) files" + tar xzf "${DL}" rport + tar xzf "${DL}" rport.example.conf + rm -f "${DL}" +} +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: install_bin +# DESCRIPTION: Install a binary located in the temp folder to /usr/local/bin +# PARAMETERS: binary name relative to the temp folder +#---------------------------------------------------------------------------------------------------------------------- +install_bin() { + EXEC_BIN=/usr/local/bin/${1} + if [ -e "$EXEC_BIN" ]; then + if [ "$FORCE" -eq 0 ]; then + abort "${EXEC_BIN} already exists. Use -f to overwrite." + fi + fi + mv "${TMP_FOLDER}/${1}" "${EXEC_BIN}" + confirm "${1} installed to ${EXEC_BIN}" + TARGET_VERSION=$(${EXEC_BIN} --version | awk '{print $2}') + confirm "RPort $TARGET_VERSION installed to $EXEC_BIN" +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: install_config +# DESCRIPTION: Install an example config located in the temp folder to /etc/rport +# PARAMETERS: config name relative to the temp folder without suffix .example.conf +#---------------------------------------------------------------------------------------------------------------------- +install_config() { + test -e "$CONF_DIR" || mkdir "$CONF_DIR" + CONFIG_FILE=${CONF_DIR}/${1}.conf + if [ -e "${CONFIG_FILE}" ]; then + true + elif [ -e "${TMP_FOLDER}/rport.example.conf" ]; then + mv "${TMP_FOLDER}/rport.example.conf" "${CONFIG_FILE}" + else + throw_hint "If you have used the RPort RPM or DEB package previously, remove it first using the package manager." + throw_fatal "No rport.conf file found." + fi + confirm "${CONFIG_FILE} created." +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: create_user +# DESCRIPTION: Create a system user "rport" +#---------------------------------------------------------------------------------------------------------------------- +create_user() { + confirm "RPort will run as user ${USER}" + if id "${USER}" >/dev/null 2>&1; then + confirm "User ${USER} already exist." + else + if is_available useradd; then + useradd -r -d /var/lib/rport -m -s /bin/false -U -c "System user for rport client" $USER + elif is_available adduser; then + addgroup rport + adduser -h /var/lib/rport -s /bin/false -G rport -S -D $USER + else + abort "No command found to add a user" + fi + fi +# test -e "$LOG_DIR" || mkdir -p "$LOG_DIR" +# test -e /var/lib/rport/scripts || mkdir -p /var/lib/rport/scripts +# chown "${USER}":root "$LOG_DIR" +# chown "${USER}":root /var/lib/rport/scripts +# chmod 0700 /var/lib/rport/scripts +# chown "${USER}":root "$CONFIG_FILE" +# chmod 0640 "$CONFIG_FILE" +# chown root:root /usr/local/bin/rport +# chmod 0755 /usr/local/bin/rport +} + +set_file_and_dir_owner() { + test -e "$LOG_DIR" || mkdir -p "$LOG_DIR" + test -e /var/lib/rport/scripts || mkdir -p /var/lib/rport/scripts + chown "${USER}":root "$LOG_DIR" + chown "${USER}":root /var/lib/rport/scripts + chmod 0700 /var/lib/rport/scripts + chown "${USER}":root "$CONFIG_FILE" + chmod 0640 "$CONFIG_FILE" + if [ -e /usr/local/bin/rport ]; then + chown root:root /usr/local/bin/rport + chmod 0755 /usr/local/bin/rport + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: create_systemd_service +# DESCRIPTION: Install a systemd service file +#---------------------------------------------------------------------------------------------------------------------- +create_systemd_service() { + if [ -e /lib/systemd/system/rport.service ]; then + echo "Systemd service already present." + else + echo "Installing systemd service for rport" + test -e /etc/systemd/system/rport.service && rm -f /etc/systemd/system/rport.service + /usr/local/bin/rport --service install --service-user "${USER}" --config /etc/rport/rport.conf + fi + start_rport +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: create_openrc_service +# DESCRIPTION: Install a oprnrc service file +#---------------------------------------------------------------------------------------------------------------------- +create_openrc_service() { + echo "Installing openrc service for rport" + cat </etc/init.d/rport +#!/sbin/openrc-run +command="/usr/local/bin/rport" +command_args="-c /etc/rport/rport.conf" +command_user="${USER}" +command_background=true +pidfile=/var/run/rport.pid +EOF + chmod 0755 /etc/init.d/rport + rc-service rport start + rc-update add rport default +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: prepare_server_cofnig +# DESCRIPTION: Make changes to the example config to give the user a better starting point +#---------------------------------------------------------------------------------------------------------------------- +prepare_config() { + echo "Preparing $CONFIG_FILE" + sed -i "s|#*server = .*|server = \"${CONNECT_URL}\"|g" "$CONFIG_FILE" + sed -i "s/#*auth = .*/auth = \"${CLIENT_ID}:${PASSWORD}\"/g" "$CONFIG_FILE" + sed -i "s/#*fingerprint = .*/fingerprint = \"${FINGERPRINT}\"/g" "$CONFIG_FILE" + sed -i "s/#*log_file = .*C.*Program Files.*/""/g" "$CONFIG_FILE" + sed -i "s/#*log_file = /log_file = /g" "$CONFIG_FILE" + sed -i "s|#updates_interval = '4h'|updates_interval = '4h'|g" "$CONFIG_FILE" + if [ "$ENABLE_COMMANDS" -eq 1 ]; then + sed -i "s/#allow = .*/allow = ['.*']/g" "$CONFIG_FILE" + sed -i "s/#deny = .*/deny = []/g" "$CONFIG_FILE" + sed -i '/^\[remote-scripts\]/a \ \ enabled = true' "$CONFIG_FILE" + sed -i "s|# script_dir = '/var/lib/rport/scripts'|script_dir = '/var/lib/rport/scripts'|g" "$CONFIG_FILE" + else + sed -i '/^\[remote-commands\]/a \ \ enabled = false' "$CONFIG_FILE" + fi + + # Set the hostname. + if grep -Eq "\s+use_hostname = true" "$CONFIG_FILE"; then + # For versions >= 0.5.9 + # Just insert an example. + sed -i "s/#name = .*/#name = \"$(get_hostname)\"/g" "$CONFIG_FILE" + else + # Older versions + # Insert a hardcoded name + sed -i "s/#*name = .*/name = \"$(get_hostname)\"/g" "$CONFIG_FILE" + fi + + # Set the machine_id + if [ -n "$MACHINE_ID" ]; then + #User wants a hard-coded client id + sed -i "s/.*use_system_id = .*/ use_system_id = false/g" "$CONFIG_FILE" + sed -i "s/#id = .*/id = \"$MACHINE_ID\"/g" "$CONFIG_FILE" + echo "Using a random hard-coded client id not based on /etc/machine-id" + else + if grep -Eq "\s+use_system_id = true" "$CONFIG_FILE" && [ -e /etc/machine-id ]; then + # Versions >= 0.5.9 read it dynamically, nothing to do here + echo "Using /etc/machine-id as rport client id" + else + # Older versions need a hard-coded id in the rport.conf, preferably based on /etc/machine-id + sed -i "s/#id = .*/id = \"$(machine_id)\"/g" "$CONFIG_FILE" + fi + fi + + # Activate client attributes + if get_geodata; then + LABELS="\"city\":\"${CITY}\", \"country\":\"${COUNTRY}\"" + fi + if [ -n "$XTAG" ]; then + XTAG="\"$XTAG\"" + fi + CLIENT_ATTRIBUTES="/var/lib/rport/client_attributes.json" + if [ -e /var/lib/rport ]; then + true + else + mkdir /var/lib/rport + chown "${USER}":root /var/lib/rport + fi + cat <$CLIENT_ATTRIBUTES +{ + "tags": [${TAGS}], + "labels": { ${LABELS} } +} +EOF + sed -i "s|#attributes_file_path = \"/var/.*|attributes_file_path = \"${CLIENT_ATTRIBUTES}\"|g" "$CONFIG_FILE" + chown "${USER}" "${CLIENT_ATTRIBUTES}" +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: get_hostname +# DESCRIPTION: Try to get the hostname from various sources +#---------------------------------------------------------------------------------------------------------------------- +get_hostname() { + hostname -f 2>/dev/null && return 0 + hostname 2>/dev/null && return 0 + cat /etc/hostname 2>/dev/null && return 0 + LANG=en hostnamectl | grep hostname | grep -v 'n/a' | cut -d':' -f2 | tr -d ' ' +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: machine_id +# DESCRIPTION: Try to get a unique machine id form different locations. +# Generate one based on the hostname as a fallback. +#---------------------------------------------------------------------------------------------------------------------- +machine_id() { + if [ -e /etc/machine-id ]; then + cat /etc/machine-id + return 0 + fi + + if [ -e /var/lib/dbus/machine-id ]; then + cat /var/lib/dbus/machine-id + return 0 + fi + + alt_machine_id +} + +alt_machine_id() { + ip a | grep ether | md5sum | awk '{print $1}' +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: install_client +# DESCRIPTION: Execute all needed steps to install the rport client +#---------------------------------------------------------------------------------------------------------------------- +install_client() { + echo "Installing rport client" + print_distro + if runs_with_selinux && [ "$SELINUX_FORCE" -ne 1 ]; then + echo "" + echo "Your system has SELinux enabled. This installer will not create the needed policies." + echo "Rport will not connect with out the right policies." + echo "Read more https://kb.openrport.io/digging-deeper/advanced-client-management/run-with-selinux" + echo "Excute '$0 ${RAW_ARGS} -l' to skip this warning and install anyways. You must create the polcies later." + exit 1 + fi + test_connection + if [ -n "$PKG_URL" ]; then + if is_debian; then + install_from_deb_download + elif is_rhel; then + install_from_rpm_download + else + download_and_extract_from_url + install_bin rport + fi + elif is_debian; then + install_via_deb_repo + elif is_rhel; then + install_via_rpm_repo + else + download_and_extract + install_bin rport + fi +# install_bin rport + create_user + install_config rport + prepare_config + enable_lan_monitoring + detect_interpreters + set_file_and_dir_owner + if is_available openrc; then + create_openrc_service + else + create_systemd_service + fi + create_sudoers_updates + [ "$ENABLE_SUDO" -eq 1 ] && create_sudoers_all + [ "$INSTALL_TACO" -eq 1 ] && install_tacoscript + verify_and_terminate +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: verify_and_terminate +# DESCRIPTION: Verify the installation has succeeded +#---------------------------------------------------------------------------------------------------------------------- +verify_and_terminate() { + sleep 1 + if pgrep rport >/dev/null 2>&1; then + if check_log; then + finish + return 0 + elif [ $? -eq 1 ] && [ "$USE_ALTERNATIVE_MACHINEID" -ne 1 ]; then + USE_ALTERNATIVE_MACHINEID=1 + use_alternative_machineid + verify_and_terminate + return 0 + fi + fi + fail +} + +use_alternative_machineid() { + # If the /etc/machine-id is already used, use an alternative unique id + stop_rport + rm -f "$LOG_FILE" + echo "Creating a unique id based on the mac addresses of the network cards." + sed -i "s/^id = .*/id = \"$(alt_machine_id)\"/g" "$CONFIG_FILE" + start_rport +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: get_geodata +# DESCRIPTION: Retrieve the Country and the city of the currently used public IP address +#---------------------------------------------------------------------------------------------------------------------- +get_geodata() { + GEODATA="" + GEOSERVICE_URL="http://ip-api.com/line/?fields=status,country,city" + if is_available curl; then + GEODATA=$(curl -m2 -Ss "${GEOSERVICE_URL}" 2>/dev/null) + else + GEODATA=$(wget --timeout=2 -O - -q "${GEOSERVICE_URL}" 2>/dev/null) + fi + if echo "$GEODATA" | grep -q "^success"; then + CITY="$(echo "$GEODATA" | head -n3 | tail -n1)" + COUNTRY="$(echo "$GEODATA" | head -n2 | tail -n1)" + GEODATA="1" + return 0 + else + return 1 + fi +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: check_log +# DESCRIPTION: Check the log file for proper operation or common errors +#---------------------------------------------------------------------------------------------------------------------- +check_log() { + if [ -e "$LOG_FILE" ]; then + true + else + echo 2>&1 "[!] Logfile $LOG_FILE does not exist." + echo 2>&1 "[!] RPOrt very likely failed to start." + return 4 + fi + if grep -q "client id .* is already in use" "$LOG_FILE"; then + echo "" + echo 2>&1 "[!] Configuration error: client id is already in use." + echo 2>&1 "[!] Likely you have systems with an duplicated machine-id in your network." + echo "" + return 1 + elif grep -q "Connection error: websocket: bad handshake" "$LOG_FILE"; then + echo "" + echo 2>&1 "[!] Connection error: websocket: bad handshake" + echo "Check if transparent proxies are interfering outgoing http connections." + return 2 + elif tac "$LOG_FILE" | grep error; then + return 3 + fi + + return 0 +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: help +# DESCRIPTION: print a help message and exit +#---------------------------------------------------------------------------------------------------------------------- +help() { + cat < Use a different user account than 'rport'. Will be created if not present. +-i Install Tacoscript along with the RPort client. +-l Install with SELinux enabled. +-g Add an extra tag to the client. +-d Do not use /etc/machine-id to identify this machine. A random UUID will be used instead. +-p Do not use the RPM/DEB repository. Forces tar.gz installation. +-z Download the rport client tar.gz from the given URL instead of using GitHub releases. See environment variables. + +Environment variables: + If RPORT_INSTALLER_DL_USERNAME and RPORT_INSTALLER_DL_PASSWORD are set, downloads of custom packages triggered with + '-z' are initiated with HTTP basic authentication. + +Learn more https://kb.openrport.io/connecting-clients#advanced-pairing-options +EOF + exit 0 +} + +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: finish +# DESCRIPTION: print some information +#---------------------------------------------------------------------------------------------------------------------- +finish() { + echo " +# +# Installation of rport finished. +# +# This client is now connected to $SERVER +# +# Look at $CONFIG_FILE and explore all options. +# Logs are written to /var/log/rport/rport.log. +# +# READ THE DOCS ON https://kb.openrport.io/ +# +# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Give us a star on https://github.com/openrport/openrport +# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# + +Thanks for using + ____ _____ _____ _ + / __ \ | __ \| __ \ | | + | | | |_ __ ___ _ __ | |__) | |__) |__ _ __| |_ + | | | | '_ \ / _ \ '_ \| _ /| ___/ _ \| '__| __| + | |__| | |_) | __/ | | | | \ \| | | (_) | | | |_ + \____/| .__/ \___|_| |_|_| \_\_| \___/|_| \__| + | | + |_| + +" +} + +fail() { + echo " +# +# -------------!! ERROR !!------------- +# +# Installation of openrport finished with errors. +# + +Try the following to investigate: +1) systemctl rport status + +2) tail /var/log/rport/rport.log + +3) Ask for help on https://kb.openrport.io/need-help/request-support +" + if runs_with_selinux; then + echo " +4) Check your SELinux settings and create a policy for rport." + fi +} + +#---------------------------------------------------------------------------------------------------------------------- +# END OF FUNCTION DECLARATION +#---------------------------------------------------------------------------------------------------------------------- + +# +# Check for prerequisites +# +check_prerequisites + +MANDATORY="SERVER FINGERPRINT CLIENT_ID PASSWORD" +for VAR in $MANDATORY; do + if eval "[ -z $${VAR} ]"; then + abort "Variable \$${VAR} not set." + fi +done + +# +# Read the command line options and map to a function call +# +RAW_ARGS=$* +ACTION=install_client +ENABLE_COMMANDS=0 +ENABLE_SUDO=0 +RELEASE=stable +INSTALL_TACO=0 +SELINUX_FORCE=0 +ENABLE_FILEREC=0 +ENABLE_FILEREC_SUDO=0 +XTAG="" +NO_REPO=0 +while getopts 'phvfcsuxstildrba:g:z:' opt; do + case "${opt}" in + + h) + help + exit 0 + ;; + f) FORCE=1 ;; + v) + echo "$0 -- Version $VERSION" + exit 0 + ;; + c) ACTION=install_client ;; + u) ACTION=uninstall ;; + x) ENABLE_COMMANDS=1 ;; + s) ENABLE_SUDO=1 ;; + t) RELEASE=unstable ;; + i) INSTALL_TACO=1 ;; + l) SELINUX_FORCE=1 ;; + r) ENABLE_FILEREC=1 ;; + b) ENABLE_FILEREC_SUDO=1 ;; + a) USER=${OPTARG} ;; + g) XTAG=${OPTARG} ;; + z) export PKG_URL="${OPTARG}" ;; + d) MACHINE_ID=$(gen_uuid) ;; + p) NO_REPO=1 ;; + + \?) + echo "Option does not exist." + exit 1 + ;; + esac # --- end of case --- +done +shift $((OPTIND - 1)) +prepare # Prepare the system +$ACTION # Execute the function according to the users decision +clean_up # Clean up the system + +# END of templates/linux/install.sh ----------------------------------------------------------------------------------| + diff --git a/run.py b/run.py new file mode 100644 index 0000000..14981a8 --- /dev/null +++ b/run.py @@ -0,0 +1,10 @@ +import http.client, urllib + +conn = http.client.HTTPSConnection("api.pushover.net:443") +conn.request("POST", "/1/messages.json", + urllib.parse.urlencode({ + "token": "avzcexyjeu7y71pcskwshyx8ytmq8i", + "user": "uo2sf2pmrtjvt8auu786fviabimimr", + "message": "Reporting was running!", + }), { "Content-type": "application/x-www-form-urlencoded" }) +conn.getresponse()