Compare commits

..

120 Commits

Author SHA1 Message Date
wh1te909
8dd636b0eb Release 0.6.2 2021-04-23 06:40:31 +00:00
wh1te909
6b5bda8ee1 bump versions 2021-04-23 06:12:19 +00:00
Dan
ddc5597157 Merge pull request #421 from silversword411/develop
script library and docs tweaks
2021-04-22 19:08:33 -07:00
silversword411
ae112c7257 script library tweaks 2021-04-21 11:14:45 -04:00
silversword411
c22f10f96a Adding notes to vscode docs 2021-04-21 11:06:40 -04:00
silversword411
18d10c9bec power restart script tweaks 2021-04-21 09:40:07 -04:00
silversword411
890e430cb7 script library - merging scripts and parameterizing 2021-04-21 09:29:25 -04:00
wh1te909
dadc3d4cd7 add #418 2021-04-21 05:09:55 +00:00
Dan
d98b4d7320 Merge pull request #417 from silversword411/develop
tweaks to docs and scripts
2021-04-19 23:05:42 -07:00
silversword411
340f532238 tweaks to docs and scripts 2021-04-20 06:00:03 +00:00
wh1te909
7669f68e7c add code signing to docs 2021-04-20 05:54:24 +00:00
Dan
3557e5514f Merge pull request #416 from silversword411/develop
docs tweaks
2021-04-19 22:50:58 -07:00
silversword411
a9f09b7614 knew there was a bold somewhere 2021-04-20 05:15:24 +00:00
silversword411
845b9e4568 docs tweaks 2021-04-20 05:12:58 +00:00
Dan
24a6092dcf Merge pull request #415 from silversword411/develop
Community Script Library Docs v1
2021-04-19 21:36:37 -07:00
wh1te909
195ae7d8b1 add conditional menu render 2021-04-20 04:35:07 +00:00
silversword411
a5c6ea7ffc Merge branch 'develop' of https://github.com/silversword411/tacticalrmm into develop 2021-04-20 00:32:47 -04:00
silversword411
eb7a4ac29f Script library - more cleaning 2021-04-20 00:32:35 -04:00
silversword411
508ef73fde Contributing Community Scripts v1 2021-04-20 00:32:35 -04:00
silversword411
838d6d8076 Merge branch 'develop' of https://github.com/silversword411/tacticalrmm into develop 2021-04-20 00:26:25 -04:00
silversword411
762c3159b8 Script library - more cleaning 2021-04-20 00:26:20 -04:00
wh1te909
7a88a06bcf isort 2021-04-20 04:14:20 +00:00
wh1te909
0b1e3d7de5 start fixing #409 2021-04-20 04:11:48 +00:00
silversword411
9a83c73f21 Contributing Community Scripts v1 2021-04-20 04:11:15 +00:00
Dan
aa50c7b268 Merge pull request #414 from silversword411/develop
adding agent remove/add to docs
2021-04-19 20:33:53 -07:00
silversword411
179a5a80f4 Fixing Defender GUID 2021-04-19 23:06:35 -04:00
silversword411
0ddae527ef Script Library - Renaming files to follow best practices 2021-04-19 23:02:42 -04:00
silversword411
ee7a46de26 Script library - defender status tweaks 2021-04-19 22:28:51 -04:00
silversword411
95522fda74 script library - Adding set DNS script 2021-04-19 18:38:22 -04:00
silversword411
e58881c2bd script library Set Ethernet to use DHCP 2021-04-19 18:32:36 -04:00
silversword411
36a902a44e script rename and tweaks 2021-04-19 17:11:53 -04:00
silversword411
16b74549a2 adding agent remove/add to docs 2021-04-19 11:45:55 +00:00
wh1te909
da7ededfb1 fix sorting #402 2021-04-17 20:06:07 +00:00
wh1te909
790bb08718 fix check status in summary tab 2021-04-17 19:56:10 +00:00
Dan
e6765f421f Merge pull request #408 from silversword411/develop
Tooltip update
2021-04-17 12:54:36 -07:00
silversword411
7e8f1fe904 Tooltip update 2021-04-17 02:09:06 -04:00
Dan
eacce4578a Merge pull request #407 from bradhawkins85/patch-15
Update installer.ps1
2021-04-16 22:05:14 -07:00
bradhawkins85
07b2543972 Update installer.ps1
Test and wait (up to 15 seconds) to be able to connect to the server to download installer, don't try and download if we can't connect.
2021-04-17 13:41:35 +10:00
wh1te909
d1c3fc8493 Release 0.6.1 2021-04-16 07:46:42 +00:00
wh1te909
f453b16010 bump versions 2021-04-16 07:36:27 +00:00
wh1te909
05151d8978 add code signed agent to powershell/manual install methods 2021-04-16 07:16:55 +00:00
Dan
8218e1acc3 Merge pull request #397 from silversword411/develop
script library updates
2021-04-16 00:11:57 -07:00
wh1te909
30212fc89a fix maint mode text 2021-04-16 06:24:27 +00:00
sadnub
b31c13fcae add warning color to agents table and clients tree. Also made it upadte colors when checks UI is refreshed 2021-04-15 22:24:44 -04:00
sadnub
6b95fc6f1d change maintenance mode to green and modify the icon in the agent table when agent is in maintenance mode 2021-04-15 19:15:02 -04:00
sadnub
369cf17eb2 also resolve alerts when a check is cleared 2021-04-15 17:23:43 -04:00
sadnub
4dd8f512cc split up check statuses in the agent summary tab. #386 2021-04-15 17:12:46 -04:00
sadnub
26cfec7d80 add reset check status to check context menu. #388 2021-04-15 16:52:42 -04:00
sadnub
67a87ccf00 fix sticky table header in automated tasks 2021-04-15 16:12:09 -04:00
sadnub
667cebcf94 remove certain fields from view in the patch policy form when settnigs are inherited #396 2021-04-15 13:52:24 -04:00
sadnub
bc1747ca1c fix categories in script manager folder view. Truncate script args text in scripts table 2021-04-15 13:52:24 -04:00
silversword411
945d8647bf script add ipv6 disable 2021-04-15 11:34:35 -04:00
silversword411
dfe2e94627 tweaking script library after 0.6.0 update 2021-04-15 08:47:04 -04:00
silversword411
09a5591eec tweak docs so backup script overwrites existing name 2021-04-15 07:59:22 -04:00
silversword411
f2bf06a0ba tweak Network script names for sorting 2021-04-15 07:51:07 -04:00
silversword411
eedad4ab1c Merge branch 'develop' of https://github.com/silversword411/tacticalrmm into develop 2021-04-15 07:47:41 -04:00
silversword411
336a62ab29 Tweaking script names 2021-04-15 07:47:31 -04:00
wh1te909
b5603a5233 Release 0.6.0 2021-04-15 05:39:24 +00:00
wh1te909
73890f553c bump versions 2021-04-15 05:34:47 +00:00
sadnub
f6243b8968 update community script to include guid and update/delete existing community scripts 2021-04-14 22:43:12 -04:00
sadnub
3770dc74d4 fix scripts dropdown 2021-04-14 21:48:39 -04:00
wh1te909
45f4e947c5 more code signing stuff 2021-04-15 01:24:58 +00:00
Dan
9928d7c6e1 Merge pull request #394 from tremor021/develop
Fixed script naming scheme
2021-04-14 18:20:31 -07:00
silversword411
bf776eeb2b Tweaking script names 2021-04-14 15:12:02 -04:00
tremor021
ae7c0e9195 update 2021-04-14 15:53:54 +02:00
tremor021
e90b640602 Merge branch 'develop' of https://github.com/wh1te909/tacticalrmm into develop 2021-04-14 15:48:37 +02:00
tremor021
ba7529d3f5 update 2021-04-14 15:30:57 +02:00
tremor021
34667f252e Fixed naming and added task scheduling script 2021-04-14 15:23:22 +02:00
wh1te909
d18bddcb7b add code signing auth token 2021-04-14 07:49:28 +00:00
wh1te909
96dff49d33 add better exception messages to tests 2021-04-14 07:39:17 +00:00
Dan
b389728338 Merge pull request #392 from tremor021/develop
Scripts update
2021-04-14 00:35:20 -07:00
tremor021
cdc7da86f3 Fixes 2021-04-14 09:26:42 +02:00
tremor021
4745cc0378 Fixes 2021-04-14 09:25:26 +02:00
wh1te909
434f132479 add a test to make sure community script has jsonfile entry 2021-04-14 07:05:08 +00:00
tremor021
fb0f31ffc7 Added script to json 2021-04-14 08:55:17 +02:00
Dan
bb1d73c0ae Merge pull request #393 from silversword411/develop
Script library additions
2021-04-13 23:32:33 -07:00
silversword411
0e823d1191 Fixing bitlocker get recovery keys script 2021-04-14 00:40:26 -04:00
silversword411
48f4199ff3 script library - bitlocker get recovery keys 2021-04-14 00:39:20 -04:00
silversword411
eaf379587b script library - disable hibernation 2021-04-14 00:04:43 -04:00
silversword411
672446b7d1 script library check if user using temp profile 2021-04-13 23:52:04 -04:00
silversword411
dfe52c1b07 script library - task scheduler monitor 2021-04-13 23:48:15 -04:00
silversword411
d63df03ad8 script library - new user notify 2021-04-13 23:42:34 -04:00
silversword411
aba4f9f2ce script library - Azure Mars Backup check 2021-04-13 23:37:05 -04:00
silversword411
ac5c1e7803 Script library - Adding enable and disable USB devices 2021-04-13 23:26:48 -04:00
tremor021
d521dbf50e Added more scripts 2021-04-13 23:39:44 +02:00
tremor021
f210ed3e6a Added Get_Computer_Users script 2021-04-13 23:25:10 +02:00
tremor021
df3cac4ea6 Merge branch 'develop' of https://github.com/wh1te909/tacticalrmm into develop 2021-04-13 22:14:14 +02:00
tremor021
f778c5175b Added some explanations into scripts 2021-04-13 22:10:37 +02:00
Dan
6c66ff28dd Merge pull request #385 from silversword411/develop
script library updates and docs tweak
2021-04-11 22:39:12 -07:00
silversword411
d5b6ec702b Merge branch 'develop' of https://github.com/silversword411/tacticalrmm into develop 2021-04-11 11:36:20 -04:00
silversword411
c62a5fcef2 script library empty recycle bin2 2021-04-11 11:35:53 -04:00
silversword411
59c47e9200 script library - empty recycle bin 2021-04-11 11:35:53 -04:00
silversword411
4ba44d8932 script library - Monitor info script 2021-04-11 11:35:53 -04:00
silversword411
27dae05e1b Script add - RAM status 2021-04-11 11:35:53 -04:00
silversword411
a251ae9b90 2 scripts added 2021-04-11 11:35:53 -04:00
silversword411
7e960b2bde Fixing Table of Contents levels 2021-04-11 11:35:53 -04:00
silversword411
5df4825158 Consistency check on script "name" field for alphabetic sorts 2021-04-11 11:35:53 -04:00
silversword411
8984d06d93 script library empty recycle bin2 2021-04-11 11:26:59 -04:00
silversword411
eed7aac047 script library - empty recycle bin 2021-04-11 11:26:22 -04:00
silversword411
54b068de4a script library - Monitor info script 2021-04-11 11:21:03 -04:00
silversword411
f0f33b00b6 Script add - RAM status 2021-04-11 09:13:47 -04:00
silversword411
1043405088 2 scripts added 2021-04-11 08:47:36 -04:00
silversword411
0131b10805 Fixing Table of Contents levels 2021-04-11 08:02:51 -04:00
silversword411
a19b441f62 Consistency check on script "name" field for alphabetic sorts 2021-04-11 07:58:03 -04:00
wh1te909
28edc31d43 Release 0.5.3 2021-04-11 08:08:58 +00:00
wh1te909
0f9872a818 bump versions 2021-04-11 08:08:48 +00:00
wh1te909
76ce4296f3 fix graphics 2021-04-11 07:25:37 +00:00
wh1te909
3dd2671380 add graphics 2021-04-11 06:50:16 +00:00
wh1te909
298ca31332 remove unused func 2021-04-11 05:43:17 +00:00
wh1te909
8f911aa6b9 more tests 2021-04-11 05:35:24 +00:00
wh1te909
82a5c7d9b1 add test 2021-04-11 05:17:49 +00:00
wh1te909
7f013dcdba refactor nats-api / optimize queries 2021-04-11 05:04:33 +00:00
wh1te909
68e2e16076 add feat #377 2021-04-11 03:23:40 +00:00
wh1te909
ea23c763c9 add feat #376 2021-04-11 02:01:40 +00:00
wh1te909
5dcecb3206 fix alert text for policy diskspace check where disk doesn't exist 2021-04-10 22:09:24 +00:00
Dan
5bd48e2d0e Merge pull request #380 from silversword411/develop
Community Script Additions
2021-04-10 13:36:26 -07:00
silversword411
afd0a02589 3 scripts added from dinger1986 2021-04-10 13:44:02 -04:00
silversword411
2379192d53 Merge branch 'develop' of https://github.com/silversword411/tacticalrmm into develop 2021-04-10 13:27:35 -04:00
silversword411
a6489290c8 2 scripts added 2021-04-10 13:26:39 -04:00
silversword411
5f74c43415 2 scripts added 2021-04-10 13:22:54 -04:00
99 changed files with 1792 additions and 402 deletions

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2 on 2021-04-11 01:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0013_user_client_tree_sort'),
]
operations = [
migrations.AddField(
model_name='user',
name='client_tree_splitter',
field=models.PositiveIntegerField(default=11),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2 on 2021-04-11 03:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0014_user_client_tree_splitter'),
]
operations = [
migrations.AddField(
model_name='user',
name='loading_bar_color',
field=models.CharField(default='red', max_length=255),
),
]

View File

@@ -36,6 +36,8 @@ class User(AbstractUser, BaseAuditModel):
client_tree_sort = models.CharField(
max_length=50, choices=CLIENT_TREE_SORT_CHOICES, default="alphafail"
)
client_tree_splitter = models.PositiveIntegerField(default=11)
loading_bar_color = models.CharField(max_length=255, default="red")
agent = models.OneToOneField(
"agents.Agent",

View File

@@ -13,6 +13,8 @@ class UserUISerializer(ModelSerializer):
"agent_dblclick_action",
"default_agent_tbl_tab",
"client_tree_sort",
"client_tree_splitter",
"loading_bar_color",
]

View File

@@ -278,6 +278,8 @@ class TestUserAction(TacticalTestCase):
"agent_dblclick_action": "editagent",
"default_agent_tbl_tab": "mixed",
"client_tree_sort": "alpha",
"client_tree_splitter": 14,
"loading_bar_color": "green",
}
r = self.client.patch(url, data, format="json")
self.assertEqual(r.status_code, 200)

View File

@@ -166,7 +166,7 @@ class Agent(BaseAuditModel):
@property
def checks(self):
total, passing, failing = 0, 0, 0
total, passing, failing, warning, info = 0, 0, 0, 0, 0
if self.agentchecks.exists(): # type: ignore
for i in self.agentchecks.all(): # type: ignore
@@ -174,13 +174,20 @@ class Agent(BaseAuditModel):
if i.status == "passing":
passing += 1
elif i.status == "failing":
failing += 1
if i.alert_severity == "error":
failing += 1
elif i.alert_severity == "warning":
warning += 1
elif i.alert_severity == "info":
info += 1
ret = {
"total": total,
"passing": passing,
"failing": failing,
"has_failing_checks": failing > 0,
"warning": warning,
"info": info,
"has_failing_checks": failing > 0 or warning > 0,
}
return ret
@@ -195,6 +202,27 @@ class Agent(BaseAuditModel):
except:
return ["unknown cpu model"]
@property
def graphics(self):
ret, mrda = [], []
try:
graphics = self.wmi_detail["graphics"]
for i in graphics:
caption = [x["Caption"] for x in i if "Caption" in x][0]
if "microsoft remote display adapter" in caption.lower():
mrda.append("yes")
continue
ret.append([x["Caption"] for x in i if "Caption" in x][0])
# only return this if no other graphics cards
if not ret and mrda:
return "Microsoft Remote Display Adapter"
return ", ".join(ret)
except:
return "Graphics info requires agent v1.4.14"
@property
def local_ips(self):
ret = []
@@ -322,7 +350,7 @@ class Agent(BaseAuditModel):
online = [
agent
for agent in Agent.objects.only(
"pk", "last_seen", "overdue_time", "offline_time"
"pk", "agent_id", "last_seen", "overdue_time", "offline_time"
)
if agent.status == "online"
]
@@ -819,12 +847,6 @@ class RecoveryAction(models.Model):
def __str__(self):
return f"{self.agent.hostname} - {self.mode}"
def send(self):
ret = {"recovery": self.mode}
if self.mode == "command":
ret["cmd"] = self.command
return ret
class Note(models.Model):
agent = models.ForeignKey(

View File

@@ -16,6 +16,7 @@ class AgentSerializer(serializers.ModelSerializer):
local_ips = serializers.ReadOnlyField()
make_model = serializers.ReadOnlyField()
physical_disks = serializers.ReadOnlyField()
graphics = serializers.ReadOnlyField()
checks = serializers.ReadOnlyField()
timezone = serializers.ReadOnlyField()
all_timezones = serializers.SerializerMethodField()

View File

@@ -1,9 +1,7 @@
import asyncio
import datetime as dt
import json
import random
import subprocess
import tempfile
import urllib.parse
from time import sleep
from typing import Union
@@ -13,21 +11,21 @@ from loguru import logger
from packaging import version as pyver
from agents.models import Agent
from core.models import CoreSettings
from core.models import CodeSignToken, CoreSettings
from logs.models import PendingAction
from scripts.models import Script
from tacticalrmm.celery import app
from tacticalrmm.utils import run_nats_api_cmd
logger.configure(**settings.LOG_CONFIG)
def agent_update(pk: int) -> str:
def agent_update(pk: int, codesigntoken: str = None) -> str:
from agents.utils import get_exegen_url
agent = Agent.objects.get(pk=pk)
if pyver.parse(agent.version) <= pyver.parse("1.1.11"):
logger.warning(
f"{agent.hostname} v{agent.version} is running an unsupported version. Refusing to auto update."
)
if pyver.parse(agent.version) <= pyver.parse("1.3.0"):
return "not supported"
# skip if we can't determine the arch
@@ -37,18 +35,15 @@ def agent_update(pk: int) -> str:
)
return "noarch"
# removed sqlite in 1.4.0 to get rid of cgo dependency
# 1.3.0 has migration func to move from sqlite to win registry, so force an upgrade to 1.3.0 if old agent
if pyver.parse(agent.version) >= pyver.parse("1.3.0"):
version = settings.LATEST_AGENT_VER
url = agent.winagent_dl
inno = agent.win_inno_exe
version = settings.LATEST_AGENT_VER
inno = agent.win_inno_exe
if codesigntoken is not None and pyver.parse(version) >= pyver.parse("1.5.0"):
base_url = get_exegen_url() + "/api/v1/winagents/?"
params = {"version": version, "arch": agent.arch, "token": codesigntoken}
url = base_url + urllib.parse.urlencode(params)
else:
version = "1.3.0"
inno = (
"winagent-v1.3.0.exe" if agent.arch == "64" else "winagent-v1.3.0-x86.exe"
)
url = f"https://github.com/wh1te909/rmmagent/releases/download/v1.3.0/{inno}"
url = agent.winagent_dl
if agent.pendingactions.filter(
action_type="agentupdate", status="pending"
@@ -81,10 +76,15 @@ def agent_update(pk: int) -> str:
@app.task
def send_agent_update_task(pks: list[int]) -> None:
try:
codesigntoken = CodeSignToken.objects.first().token
except:
codesigntoken = None
chunks = (pks[i : i + 30] for i in range(0, len(pks), 30))
for chunk in chunks:
for pk in chunk:
agent_update(pk)
agent_update(pk, codesigntoken)
sleep(0.05)
sleep(4)
@@ -95,6 +95,11 @@ def auto_self_agent_update_task() -> None:
if not core.agent_auto_update:
return
try:
codesigntoken = CodeSignToken.objects.first().token
except:
codesigntoken = None
q = Agent.objects.only("pk", "version")
pks: list[int] = [
i.pk
@@ -105,7 +110,7 @@ def auto_self_agent_update_task() -> None:
chunks = (pks[i : i + 30] for i in range(0, len(pks), 30))
for chunk in chunks:
for pk in chunk:
agent_update(pk)
agent_update(pk, codesigntoken)
sleep(0.05)
sleep(4)
@@ -257,30 +262,13 @@ def run_script_email_results_task(
logger.error(e)
def _get_nats_config() -> dict:
return {
"key": settings.SECRET_KEY,
"natsurl": f"tls://{settings.ALLOWED_HOSTS[0]}:4222",
}
@app.task
def monitor_agents_task() -> None:
agents = Agent.objects.only(
"pk", "agent_id", "last_seen", "overdue_time", "offline_time"
)
ret = [i.agent_id for i in agents if i.status != "online"]
config = _get_nats_config()
config["agents"] = ret
with tempfile.NamedTemporaryFile() as fp:
with open(fp.name, "w") as f:
json.dump(config, f)
cmd = ["/usr/local/bin/nats-api", "-c", fp.name, "-m", "monitor"]
try:
subprocess.run(cmd, capture_output=True, timeout=30)
except Exception as e:
logger.error(e)
ids = [i.agent_id for i in agents if i.status != "online"]
run_nats_api_cmd("monitor", ids)
@app.task
@@ -288,15 +276,5 @@ def get_wmi_task() -> None:
agents = Agent.objects.only(
"pk", "agent_id", "last_seen", "overdue_time", "offline_time"
)
ret = [i.agent_id for i in agents if i.status == "online"]
config = _get_nats_config()
config["agents"] = ret
with tempfile.NamedTemporaryFile() as fp:
with open(fp.name, "w") as f:
json.dump(config, f)
cmd = ["/usr/local/bin/nats-api", "-c", fp.name, "-m", "wmi"]
try:
subprocess.run(cmd, capture_output=True, timeout=30)
except Exception as e:
logger.error(e)
ids = [i.agent_id for i in agents if i.status == "online"]
run_nats_api_cmd("wmi", ids)

View File

@@ -914,8 +914,9 @@ class TestAgentTasks(TacticalTestCase):
self.authenticate()
self.setup_coresettings()
@patch("agents.utils.get_exegen_url")
@patch("agents.models.Agent.nats_cmd")
def test_agent_update(self, nats_cmd):
def test_agent_update(self, nats_cmd, get_exe):
from agents.tasks import agent_update
agent_noarch = baker.make_recipe(
@@ -926,63 +927,96 @@ class TestAgentTasks(TacticalTestCase):
r = agent_update(agent_noarch.pk)
self.assertEqual(r, "noarch")
agent_1111 = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
version="1.1.11",
)
r = agent_update(agent_1111.pk)
self.assertEqual(r, "not supported")
agent64_1112 = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
version="1.1.12",
)
r = agent_update(agent64_1112.pk)
self.assertEqual(r, "created")
action = PendingAction.objects.get(agent__pk=agent64_1112.pk)
self.assertEqual(action.action_type, "agentupdate")
self.assertEqual(action.status, "pending")
self.assertEqual(
action.details["url"],
"https://github.com/wh1te909/rmmagent/releases/download/v1.3.0/winagent-v1.3.0.exe",
)
self.assertEqual(action.details["inno"], "winagent-v1.3.0.exe")
self.assertEqual(action.details["version"], "1.3.0")
nats_cmd.assert_called_with(
{
"func": "agentupdate",
"payload": {
"url": "https://github.com/wh1te909/rmmagent/releases/download/v1.3.0/winagent-v1.3.0.exe",
"version": "1.3.0",
"inno": "winagent-v1.3.0.exe",
},
},
wait=False,
)
agent_64_130 = baker.make_recipe(
agent_130 = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
version="1.3.0",
)
nats_cmd.return_value = "ok"
r = agent_update(agent_64_130.pk)
r = agent_update(agent_130.pk)
self.assertEqual(r, "not supported")
# test __without__ code signing
agent64_nosign = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
version="1.4.14",
)
r = agent_update(agent64_nosign.pk, None)
self.assertEqual(r, "created")
action = PendingAction.objects.get(agent__pk=agent64_nosign.pk)
self.assertEqual(action.action_type, "agentupdate")
self.assertEqual(action.status, "pending")
self.assertEqual(
action.details["url"],
f"https://github.com/wh1te909/rmmagent/releases/download/v{settings.LATEST_AGENT_VER}/winagent-v{settings.LATEST_AGENT_VER}.exe",
)
self.assertEqual(
action.details["inno"], f"winagent-v{settings.LATEST_AGENT_VER}.exe"
)
self.assertEqual(action.details["version"], settings.LATEST_AGENT_VER)
nats_cmd.assert_called_with(
{
"func": "agentupdate",
"payload": {
"url": settings.DL_64,
"url": f"https://github.com/wh1te909/rmmagent/releases/download/v{settings.LATEST_AGENT_VER}/winagent-v{settings.LATEST_AGENT_VER}.exe",
"version": settings.LATEST_AGENT_VER,
"inno": f"winagent-v{settings.LATEST_AGENT_VER}.exe",
},
},
wait=False,
)
action = PendingAction.objects.get(agent__pk=agent_64_130.pk)
# test __with__ code signing (64 bit)
codesign = baker.make("core.CodeSignToken", token="testtoken123")
agent64_sign = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 64 bit (build 19041.450)",
version="1.4.14",
)
nats_cmd.return_value = "ok"
get_exe.return_value = "https://exe.tacticalrmm.io"
r = agent_update(agent64_sign.pk, codesign.token) # type: ignore
self.assertEqual(r, "created")
nats_cmd.assert_called_with(
{
"func": "agentupdate",
"payload": {
"url": f"https://exe.tacticalrmm.io/api/v1/winagents/?version={settings.LATEST_AGENT_VER}&arch=64&token=testtoken123", # type: ignore
"version": settings.LATEST_AGENT_VER,
"inno": f"winagent-v{settings.LATEST_AGENT_VER}.exe",
},
},
wait=False,
)
action = PendingAction.objects.get(agent__pk=agent64_sign.pk)
self.assertEqual(action.action_type, "agentupdate")
self.assertEqual(action.status, "pending")
# test __with__ code signing (32 bit)
agent32_sign = baker.make_recipe(
"agents.agent",
operating_system="Windows 10 Pro, 32 bit (build 19041.450)",
version="1.4.14",
)
nats_cmd.return_value = "ok"
get_exe.return_value = "https://exe.tacticalrmm.io"
r = agent_update(agent32_sign.pk, codesign.token) # type: ignore
self.assertEqual(r, "created")
nats_cmd.assert_called_with(
{
"func": "agentupdate",
"payload": {
"url": f"https://exe.tacticalrmm.io/api/v1/winagents/?version={settings.LATEST_AGENT_VER}&arch=32&token=testtoken123", # type: ignore
"version": settings.LATEST_AGENT_VER,
"inno": f"winagent-v{settings.LATEST_AGENT_VER}-x86.exe",
},
},
wait=False,
)
action = PendingAction.objects.get(agent__pk=agent32_sign.pk)
self.assertEqual(action.action_type, "agentupdate")
self.assertEqual(action.status, "pending")

View File

@@ -0,0 +1,37 @@
import random
import urllib.parse
import requests
from django.conf import settings
def get_exegen_url() -> str:
urls: list[str] = settings.EXE_GEN_URLS
for url in urls:
try:
r = requests.get(url, timeout=10)
except:
continue
if r.status_code == 200:
return url
return random.choice(urls)
def get_winagent_url(arch: str) -> str:
from core.models import CodeSignToken
try:
codetoken = CodeSignToken.objects.first().token
base_url = get_exegen_url() + "/api/v1/winagents/?"
params = {
"version": settings.LATEST_AGENT_VER,
"arch": arch,
"token": codetoken,
}
dl_url = base_url + urllib.parse.urlencode(params)
except:
dl_url = settings.DL_64 if arch == "64" else settings.DL_32
return dl_url

View File

@@ -40,7 +40,7 @@ logger.configure(**settings.LOG_CONFIG)
@api_view()
def get_agent_versions(request):
agents = Agent.objects.only("pk")
agents = Agent.objects.prefetch_related("site").only("pk", "hostname")
return Response(
{
"versions": [settings.LATEST_AGENT_VER],
@@ -355,6 +355,8 @@ class Reboot(APIView):
def install_agent(request):
from knox.models import AuthToken
from agents.utils import get_winagent_url
client_id = request.data["client"]
site_id = request.data["site"]
version = settings.LATEST_AGENT_VER
@@ -375,7 +377,7 @@ def install_agent(request):
inno = (
f"winagent-v{version}.exe" if arch == "64" else f"winagent-v{version}-x86.exe"
)
download_url = settings.DL_64 if arch == "64" else settings.DL_32
download_url = get_winagent_url(arch)
_, token = AuthToken.objects.create(
user=request.user, expiry=dt.timedelta(hours=request.data["expires"])

View File

@@ -633,12 +633,15 @@ class Check(BaseAuditModel):
if self.error_threshold:
text += f" Error Threshold: {self.error_threshold}%"
percent_used = [
d["percent"] for d in self.agent.disks if d["device"] == self.disk
][0]
percent_free = 100 - percent_used
try:
percent_used = [
d["percent"] for d in self.agent.disks if d["device"] == self.disk
][0]
percent_free = 100 - percent_used
body = subject + f" - Free: {percent_free}%, {text}"
body = subject + f" - Free: {percent_free}%, {text}"
except:
body = subject + f" - Disk {self.disk} does not exist"
elif self.check_type == "script":
@@ -710,11 +713,15 @@ class Check(BaseAuditModel):
if self.error_threshold:
text += f" Error Threshold: {self.error_threshold}%"
percent_used = [
d["percent"] for d in self.agent.disks if d["device"] == self.disk
][0]
percent_free = 100 - percent_used
body = subject + f" - Free: {percent_free}%, {text}"
try:
percent_used = [
d["percent"] for d in self.agent.disks if d["device"] == self.disk
][0]
percent_free = 100 - percent_used
body = subject + f" - Free: {percent_free}%, {text}"
except:
body = subject + f" - Disk {self.disk} does not exist"
elif self.check_type == "script":
body = subject + f" - Return code: {self.retcode}"
elif self.check_type == "ping":

View File

@@ -86,7 +86,10 @@ class GetUpdateDeleteCheck(APIView):
check = get_object_or_404(Check, pk=pk)
# remove fields that should not be changed when editing a check from the frontend
if "check_alert" not in request.data.keys():
if (
"check_alert" not in request.data.keys()
and "check_reset" not in request.data.keys()
):
[request.data.pop(i) for i in check.non_editable_fields]
# set event id to 0 if wildcard because it needs to be an integer field for db
@@ -108,6 +111,11 @@ class GetUpdateDeleteCheck(APIView):
if check.policy:
update_policy_check_fields_task(checkpk=pk)
# resolve any alerts that are open
if "check_reset" in request.data.keys():
if obj.alert.filter(resolved=False).exists():
obj.alert.get(resolved=False).resolve()
return Response(f"{obj.readable_desc} was edited!")
def delete(self, request, pk):

View File

@@ -65,6 +65,10 @@ class Client(BaseAuditModel):
def __str__(self):
return self.name
@property
def agent_count(self) -> int:
return Agent.objects.filter(site__client=self).count()
@property
def has_maintenanace_mode_agents(self):
return (
@@ -86,16 +90,24 @@ class Client(BaseAuditModel):
.prefetch_related("agentchecks")
)
failing = 0
data = {"error": False, "warning": False}
for agent in agents:
if agent.checks["has_failing_checks"]:
failing += 1
if agent.checks["warning"]:
data["warning"] = True
if agent.checks["failing"]:
data["error"] = True
break
if agent.overdue_email_alert or agent.overdue_text_alert:
if agent.status == "overdue":
failing += 1
data["error"] = True
break
return failing > 0
return data
@staticmethod
def serialize(client):
@@ -165,6 +177,10 @@ class Site(BaseAuditModel):
def __str__(self):
return self.name
@property
def agent_count(self) -> int:
return Agent.objects.filter(site=self).count()
@property
def has_maintenanace_mode_agents(self):
return Agent.objects.filter(site=self, maintenance_mode=True).count() > 0
@@ -184,16 +200,24 @@ class Site(BaseAuditModel):
.prefetch_related("agentchecks")
)
failing = 0
data = {"error": False, "warning": False}
for agent in agents:
if agent.checks["has_failing_checks"]:
failing += 1
if agent.checks["warning"]:
data["warning"] = True
if agent.checks["failing"]:
data["error"] = True
break
if agent.overdue_email_alert or agent.overdue_text_alert:
if agent.status == "overdue":
failing += 1
data["error"] = True
break
return failing > 0
return data
@staticmethod
def serialize(site):

View File

@@ -25,6 +25,7 @@ class SiteCustomFieldSerializer(ModelSerializer):
class SiteSerializer(ModelSerializer):
client_name = ReadOnlyField(source="client.name")
custom_fields = SiteCustomFieldSerializer(many=True, read_only=True)
agent_count = ReadOnlyField()
class Meta:
model = Site
@@ -37,6 +38,7 @@ class SiteSerializer(ModelSerializer):
"client_name",
"client",
"custom_fields",
"agent_count",
)
def validate(self, val):
@@ -68,6 +70,7 @@ class ClientCustomFieldSerializer(ModelSerializer):
class ClientSerializer(ModelSerializer):
sites = SiteSerializer(many=True, read_only=True)
custom_fields = ClientCustomFieldSerializer(many=True, read_only=True)
agent_count = ReadOnlyField()
class Meta:
model = Client
@@ -79,6 +82,7 @@ class ClientSerializer(ModelSerializer):
"alert_template",
"sites",
"custom_fields",
"agent_count",
)
def validate(self, val):
@@ -95,7 +99,6 @@ class SiteTreeSerializer(ModelSerializer):
class Meta:
model = Site
fields = "__all__"
ordering = ("failing_checks",)
class ClientTreeSerializer(ModelSerializer):
@@ -106,7 +109,6 @@ class ClientTreeSerializer(ModelSerializer):
class Meta:
model = Client
fields = "__all__"
ordering = ("failing_checks",)
class DeploymentSerializer(ModelSerializer):

View File

@@ -1,6 +1,7 @@
from django.contrib import admin
from .models import CoreSettings, CustomField
from .models import CodeSignToken, CoreSettings, CustomField
admin.site.register(CoreSettings)
admin.site.register(CustomField)
admin.site.register(CodeSignToken)

View File

@@ -9,6 +9,7 @@ $rdp = rdpchange
$ping = pingchange
$auth = '"tokenchange"'
$downloadlink = 'downloadchange'
$apilink = $downloadlink.split('/')
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
@@ -47,24 +48,35 @@ If (Get-Service $serviceName -ErrorAction SilentlyContinue) {
# pass
}
Try
{
Invoke-WebRequest -Uri $downloadlink -OutFile $OutPath\$output
Start-Process -FilePath $OutPath\$output -ArgumentList ('/VERYSILENT /SUPPRESSMSGBOXES') -Wait
write-host ('Extracting...')
Start-Sleep -s 5
Start-Process -FilePath "C:\Program Files\TacticalAgent\tacticalrmm.exe" -ArgumentList $installArgs -Wait
exit 0
}
Catch
{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
Write-Error -Message "$ErrorMessage $FailedItem"
exit 1
}
Finally
{
Remove-Item -Path $OutPath\$output
$X = 0
do {
Write-Output "Waiting for network"
Start-Sleep -s 5
$X += 1
} until(($connectreult = Test-NetConnection $apilink[2] -Port 443 | ? { $_.TcpTestSucceeded }) -or $X -eq 3)
if ($connectreult.TcpTestSucceeded -eq $true){
Try
{
Invoke-WebRequest -Uri $downloadlink -OutFile $OutPath\$output
Start-Process -FilePath $OutPath\$output -ArgumentList ('/VERYSILENT /SUPPRESSMSGBOXES') -Wait
write-host ('Extracting...')
Start-Sleep -s 5
Start-Process -FilePath "C:\Program Files\TacticalAgent\tacticalrmm.exe" -ArgumentList $installArgs -Wait
exit 0
}
Catch
{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
Write-Error -Message "$ErrorMessage $FailedItem"
exit 1
}
Finally
{
Remove-Item -Path $OutPath\$output
}
} else {
Write-Output "Unable to connect to server"
}
}

View File

@@ -0,0 +1,20 @@
# Generated by Django 3.2 on 2021-04-13 05:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0018_auto_20210329_1709'),
]
operations = [
migrations.CreateModel(
name='CodeSignToken',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('token', models.CharField(blank=True, max_length=255, null=True)),
],
),
]

View File

@@ -266,3 +266,16 @@ class CustomField(models.Model):
return self.default_value_bool
else:
return self.default_value_string
class CodeSignToken(models.Model):
token = models.CharField(max_length=255, null=True, blank=True)
def save(self, *args, **kwargs):
if not self.pk and CodeSignToken.objects.exists():
raise ValidationError("There can only be one CodeSignToken instance")
super(CodeSignToken, self).save(*args, **kwargs)
def __str__(self):
return "Code signing token"

View File

@@ -1,7 +1,7 @@
import pytz
from rest_framework import serializers
from .models import CoreSettings, CustomField
from .models import CodeSignToken, CoreSettings, CustomField
class CoreSettingsSerializer(serializers.ModelSerializer):
@@ -27,3 +27,9 @@ class CustomFieldSerializer(serializers.ModelSerializer):
class Meta:
model = CustomField
fields = "__all__"
class CodeSignTokenSerializer(serializers.ModelSerializer):
class Meta:
model = CodeSignToken
fields = "__all__"

View File

@@ -1,5 +1,6 @@
from unittest.mock import patch
import requests
from channels.db import database_sync_to_async
from channels.testing import WebsocketCommunicator
from model_bakery import baker
@@ -12,6 +13,28 @@ from .serializers import CustomFieldSerializer
from .tasks import core_maintenance_tasks
class TestCodeSign(TacticalTestCase):
def setUp(self):
self.setup_coresettings()
self.authenticate()
self.url = "/core/codesign/"
def test_get_codesign(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.check_not_authenticated("get", self.url)
@patch("requests.post")
def test_edit_codesign_timeout(self, mock_post):
mock_post.side_effect = requests.exceptions.ConnectionError()
data = {"token": "token123"}
r = self.client.patch(self.url, data, format="json")
self.assertEqual(r.status_code, 400)
self.check_not_authenticated("patch", self.url)
class TestConsumers(TacticalTestCase):
def setUp(self):
self.setup_coresettings()

View File

@@ -12,4 +12,5 @@ urlpatterns = [
path("servermaintenance/", views.server_maintenance),
path("customfields/", views.GetAddCustomFields.as_view()),
path("customfields/<int:pk>/", views.GetUpdateDeleteCustomFields.as_view()),
path("codesign/", views.CodeSign.as_view()),
]

View File

@@ -11,8 +11,12 @@ from rest_framework.views import APIView
from tacticalrmm.utils import notify_error
from .models import CoreSettings, CustomField
from .serializers import CoreSettingsSerializer, CustomFieldSerializer
from .models import CodeSignToken, CoreSettings, CustomField
from .serializers import (
CodeSignTokenSerializer,
CoreSettingsSerializer,
CustomFieldSerializer,
)
class UploadMeshAgent(APIView):
@@ -65,6 +69,9 @@ def dashboard_info(request):
"dbl_click_action": request.user.agent_dblclick_action,
"default_agent_tbl_tab": request.user.default_agent_tbl_tab,
"client_tree_sort": request.user.client_tree_sort,
"client_tree_splitter": request.user.client_tree_splitter,
"loading_bar_color": request.user.loading_bar_color,
"no_code_sign": hasattr(settings, "NOCODESIGN") and settings.NOCODESIGN,
}
)
@@ -177,3 +184,48 @@ class GetUpdateDeleteCustomFields(APIView):
get_object_or_404(CustomField, pk=pk).delete()
return Response("ok")
class CodeSign(APIView):
def get(self, request):
token = CodeSignToken.objects.first()
return Response(CodeSignTokenSerializer(token).data)
def patch(self, request):
import requests
errors = []
for url in settings.EXE_GEN_URLS:
try:
r = requests.post(
f"{url}/api/v1/checktoken",
json={"token": request.data["token"]},
headers={"Content-type": "application/json"},
timeout=15,
)
except Exception as e:
errors.append(str(e))
else:
errors = []
break
if errors:
return notify_error(", ".join(errors))
if r.status_code == 400 or r.status_code == 401: # type: ignore
return notify_error(r.json()["ret"]) # type: ignore
elif r.status_code == 200: # type: ignore
t = CodeSignToken.objects.first()
if t is None:
CodeSignToken.objects.create(token=request.data["token"])
else:
serializer = CodeSignTokenSerializer(instance=t, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response("Token was saved")
try:
ret = r.json()["ret"] # type: ignore
except:
ret = "Something went wrong"
return notify_error(ret)

View File

@@ -1,18 +1,18 @@
[
{
"guid": "6820cb5e-5a7f-4d9b-8c22-d54677e3cc04",
"filename": "Win_Clear_Firefox_Cache.ps1",
"filename": "Win_Firefox_Clear_Cache.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Clear Firefox Cache",
"name": "Firefox - Clean Cache",
"description": "This script will clean up Mozilla Firefox for all users.",
"shell": "powershell",
"category": "TRMM (Win):Browsers"
},
{
"guid": "3ff6a386-11d1-4f9d-8cca-1b0563bb6443",
"filename": "Win_Clear_Google_Chrome_Cache.ps1",
"filename": "Win_Google_Chrome_Clear_Cache.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Clear Google Chrome Cache",
"name": "Chrome - Clear Cache for All Users",
"description": "This script will clean up Google Chrome for all users.",
"shell": "powershell",
"category": "TRMM (Win):Browsers"
@@ -21,7 +21,7 @@
"guid": "be1de837-f677-4ac5-aa0c-37a0fc9991fc",
"filename": "Win_Install_Adobe_Reader.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Install Adobe Reader DC",
"name": "Adobe Reader DC - Install",
"description": "Installs Adobe Reader DC.",
"shell": "powershell",
"category": "TRMM (Win):3rd Party Software>Chocolatey"
@@ -30,7 +30,7 @@
"guid": "2ee134d5-76aa-4160-b334-a1efbc62079f",
"filename": "Win_Install_Duplicati.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Install Duplicati",
"name": "Duplicati - Install",
"description": "This script installs Duplicati 2.0.5.1 as a service.",
"shell": "powershell",
"category": "TRMM (Win):3rd Party Software"
@@ -39,7 +39,7 @@
"guid": "81cc5bcb-01bf-4b0c-89b9-0ac0f3fe0c04",
"filename": "Win_Reset_Windows_Update.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Reset Windows Update",
"name": "Windows Update - Reset",
"description": "This script will reset all of the Windows Updates components to DEFAULT SETTINGS.",
"shell": "powershell",
"category": "TRMM (Win):Updates"
@@ -48,16 +48,16 @@
"guid": "8db87ff0-a9b4-4d9d-bc55-377bbcb85b6d",
"filename": "Win_Start_Cleanup.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Cleanup C: drive",
"name": "Disk - Cleanup C: drive",
"description": "Cleans the C: drive's Window Temperary files, Windows SoftwareDistribution folder, the local users Temperary folder, IIS logs (if applicable) and empties the recycling bin. All deleted files will go into a log transcript in $env:TEMP. By default this script leaves files that are newer than 7 days old however this variable can be edited.",
"shell": "powershell",
"category": "TRMM (Win):Other"
"category": "TRMM (Win):Maintenance"
},
{
"guid": "2f28e8c1-ae0f-4b46-a826-f513974526a3",
"filename": "Win_Defender_FullScan_Background.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Windows Defender Full Scan",
"name": "Defender - Full Scan",
"description": "Runs a Windows Defender Full background scan.",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
@@ -66,7 +66,7 @@
"guid": "adf81ddb-3b77-415c-a89b-2ccc826b5aa7",
"filename": "Win_Defender_QuickScan_Background.ps1",
"submittedBy": "https://github.com/Omnicef",
"name": "Windows Defender Quick Scan",
"name": "Defender - Quick Scan",
"description": "Runs a Quick Scan using Windows Defender in the Background.",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
@@ -75,16 +75,16 @@
"guid": "3c46290b-85db-4cd2-93a2-943c8c93b3b1",
"filename": "Speedtest.py",
"submittedBy": "https://github.com/wh1te909",
"name": "Speed Test",
"name": "Speed Test - Python",
"description": "Runs a Speed Test using Python",
"shell": "python",
"category": "TRMM (Win):Network"
},
{
"guid": "9d34f482-1f0c-4b2f-b65f-a9cf3c13ef5f",
"filename": "Win_Rename_Installed_App.ps1",
"filename": "Win_TRMM_Rename_Installed_App.ps1",
"submittedBy": "https://github.com/bradhawkins85",
"name": "Rename Tactical RMM Agent",
"name": "TacticalRMM Agent Rename",
"description": "Updates the DisplayName registry entry for the Tactical RMM windows agent to your desired name. This script takes 1 required argument: the name you wish to set.",
"shell": "powershell",
"category": "TRMM (Win):TacticalRMM Related"
@@ -93,7 +93,7 @@
"guid": "525ae965-1dcf-4c17-92b3-5da3cf6819f5",
"filename": "Win_Bitlocker_Encrypted_Drive_c.ps1",
"submittedBy": "https://github.com/ThatsNASt",
"name": "Check C Drive for Bitlocker Status",
"name": "Bitlocker - Check C Drive for Status",
"description": "Runs a check on drive C for Bitlocker status.",
"shell": "powershell",
"category": "TRMM (Win):Storage"
@@ -102,7 +102,7 @@
"guid": "2ea35fa2-c227-4d17-a40e-4d39f252e27a",
"filename": "Win_Bitlocker_Create_Status_Report.ps1",
"submittedBy": "https://github.com/ThatsNASt",
"name": "Create Bitlocker Status Report",
"name": "Bitlocker - Create Status Report",
"description": "Creates a Bitlocker status report.",
"shell": "powershell",
"category": "TRMM (Win):Storage"
@@ -111,34 +111,61 @@
"guid": "9e5769c1-3873-4941-bf70-e851e0afbd6d",
"filename": "Win_Bitlocker_Retrieve_Status_Report.ps1",
"submittedBy": "https://github.com/ThatsNASt",
"name": "Retreive Bitlocker Status Report",
"name": "Bitlocker - Retrieve Status Report",
"description": "Retreives a Bitlocker status report.",
"shell": "powershell",
"category": "TRMM (Win):Storage"
},
{
"guid": "72b93487-0266-43f0-97cc-03d4c7ee0b44",
"filename": "Win_Bitlocker_Get_Recovery_Keys.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Bitlocker - Get Recovery Keys",
"description": "Retreives a Bitlocker Recovery Keys",
"shell": "powershell",
"category": "TRMM (Win):Storage"
},
{
"guid": "cfa14c28-4dfc-4d4e-95ee-a380652e058d",
"filename": "Win_Bios_Check.ps1",
"submittedBy": "https://github.com/ThatsNASt",
"name": "Check BIOS Information",
"name": "BIOS - Check Information",
"description": "Retreives and reports on BIOS make, version, and date.",
"shell": "powershell",
"category": "TRMM (Win):Hardware"
},
{
"guid": "e1c27982-b955-4766-85b6-d92527a177cf",
"filename": "Win_Hardware_Monitor_Get_Info.ps1",
"submittedBy": "https://github.com/MaxAnderson95/",
"name": "Monitor - Get Info",
"description": "Retreives and reports on Monitor info: Manufacturer, Model, Serial",
"shell": "powershell",
"category": "TRMM (Win):Hardware"
},
{
"guid": "ae231ac4-b01f-4a39-a9d2-3d817af75260",
"filename": "Win_Hardware_RAM_Status.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "RAM - Check Information",
"description": "Retreives and reports on RAM info: DIMM's, total memory, slots total and used",
"shell": "powershell",
"category": "TRMM (Win):Hardware"
},
{
"guid": "95a2ee6f-b89b-4551-856e-3081b041caa7",
"filename": "Win_Reset_High_Performance_Power_Profile_to_Defaults.ps1",
"filename": "Win_Power_Profile_Reset_High_Performance_to_Defaults.ps1",
"submittedBy": "https://github.com/azulskyknight",
"name": "Reset High Perf Power Profile",
"name": "Power Profile - Reset High Perf Power Profile to defaults",
"description": "Resets monitor, disk, standby, and hibernate timers in the default High Performance power profile to their default values. It also re-indexes the AC and DC power profiles into their default order.",
"shell": "powershell",
"category": "TRMM (Win):Power"
},
{
"guid": "2cbd30b0-84dd-4388-a36d-2e2e980f1a3e",
"filename": "Win_Set_High_Performance_Power_Profile.ps1",
"filename": "Win_Power_Profile_Set_High_Performance.ps1",
"submittedBy": "https://github.com/azulskyknight",
"name": "Set High Perf Power Profile",
"name": "Power Profile - Set High Performance",
"description": "Sets the High Performance Power profile to the active power profile. Use this to keep machines from falling asleep.",
"shell": "powershell",
"category": "TRMM (Win):Power"
@@ -156,7 +183,7 @@
"guid": "375323e5-cac6-4f35-a304-bb7cef35902d",
"filename": "Win_Disk_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Check Disk Hardware Health (using Event Viewer errors)",
"name": "Disk Hardware Health Check (using Event Viewer errors)",
"description": "Checks local disks for errors reported in event viewer within the last 24 hours",
"shell": "powershell",
"category": "TRMM (Win):Hardware"
@@ -165,7 +192,7 @@
"guid": "7c14beb4-d1c3-41aa-8e70-92a267d6e080",
"filename": "Win_Duplicati_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Check Duplicati",
"name": "Duplicati - Check Status",
"description": "Checks Duplicati Backup is running properly over the last 24 hours",
"shell": "powershell",
"category": "TRMM (Win):3rd Party Software"
@@ -174,7 +201,7 @@
"guid": "da51111c-aff6-4d87-9d76-0608e1f67fe5",
"filename": "Win_Defender_Enable.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Enable Windows Defender",
"name": "Defender - Enable",
"description": "Enables Windows Defender and sets preferences",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
@@ -183,8 +210,8 @@
"guid": "a223d03a-e22e-40e0-94f2-92dd8c481d14",
"filename": "Win_Open_SSH_Server_Install.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Install SSH",
"description": "Installs and enabled OpenSSH Server",
"name": "SSH - Install Feature and Enable",
"description": "Installs and enabled OpenSSH Server Feature in Win10",
"shell": "powershell",
"category": "TRMM (Win):Windows Features"
},
@@ -192,7 +219,7 @@
"guid": "2435297a-6263-4e90-8688-1847400d0e22",
"filename": "Win_RDP_enable.bat",
"submittedBy": "https://github.com/dinger1986",
"name": "Enable RDP",
"name": "RDP - Enable",
"description": "Enables RDP",
"shell": "cmd",
"category": "TRMM (Win):Windows Features"
@@ -201,7 +228,7 @@
"guid": "24f19ead-fdfe-46b4-9dcb-4cd0e12a3940",
"filename": "Win_Speedtest.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Speed Test Powershell",
"name": "Speed Test - Powershell",
"description": "Speed Test with Powershell(win 10 or server2016+)",
"shell": "powershell",
"category": "TRMM (Win):Network"
@@ -210,41 +237,69 @@
"guid": "a821975c-60df-4d58-8990-6cf8a55b4ee0",
"filename": "Win_Sync_Time.bat",
"submittedBy": "https://github.com/dinger1986",
"name": "Sync DC Time",
"name": "ADDC - Sync DC Time",
"description": "Syncs time with domain controller",
"shell": "cmd",
"category": "TRMM (Win):Active Directory"
},
{
"guid": "b6b9912f-4274-4162-99cc-9fd47fbcb292",
"filename": "Win_ADDC_Sync_Start.bat",
"submittedBy": "https://github.com/silversword411",
"name": "ADDC - Sync AD",
"description": "Trigger AD Sync on domain controller",
"shell": "cmd",
"category": "TRMM (Win):Active Directory"
},
{
"guid": "b720e320-7755-4c89-9992-e1a6c43699ed",
"filename": "Win_Defender_Clear_Logs.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Clear Defender Logs",
"name": "Defender - Clear Logs",
"description": "Clears Windows Defender Logs",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
},
{
"guid": "d980fda3-a068-47eb-8495-1aab07a24e64",
"filename": "Win_Defender_Status.ps1",
"filename": "Win_Defender_Status_Report_Last24hrs.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Defender Status",
"description": "This will check for Malware, Antispyware, that Windows Defender is Healthy, last scan etc within the last 24 hours",
"name": "Defender - Status Report Last 24hr",
"description": "Using Event Viewer, this will check for Malware and Antispyware within the last 24 hours and display, otherwise will report as Healthy",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
},
{
"guid": "6a0202fc-1b27-4905-95c0-0a03bb881893",
"filename": "Win_Defender_Status_Report_LastYear.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Defender - Status Report Last Year",
"description": "Using Event Viewer, this will check for Malware and Antispyware and display, otherwise will report as Healthy",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus",
"default_timeout": "300"
},
{
"guid": "9956e936-6fdb-4488-a9d8-8b274658037f",
"filename": "Win_Disable_Fast_Startup.bat",
"submittedBy": "https://github.com/dinger1986",
"name": "Disable Fast Startup",
"name": "Power - Fast Startup Disable",
"description": "Disables Faststartup on Windows 10",
"shell": "cmd",
"category": "TRMM (Win):Power"
},
{
"guid": "f628a02b-16c3-4ab5-b788-dec5bc2af1d9",
"filename": "Win_Power_Disable_Hibernation.bat",
"submittedBy": "https://github.com/silversword411",
"name": "Power - Hibernate Disable",
"description": "Disables Hibernation",
"shell": "cmd",
"category": "TRMM (Win):Power"
},
{
"guid": "2472bbaf-1941-4722-8a58-d1dd0f528801",
"filename": "Win_Update_Tactical_Exclusion.ps1",
"filename": "Win_TRMM_AV_Update_Exclusion.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "TRMM Defender Exclusions",
"description": "Windows Defender Exclusions for Tactical RMM",
@@ -255,7 +310,7 @@
"guid": "b253dc76-41a0-48ca-9cea-bee4277402c4",
"filename": "Win_Display_Message_To_User.ps1",
"submittedBy": "https://github.com/bradhawkins85",
"name": "Display Message To User",
"name": "Message Popup To User",
"description": "Displays a popup message to the currently logged on user",
"shell": "powershell",
"category": "TRMM (Win):Other"
@@ -264,7 +319,7 @@
"guid": "19224d21-bd39-44bc-b9cf-8f1ba3ca9c11",
"filename": "Win_Antivirus_Verify.ps1",
"submittedBy": "https://github.com/beejayzed",
"name": "Verify Antivirus Status",
"name": "Antivirus - Verify Status",
"description": "Verify and display status for all installed Antiviruses",
"shell": "powershell",
"category": "TRMM (Win):Security>Antivirus"
@@ -273,7 +328,7 @@
"guid": "f88c5c52-c6fe-44db-b727-b7912a4279ed",
"filename": "Win_Create_All_User_Logon_Script.ps1",
"submittedBy": "https://github.com/nr-plaxon",
"name": "Create User Logon Script",
"name": "Template Example - Create User Logon Script",
"description": "Creates a powershell script that runs at logon of any user on the machine in the security context of the user.",
"shell": "powershell",
"category": "TRMM (Win):Other"
@@ -282,7 +337,7 @@
"guid": "5615aa90-0272-427b-8acf-0ca019612501",
"filename": "Win_Chocolatey_Update_Installed.bat",
"submittedBy": "https://github.com/silversword411",
"name": "Chocolatey Update Installed Apps",
"name": "Update Installed Apps",
"description": "Update all apps that were installed using Chocolatey.",
"shell": "cmd",
"category": "TRMM (Win):3rd Party Software>Chocolatey"
@@ -291,7 +346,7 @@
"guid": "fff8024d-d72e-4457-84fa-6c780f69a16f",
"filename": "Win_AD_Check_And_Enable_AD_Recycle_Bin.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "AD - Check and Enable AD Recycle Bin",
"name": "ADDC - Check and Enable AD Recycle Bin",
"description": "Only run on Domain Controllers, checks for Active Directory Recycle Bin and enables if not already enabled",
"shell": "powershell",
"category": "TRMM (Win):Active Directory"
@@ -300,11 +355,38 @@
"guid": "71090fc4-faa6-460b-adb0-95d7863544e1",
"filename": "Win_Check_Events_for_Bluescreens.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Event Viewer - Check for Bluescreens",
"description": "This will check for Bluescreen events on your system",
"name": "Event Viewer - Bluescreen Notification",
"description": "Event Viewer Monitor - Notify Bluescreen events on your system",
"shell": "powershell",
"category": "TRMM (Win):Monitoring"
},
{
"guid": "8373846f-facc-49b9-9891-3a780a394c89",
"filename": "Win_Local_User_Created_Monitor.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Event Viewer - New User Notification",
"description": "Event Viewer Monitor - Notify when new Local user is created",
"shell": "powershell",
"category": "TRMM (Win):Monitoring"
},
{
"guid": "65e5cef1-8338-4180-a0bc-cd54e62de690",
"filename": "Win_Task_Scheduler_New_Items_Monitor.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Event Viewer - Task Scheduler New Item Notification",
"description": "Event Viewer Monitor - Notify when new Task Scheduler item is created",
"shell": "powershell",
"category": "TRMM (Win):Monitoring"
},
{
"guid": "08ca81f2-f044-4dfc-ad47-090b19b19d76",
"filename": "Win_User_Logged_in_with_Temp_Profile.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "User Logged in with temp profile check",
"description": "Check if users are logged in with a temp profile",
"shell": "powershell",
"category": "TRMM (Win):Other"
},
{
"guid": "5d905886-9eb1-4129-8b81-a013f842eb24",
"filename": "Win_Rename_Computer.ps1",
@@ -317,21 +399,12 @@
},
{
"guid": "f396dae2-c768-45c5-bd6c-176e56ed3614",
"filename": "Win_Finish_updates_and_restart.ps1",
"filename": "Win_Power_RestartorShutdown.ps1",
"submittedBy": "https://github.com/tremor021",
"name": "Finish updates and restart",
"description": "Finish installing updates and restart PC",
"name": "Power - Restart or Shutdown PC",
"description": "Restart PC. Add parameter: shutdown if you want to shutdown computer",
"shell": "powershell",
"category": "TRMM (Win):Other"
},
{
"guid": "63f89be0-a9c9-4c61-9b55-bce0b28b90b2",
"filename": "Win_Finish_updates_and_shutdown.ps1",
"submittedBy": "https://github.com/tremor021",
"name": "Finish updates and shutdown",
"description": "Finish installing updates and shutdown PC",
"shell": "powershell",
"category": "TRMM (Win):Other"
"category": "TRMM (Win):Updates"
},
{
"guid": "e09895d5-ca13-44a2-a38c-6e77c740f0e8",
@@ -350,9 +423,9 @@
},
{
"guid": "3abbb62a-3757-492c-8979-b4fc6174845d",
"filename": "Win_Disable_AutoRun.bat",
"filename": "Win_AutoRun_Disable.bat",
"submittedBy": "https://github.com/silversword411",
"name": "Disable Autorun",
"name": "Autorun - Disable",
"description": "Disable Autorun System Wide",
"shell": "cmd",
"category": "TRMM (Win):Other",
@@ -360,9 +433,9 @@
},
{
"guid": "4a11877a-7555-494c-ac74-29d6df3c1989",
"filename": "Win_Disable_Cortana.bat",
"filename": "Win_Cortana_Disable.bat",
"submittedBy": "https://github.com/silversword411",
"name": "Disable Cortana",
"name": "Cortana - Disable",
"description": "Disable Cortana System Wide",
"shell": "cmd",
"category": "TRMM (Win):Other",
@@ -372,7 +445,7 @@
"guid": "28ef1387-dd4f-4bab-b042-26250914e370",
"filename": "Win_WOL_Enable_Status.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "WoL - Enable function",
"name": "BROKEN Network WoL - Enable function",
"description": "Wake on Lan enable on Dell, HP, Lenovo",
"shell": "powershell",
"category": "TRMM (Win):Network",
@@ -382,20 +455,159 @@
"guid": "685d5432-0b84-46d5-98e8-3ec2054150fe",
"filename": "Win_WOL_Test_State.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "WoL - Test State",
"name": "BROKEN Network WoL - Test State",
"description": "Wake on Lan test status",
"shell": "powershell",
"category": "TRMM (Win):Network",
"default_timeout": "90"
},
{
"guid": "abe78170-7cf9-435b-9666-c5ef6c11a106",
"filename": "Win_Network_IPv6_Disable.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Network IPv6 - Disable",
"description": "Disable IPv6 on all adapters",
"shell": "powershell",
"category": "TRMM (Win):Network",
"default_timeout": "90"
},
{
"guid": "745bb7cd-b71a-4f2e-b6f2-c579b1828162",
"filename": "Win_Network_DHCP_Set.bat",
"submittedBy": "https://github.com/silversword411",
"name": "Network - Set Primary NIC to DHCP",
"description": "Enable DHCP on primary adapter",
"shell": "cmd",
"category": "TRMM (Win):Network",
"default_timeout": "90"
},
{
"guid": "83aa4d51-63ce-41e7-829f-3c16e6115bbf",
"filename": "Win_Network_DNS_Set_to_1.1.1.2.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Network - Set all NICs to use DNS of 1.1.1.2",
"description": "Domain computers skipped. Sets all NICs to have primary DNS server of 1.1.1.2, backup of 1.0.0.2 (Cloudflare malware blocking)",
"shell": "powershell",
"category": "TRMM (Win):Network",
"default_timeout": "90"
},
{
"guid": "6ce5682a-49db-4c0b-9417-609cf905ac43",
"filename": "Win_Win10_Change_Key_and_Activate.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Change Win10 Product Key and Activate",
"name": "Product Key in Win10 Change and Activate",
"description": "Insert new product key and Activate. Requires 1 parameter the product key you want to use",
"shell": "powershell",
"category": "TRMM (Win):Other",
"default_timeout": "90"
},
{
"guid": "83f6c6ea-6120-4fd3-bec8-d3abc505dcdf",
"filename": "Win_TRMM_Start_Menu_Delete_Shortcut.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "TacticalRMM Delete Start Menu Shortcut for App",
"description": "Delete its application shortcut that's installed in the start menu by default",
"shell": "powershell",
"category": "TRMM (Win):TacticalRMM Related",
"default_timeout": "10"
},
{
"guid": "60130fca-7636-446e-acd7-cc5d29d609c2",
"filename": "Win_Firewall_Check_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Windows Firewall - Check Status",
"description": "Windows Firewall - Check state, report status",
"shell": "powershell",
"category": "TRMM (Win):Network"
},
{
"guid": "93379675-c01c-433f-87df-a11597c959f0",
"filename": "Win_UAC_Check_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Windows UAC - Check Status",
"description": "Windows UAC - Report status",
"shell": "powershell",
"category": "TRMM (Win):Security"
},
{
"guid": "7ea6a11a-05c0-4151-b5c1-cb8af029299f",
"filename": "Win_AzureAD_Check_Connection_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Azure AD - Check Status",
"description": "Azure AD - Check if joined or not",
"shell": "powershell",
"category": "TRMM (Win):Azure>AD"
},
{
"guid": "7d81859a-1ba3-42b0-8664-29844f0dd765",
"filename": "Win_Azure_Mars_Cloud_Backup_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Azure - Mars Cloud backup Status",
"description": "Azure - Mars Cloud backup Check Status",
"shell": "powershell",
"category": "TRMM (Win):Azure>Backup"
},
{
"guid": "e18c64d0-b783-4b52-b44b-9bb7592b439b",
"filename": "Win_FileSystem_Enable_Long_Paths.bat",
"submittedBy": "https://github.com/silversword411",
"name": "File System - Enable Long Paths",
"description": "Enables NTFS Long paths greater than 260 characters",
"shell": "cmd",
"category": "TRMM (Win):Storage"
},
{
"guid": "c6252ca8-5172-42ea-9114-e447f80868f5",
"filename": "Win_USB_Disable_Access.bat",
"submittedBy": "https://github.com/silversword411",
"name": "USB - Disable Access",
"description": "USB - Disable Plugged in USB devices",
"shell": "cmd",
"category": "TRMM (Win):Storage"
},
{
"guid": "3785952f-69fb-4bda-b2fe-5e3e8642738a",
"filename": "Win_USB_Enable_Access.bat",
"submittedBy": "https://github.com/silversword411",
"name": "USB - Enable Access",
"description": "USB - Enable Plugged in USB devices",
"shell": "cmd",
"category": "TRMM (Win):Storage"
},
{
"guid": "c6014da2-b188-4e1b-b96a-e3440ade3a6a",
"filename": "Win_RecycleBin_Empty.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "File System - Empty Recycle Bin",
"description": "Empty the recycle bin",
"shell": "powershell",
"category": "TRMM (Win):Storage"
},
{
"guid": "57997ec7-b293-4fd5-9f90-a25426d0eb90",
"filename": "Win_Get_Computer_Users.ps1",
"submittedBy": "https://github.com/tremor021",
"name": "Get Computer Users",
"description": "Get list of computer users and show which one is enabled",
"shell": "powershell",
"category": "TRMM (Win):Other"
},
{
"guid": "77da9c87-5a7a-4ba1-bdde-3eeb3b01d62d",
"filename": "Win_Network_Set_To_Private.ps1",
"submittedBy": "https://github.com/tremor021",
"name": "Network Category - Set Network To Private",
"description": "Sets current network type to Private",
"shell": "powershell",
"category": "TRMM (Win):Network"
},
{
"guid": "768f42d5-7b45-45ed-8233-254ae537aaa2",
"filename": "Win_TaskScheduler_Add_Task.ps1",
"submittedBy": "https://github.com/tremor021",
"name": "Task Scheduler - Add a task",
"description": "Add a task to Task Scheduler, needs editing",
"shell": "powershell",
"category": "TRMM (Win):Other"
}
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.7 on 2021-04-15 02:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('scripts', '0007_script_args'),
]
operations = [
migrations.AddField(
model_name='script',
name='guid',
field=models.CharField(blank=True, max_length=64, null=True),
),
]

View File

@@ -1,10 +1,11 @@
import base64
import re
from loguru import logger
from typing import Any, List, Union
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.db import models
from loguru import logger
from logs.models import BaseAuditModel
@@ -23,6 +24,7 @@ logger.configure(**settings.LOG_CONFIG)
class Script(BaseAuditModel):
guid = name = models.CharField(max_length=64, null=True, blank=True)
name = models.CharField(max_length=255)
description = models.TextField(null=True, blank=True)
filename = models.CharField(max_length=255) # deprecated
@@ -78,9 +80,7 @@ class Script(BaseAuditModel):
for script in info:
if os.path.exists(os.path.join(scripts_dir, script["filename"])):
s = cls.objects.filter(script_type="builtin").filter(
name=script["name"]
)
s = cls.objects.filter(script_type="builtin", guid=script["guid"])
category = (
script["category"] if "category" in script.keys() else "Community"
@@ -120,6 +120,46 @@ class Script(BaseAuditModel):
"args",
]
)
# check if script was added without a guid
elif cls.objects.filter(
script_type="builtin", name=script["name"]
).exists():
s = cls.objects.get(script_type="builtin", name=script["name"])
if not s.guid:
print(f"Updating GUID for: {script['name']}")
s.guid = script["guid"]
s.name = script["name"]
s.description = script["description"]
s.category = category
s.shell = script["shell"]
s.default_timeout = default_timeout
s.args = args
with open(
os.path.join(scripts_dir, script["filename"]), "rb"
) as f:
script_bytes = (
f.read().decode("utf-8").encode("ascii", "ignore")
)
s.code_base64 = base64.b64encode(script_bytes).decode(
"ascii"
)
s.save(
update_fields=[
"guid",
"name",
"description",
"category",
"default_timeout",
"code_base64",
"shell",
"args",
]
)
else:
print(f"Adding new community script: {script['name']}")
@@ -131,6 +171,7 @@ class Script(BaseAuditModel):
cls(
code_base64=code_base64,
guid=script["guid"],
name=script["name"],
description=script["description"],
filename=script["filename"],
@@ -141,6 +182,9 @@ class Script(BaseAuditModel):
args=args,
).save()
# delete community scripts that had their name changed
cls.objects.filter(script_type="builtin", guid=None).delete()
@staticmethod
def serialize(script):
# serializes the script and returns json

View File

@@ -1,6 +1,6 @@
from email.policy import default
import json
import os
from email.policy import default
from pathlib import Path
from django.conf import settings
@@ -245,12 +245,39 @@ class TestScriptViews(TacticalTestCase):
Script.load_community_scripts()
community_scripts = Script.objects.filter(script_type="builtin").count()
self.assertEqual(len(info), community_scripts)
community_scripts_count = Script.objects.filter(script_type="builtin").count()
if len(info) != community_scripts_count:
raise Exception(
f"There are {len(info)} scripts in json file but only {community_scripts_count} in database"
)
# test updating already added community scripts
Script.load_community_scripts()
self.assertEqual(len(info), community_scripts)
community_scripts_count2 = Script.objects.filter(script_type="builtin").count()
self.assertEqual(len(info), community_scripts_count2)
def test_community_script_has_jsonfile_entry(self):
with open(
os.path.join(settings.BASE_DIR, "scripts/community_scripts.json")
) as f:
info = json.load(f)
filenames = [i["filename"] for i in info]
# normal
if not settings.DOCKER_BUILD:
scripts_dir = os.path.join(Path(settings.BASE_DIR).parents[1], "scripts")
# docker
else:
scripts_dir = settings.SCRIPTS_DIR
with os.scandir(scripts_dir) as it:
for f in it:
if not f.name.startswith(".") and f.is_file():
if f.name not in filenames:
raise Exception(
f"{f.name} is missing an entry in community_scripts.json"
)
def test_script_filenames_do_not_contain_spaces(self):
with open(
@@ -259,4 +286,5 @@ class TestScriptViews(TacticalTestCase):
info = json.load(f)
for script in info:
fn: str = script["filename"]
self.assertTrue(" " not in fn)
if " " in fn:
raise Exception(f"{fn} must not contain spaces in filename")

View File

@@ -15,16 +15,16 @@ EXE_DIR = os.path.join(BASE_DIR, "tacticalrmm/private/exe")
AUTH_USER_MODEL = "accounts.User"
# latest release
TRMM_VERSION = "0.5.2"
TRMM_VERSION = "0.6.2"
# bump this version everytime vue code is changed
# to alert user they need to manually refresh their browser
APP_VER = "0.0.127"
APP_VER = "0.0.131"
# https://github.com/wh1te909/rmmagent
LATEST_AGENT_VER = "1.4.13"
LATEST_AGENT_VER = "1.5.2"
MESH_VER = "0.7.93"
MESH_VER = "0.8.17"
# for the update script, bump when need to recreate venv or npm install
PIP_VER = "15"
@@ -34,8 +34,8 @@ DL_64 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_
DL_32 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_VER}/winagent-v{LATEST_AGENT_VER}-x86.exe"
EXE_GEN_URLS = [
"https://exe2.tacticalrmm.io/api/v1/exe",
"https://exe.tacticalrmm.io/api/v1/exe",
"https://exe2.tacticalrmm.io",
"https://exe.tacticalrmm.io",
]
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"

View File

@@ -12,6 +12,7 @@ from .utils import (
generate_winagent_exe,
get_bit_days,
reload_nats,
run_nats_api_cmd,
)
@@ -74,6 +75,12 @@ class TestUtils(TestCase):
mock_subprocess.assert_called_once()
@patch("subprocess.run")
def test_run_nats_api_cmd(self, mock_subprocess):
ids = ["a", "b", "c"]
_ = run_nats_api_cmd("monitor", ids)
mock_subprocess.assert_called_once()
def test_bitdays_to_string(self):
a = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
all_days = [

View File

@@ -4,6 +4,7 @@ import string
import subprocess
import tempfile
import time
import urllib.parse
from typing import Union
import pytz
@@ -19,6 +20,7 @@ from rest_framework import status
from rest_framework.response import Response
from agents.models import Agent
from core.models import CodeSignToken
logger.configure(**settings.LOG_CONFIG)
@@ -50,12 +52,27 @@ def generate_winagent_exe(
file_name: str,
) -> Union[Response, FileResponse]:
from agents.utils import get_exegen_url
inno = (
f"winagent-v{settings.LATEST_AGENT_VER}.exe"
if arch == "64"
else f"winagent-v{settings.LATEST_AGENT_VER}-x86.exe"
)
try:
codetoken = CodeSignToken.objects.first().token
base_url = get_exegen_url() + "/api/v1/winagents/?"
params = {
"version": settings.LATEST_AGENT_VER,
"arch": arch,
"token": codetoken,
}
dl_url = base_url + urllib.parse.urlencode(params)
except:
codetoken = ""
dl_url = settings.DL_64 if arch == "64" else settings.DL_32
data = {
"client": client,
"site": site,
@@ -66,8 +83,9 @@ def generate_winagent_exe(
"goarch": "amd64" if arch == "64" else "386",
"token": token,
"inno": inno,
"url": settings.DL_64 if arch == "64" else settings.DL_32,
"url": dl_url,
"api": api,
"codesigntoken": codetoken,
}
headers = {"Content-type": "application/json"}
@@ -76,7 +94,7 @@ def generate_winagent_exe(
for url in settings.EXE_GEN_URLS:
try:
r = requests.post(
url,
f"{url}/api/v1/exe",
json=data,
headers=headers,
stream=True,
@@ -228,3 +246,20 @@ class KnoxAuthMiddlewareInstance:
KnoxAuthMiddlewareStack = lambda inner: KnoxAuthMiddlewareInstance(
AuthMiddlewareStack(inner)
)
def run_nats_api_cmd(mode: str, ids: list[str], timeout: int = 30) -> None:
config = {
"key": settings.SECRET_KEY,
"natsurl": f"tls://{settings.ALLOWED_HOSTS[0]}:4222",
"agents": ids,
}
with tempfile.NamedTemporaryFile() as fp:
with open(fp.name, "w") as f:
json.dump(config, f)
cmd = ["/usr/local/bin/nats-api", "-c", fp.name, "-m", mode]
try:
subprocess.run(cmd, capture_output=True, timeout=timeout)
except Exception as e:
logger.error(e)

View File

@@ -14,8 +14,29 @@ class TestWinUpdateViews(TacticalTestCase):
self.authenticate()
self.setup_coresettings()
def test_get_winupdates(self):
@patch("agents.models.Agent.nats_cmd")
def test_run_update_scan(self, nats_cmd):
agent = baker.make_recipe("agents.agent")
url = f"/winupdate/{agent.pk}/runupdatescan/"
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
nats_cmd.assert_called_with({"func": "getwinupdates"}, wait=False)
self.check_not_authenticated("get", url)
@patch("agents.models.Agent.nats_cmd")
def test_install_updates(self, nats_cmd):
agent = baker.make_recipe("agents.agent")
baker.make("winupdate.WinUpdate", agent=agent, _quantity=4)
baker.make("winupdate.WinUpdatePolicy", agent=agent)
url = f"/winupdate/{agent.pk}/installnow/"
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
nats_cmd.assert_called_once()
self.check_not_authenticated("get", url)
def test_get_winupdates(self):
agent = baker.make_recipe("agents.agent")
baker.make("winupdate.WinUpdate", agent=agent, _quantity=4)
@@ -27,8 +48,8 @@ class TestWinUpdateViews(TacticalTestCase):
resp = self.client.get(url, format="json")
serializer = UpdateSerializer(agent)
self.assertEqual(resp.status_code, 200)
self.assertEqual(len(resp.data["winupdates"]), 4)
self.assertEqual(resp.data, serializer.data)
self.assertEqual(len(resp.data["winupdates"]), 4) # type: ignore
self.assertEqual(resp.data, serializer.data) # type: ignore
self.check_not_authenticated("get", url)
@@ -99,7 +120,7 @@ class TestWinUpdateViews(TacticalTestCase):
resp = self.client.patch(url, invalid_data, format="json")
self.assertEqual(resp.status_code, 404)
data = {"pk": winupdate.pk, "policy": "inherit"}
data = {"pk": winupdate.pk, "policy": "inherit"} # type: ignore
resp = self.client.patch(url, data, format="json")
self.assertEqual(resp.status_code, 200)

View File

@@ -1,12 +1,11 @@
import asyncio
from django.shortcuts import get_object_or_404
from packaging import version as pyver
from rest_framework.decorators import api_view
from rest_framework.response import Response
from agents.models import Agent
from tacticalrmm.utils import get_default_timezone, notify_error
from tacticalrmm.utils import get_default_timezone
from .models import WinUpdate
from .serializers import UpdateSerializer
@@ -24,9 +23,6 @@ def get_win_updates(request, pk):
def run_update_scan(request, pk):
agent = get_object_or_404(Agent, pk=pk)
agent.delete_superseded_updates()
if pyver.parse(agent.version) < pyver.parse("1.3.0"):
return notify_error("Requires agent version 1.3.0 or greater")
asyncio.run(agent.nats_cmd({"func": "getwinupdates"}, wait=False))
return Response("ok")
@@ -35,9 +31,6 @@ def run_update_scan(request, pk):
def install_updates(request, pk):
agent = get_object_or_404(Agent, pk=pk)
agent.delete_superseded_updates()
if pyver.parse(agent.version) < pyver.parse("1.3.0"):
return notify_error("Requires agent version 1.3.0 or greater")
agent.approve_updates()
nats_data = {
"func": "installwinupdates",

View File

@@ -4,7 +4,7 @@ A backup script is provided for quick and easy way to backup all settings into o
Download the backup script:
```bash
wget https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh
wget -N https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh
```
From the Web UI, click **Tools > Server Maintenance**

22
docs/docs/code_signing.md Normal file
View File

@@ -0,0 +1,22 @@
# Code Signing
*Version added: Tactical RMM v0.6.0 / Agent v1.5.0*
Tactical RMM agents are now [code signed](https://comodosslstore.com/resources/what-is-microsoft-authenticode-code-signing-certificate/)!
To get access to code signed agents, you must be a [Github Sponsor](https://github.com/sponsors/wh1te909) with a minumum monthly donation of $50.00
Once you have become a sponsor, please email **support@amidaware.com** with your Github username (and Discord username if you're on our [Discord](https://discord.gg/upGTkWp))
Please allow up to 24 hours for a response
You will then be sent a code signing auth token, which you should enter into Tactical's web UI from *Settings > Code Signing*
## How does it work?
Everytime you generate an agent or an agent does a self-update, your self-hosted instance sends a request to Tactical's code signing servers with your auth token.
If the token is valid, the server sends you back a code signed agent. If not, it sends you back the un-signed agent.
If you think your auth token has been compromised or stolen then please email support or contact wh1te909 on discord to get a new token / invalidate the old one.

View File

@@ -0,0 +1,103 @@
## Script Library Naming Conventions
### File names
Under `/scripts` the file name should generally follow this format:
```
(Platform)_(Category or Function)_(What It Does).xxx
```
!!!info
Although Tactical RMM only has a Windows agent for now, we're planning for a future with more platform support
Platform for now are:
```
Win
OSX
Linux
iOS
Android
```
Good filename examples include:
```
Win_Azure_Mars_Cloud_Backup_Status.ps1
Win_AzureAD_Check_Connection_Status.ps1
Win_Network_DHCP_Set.bat
Win_Network_DNS_Set_to_1.1.1.2.ps1
```
!!!info
This is so that at a glance you can see if there is already a script with that function, and you can avoid duplication of functionality. If you can improve a script or allow Script Arguments/Parameters update existing if possible
### Name field (in community_scripts.json)
Consider they are viewed in 3 different locations:
Script Manager
- List View (sortable by any column)
- Folder View (Grouped by Categories)
Run or Add script
- Running scripts manually or adding tasks (or adding in Automation Manager)
!!!info
A good max length is 50-60 chars or less for display in these 3 locations
Make sure your Name roughly follows the order of file naming as above
```
Category or Function - What It Does
```
Consider how the alphabetic sort will affect display
![json_name_examples](images/community_scripts_name_field_example1.png)
## Making Script Files
### Good Habits
- Try and make them fully self-contained.
- If they pull data from elsewhere, create comment notes at the top with references for others to audit/validate
- Good folder locations
```
c:\ProgramData\TacticalRMM\
c:\ProgramData\TacticalRMM\scripts
c:\ProgramData\TacticalRMM\toolbox
c:\ProgramData\TacticalRMM\logs
c:\ProgramData\TacticalRMM\temp
c:\ProgramData\TacticalRMM\
```
- Command Parameters are good. Optional command parameters for extra functions are better.
### Bad Habits
- Assumes non-standard configurations
- Doesn't play well with other community scripts (reused names etc.)
## Useful Reference Script Examples
RunAsUser (since Tactical RMM runs as system)
[https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_Display_Message_To_User.ps1](https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_Display_Message_To_User.ps1)
Command Paramater Ninja
[https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_ScreenConnectAIO.ps1](https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_ScreenConnectAIO.ps1)
Optional Command Parameters and testing for errors
[https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_Rename_Computer.ps1](https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_Rename_Computer.ps1)

View File

@@ -1,4 +1,5 @@
## Getting Started
### 1. Install vscode
[https://code.visualstudio.com/download](https://code.visualstudio.com/download)
@@ -19,7 +20,7 @@ Login to your Github
Choose local folder
### 3a. Install extra vscode Extensions
#### 3a. Install extra vscode Extensions
GitLens
@@ -94,4 +95,16 @@ Then you're `push`ing that updated local repo to your online Github fork
### 8. Verify and Repeat
Check your Github fork in browser, should be up to date now with original. Repeat 6 or 7 as necessary
Check your Github fork in browser, should be up to date now with original. Repeat 6 or 7 as necessary
## Reference
### Customizing the Admin Web Interface
Created using quasar, it's all your .vue files in `web/src/components/modals/agents/RunScript.vue`
Learn stuff here
https://quasar.dev/

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,6 +1,7 @@
# Installing an agent
!!!warning
If you don't want to deal with AV flagging/deleting your agents, check the instructions for getting [code signed agents](code_signing.md)<br/><br />
You must add antivirus exlusions for the tactical agent.<br/>
Any decent AV will flag the agent as a virus, since it technically is one due to the nature of this software.<br/>
Adding the following exlucions will make sure everything works, including agent update:<br/>

View File

@@ -7,7 +7,8 @@
The provided install script assumes a fresh server with no software installed on it. Attempting to run it on an existing server with other services **will** break things and the install will fail.<br/><br/>
The install script has been tested on the following public cloud providers: DigitalOcean, Linode, Vultr, BuyVM (highly recommended), Hetzner, AWS, Google Cloud and Azure, as well as behind NAT on Hyper-V, Proxmox and ESXi.
- A real domain is needed to generate a Let's Encrypt wildcard cert. <br/>If you cannot afford to purchase a domain ($12 a year) then you can get one for free at [freenom.com](https://www.freenom.com/)<br/><br/>
- A real (internet resolvable) domain is needed to generate a Let's Encrypt wildcard cert. <br/>If you cannot afford to purchase a domain ($12 a year) then you can get one for free at [freenom.com](https://www.freenom.com/)
- example.local is __NOT__ a real domain. No you [don't have to expose your server](faq.md#can-i-run-tactical-rmm-locally-behind-nat-without-exposing-anything-to-the-internet) to the internet<br/><br/>
- A TOTP based authenticator app. Some popular ones are Google Authenticator, Authy and Microsoft Authenticator.<br/><br/>

View File

@@ -30,6 +30,9 @@ If you have agents that are relatively old, you will need to uninstall them manu
#### Agents not checking in or showing up / General agent issues
First, reload NATS from tactical's web UI:<br />
*Tools > Server Maintenance > Reload Nats Configuration*
Open CMD as admin on the problem computer and stop the agent services:
```cmd
@@ -51,7 +54,7 @@ This will print out a ton of info. You should be able to see the error from the
Please then copy/paste the logs and post them either in our [Discord support chat](https://discord.gg/upGTkWp), or create a [github issue](https://github.com/wh1te909/tacticalrmm/issues).
<br/>
If all else fails, simply uninstall the agent either from control panel or silently with `"C:\Program Files\TacticalAgent\unins000.exe" /VERYSILENT` and then reinstall the agent.
#### All other errors

View File

@@ -2,6 +2,7 @@ site_name: "Tactical RMM Documentation"
nav:
- Home: index.md
- Sponsor: sponsor.md
- Code Signing: code_signing.md
- RMM Installation:
- "Traditional Install": install_server.md
- "Docker Install": install_docker.md
@@ -23,9 +24,10 @@ nav:
- FAQ: faq.md
- Management Commands: management_cmds.md
- MeshCentral Integration: mesh_integration.md
- Contributing:
- Contributing:
- "Contributing to Docs": contributing.md
- "Contributing using VSCode": contributing_using_vscode.md
- "Contributing to Community Scripts": contributing_community_scripts.md
- License: license.md
site_description: "A remote monitoring and management tool"
site_author: "wh1te909"

View File

@@ -0,0 +1,2 @@
net start ADSync
exit

View File

@@ -0,0 +1,15 @@
$ErrorActionPreference = 'silentlycontinue'
$aadchk = dsregcmd /status | Where-Object { $_ -match 'AzureAdJoined : ' } | ForEach-Object { $_.Trim() }
if ($aadchk -Eq 'AzureAdJoined : Yes') {
Write-Output "Machine is Azure Ad Joined"
exit 0
}
else {
Write-Output "Machine is not Azure Ad Joined"
exit 1
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1,23 @@
$ErrorActionPreference= 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
##Check for Errors in Backup
if (Get-WinEvent -FilterHashtable @{LogName='CloudBackup/Operational';ID='11','18';StartTime=$TimeSpan})
{
Write-Host "Cloud Backup Mars Ended with Errors"
Get-WinEvent -FilterHashtable @{LogName='CloudBackup/Operational';ID='1','14','11','18','16';StartTime=$TimeSpan}
exit 1
}
else
{
Write-Host "Cloud Backup Mars Backup Is Working Correctly"
Get-WinEvent -FilterHashtable @{LogName='CloudBackup/Operational';ID='1','14','16'}
exit 0
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1 @@
manage-bde -protectors C: -get

View File

@@ -1,24 +1,24 @@
# This will check for Malware, Antispyware, that Windows Defender is Healthy, last scan etc within the last 24 hours
$ErrorActionPreference= 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
if (Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1116','1118','1015','1006','5010','5012','5001','1123';StartTime=$TimeSpan})
{
Write-Output "Virus Found or Issue with Defender"
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1116','1118','1015','1006','5010','5012','5001','1123';StartTime=$TimeSpan}
exit 1
}
else
{
Write-Output "No Virus Found, Defender is Healthy"
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1150','1001';StartTime=$TimeSpan}
exit 0
}
Exit $LASTEXITCODE
# This will check for Malware, Antispyware, that Windows Defender is Healthy, last scan etc within the last 24 hours
$ErrorActionPreference= 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
if (Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1116','1118','1015','1006','5010','5012','5001','1123';StartTime=$TimeSpan})
{
Write-Output "Virus Found or Issue with Defender"
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1116','1118','1015','1006','5010','5012','5001','1123';StartTime=$TimeSpan}
exit 1
}
else
{
Write-Output "No Virus Found, Defender is Healthy"
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Windows Defender/Operational';ID='1150','1001';StartTime=$TimeSpan}
exit 0
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1,22 @@
# This will check for Malware, Antispyware, that Windows Defender is Healthy, last scan etc within the last 24 hours
$ErrorActionPreference = 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 365)
if (Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-Windows Defender/Operational'; ID = '1116', '1118', '1015', '1006', '5010', '5012', '5001', '1123'; StartTime = $TimeSpan })
{
Write-Output "Virus Found or Issue with Defender"
Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-Windows Defender/Operational'; ID = '1116', '1118', '1015', '1006', '5010', '5012', '5001', '1123'; StartTime = $TimeSpan }
exit 1
}
else
{
Write-Output "No Virus Found, Defender is Healthy"
Get-WinEvent -FilterHashtable @{LogName = 'Microsoft-Windows-Windows Defender/Operational'; ID = '1150', '1001'; StartTime = $TimeSpan }
exit 0
}
Exit $LASTEXITCODE

View File

@@ -1,20 +1,18 @@
# Checks local disks for errors reported in event viewer within the last 24 hours
$ErrorActionPreference= 'silentlycontinue'
$ErrorActionPreference = 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
if (Get-WinEvent -FilterHashtable @{LogName='system';ID='11','9','15','52','129','7','98';Level=2,3;ProviderName='*disk*','*storsvc*','*ntfs*';StartTime=$TimeSpan})
if (Get-WinEvent -FilterHashtable @{LogName = 'system'; ID = '11', '9', '15', '52', '129', '7', '98'; Level = 2, 3; ProviderName = '*disk*', '*storsvc*', '*ntfs*'; StartTime = $TimeSpan } -MaxEvents 10 | Where-Object -Property Message -Match Volume*)
{
Write-Output "Disk errors detected please investigate"
Get-WinEvent -FilterHashtable @{LogName='system';ID='11','9','15','52','129','7','98';Level=2,3;ProviderName='*disk*','*storsvc*','*ntfs*';StartTime=$TimeSpan}
exit 1
Write-Output "Disk errors detected please investigate"
Get-WinEvent -FilterHashtable @{LogName = 'system'; ID = '11', '9', '15', '52', '129', '7', '98'; Level = 2, 3; ProviderName = '*disk*', '*storsvc*', '*ntfs*'; StartTime = $TimeSpan }
exit 1
}
else
{
Write-Output "Disks are Healthy"
exit 0
else {
Write-Output "Disks are Healthy"
exit 0
}

View File

@@ -0,0 +1 @@
REG ADD "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem" /V LongPathsEnabled /T REG_DWORD /D 1 /F

View File

@@ -1 +0,0 @@
Restart-Computer -ComputerName $env:COMPUTERNAME -Force

View File

@@ -1 +0,0 @@
Stop-Computer -ComputerName $env:COMPUTERNAME -Force

View File

@@ -0,0 +1,17 @@
$ErrorActionPreference = 'silentlycontinue'
$fwenabled = (get-netfirewallprofile -policystore activestore).Enabled
if ($fwenabled.Contains('True')) {
Write-Output "Firewall is Enabled"
netsh advfirewall show currentprofile
exit 0
}
else {
Write-Host "Firewall is Disabled"
exit 1
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1,5 @@
# This script return the list of all users and checks
# if they are enabled or disabled
get-localuser | Select name,Enabled > $env:TEMP\users.txt
Get-Content $env:TEMP\users.txt | foreach {Write-Output $_}

View File

@@ -0,0 +1,154 @@
<#
.SYNOPSIS
This powershell function gets information about the monitors attached to any computer. It uses EDID information provided by WMI. If this value is not specified it pulls the monitors of the computer that the script is being run on.
.DESCRIPTION
The function begins by looping through each computer specified. For each computer it gets a litst of monitors.
It then gets all of the necessary data from each monitor object and converts and cleans the data and places it in a custom PSObject. It then adds
the data to an array. At the end the array is displayed.
.PARAMETER ComputerName
Use this to specify the computer(s) which you'd like to retrieve information about monitors from.
.EXAMPLE
PS C:/> Get-Monitor.ps1 -ComputerName SSL1-F1102-1G2Z
Manufacturer Model SerialNumber AttachedComputer
------------ ----- ------------ ----------------
HP HP E241i CN12345678 SSL1-F1102-1G2Z
HP HP E241i CN91234567 SSL1-F1102-1G2Z
HP HP E241i CN89123456 SSL1-F1102-1G2Z
.EXAMPLE
PS C:/> $Computers = @("SSL7-F108F-9D4Z","SSL1-F1102-1G2Z","SSA7-F1071-0T7F")
PS C:/> Get-Monitor.ps1 -ComputerName $Computers
Manufacturer Model SerialNumber AttachedComputer
------------ ----- ------------ ----------------
HP HP LA2405x CN12345678 SSL7-F108F-9D4Z
HP HP E241i CN91234567 SSL1-F1102-1G2Z
HP HP E241i CN89123456 SSL1-F1102-1G2Z
HP HP E241i CN78912345 SSL1-F1102-1G2Z
HP HP ZR22w CN67891234 SSA7-F1071-0T7F
#>
[CmdletBinding()]
PARAM (
[Parameter(ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[String[]]$ComputerName = $env:ComputerName
)
#List of Manufacture Codes that could be pulled from WMI and their respective full names. Used for translating later down.
$ManufacturerHash = @{
"AAC" = "AcerView";
"ACR" = "Acer";
"AOC" = "AOC";
"AIC" = "AG Neovo";
"APP" = "Apple Computer";
"AST" = "AST Research";
"AUO" = "Asus";
"BNQ" = "BenQ";
"CMO" = "Acer";
"CPL" = "Compal";
"CPQ" = "Compaq";
"CPT" = "Chunghwa Pciture Tubes, Ltd.";
"CTX" = "CTX";
"DEC" = "DEC";
"DEL" = "Dell";
"DPC" = "Delta";
"DWE" = "Daewoo";
"EIZ" = "EIZO";
"ELS" = "ELSA";
"ENC" = "EIZO";
"EPI" = "Envision";
"FCM" = "Funai";
"FUJ" = "Fujitsu";
"FUS" = "Fujitsu-Siemens";
"GSM" = "LG Electronics";
"GWY" = "Gateway 2000";
"HEI" = "Hyundai";
"HIT" = "Hyundai";
"HSL" = "Hansol";
"HTC" = "Hitachi/Nissei";
"HWP" = "HP";
"IBM" = "IBM";
"ICL" = "Fujitsu ICL";
"IVM" = "Iiyama";
"KDS" = "Korea Data Systems";
"LEN" = "Lenovo";
"LGD" = "Asus";
"LPL" = "Fujitsu";
"MAX" = "Belinea";
"MEI" = "Panasonic";
"MEL" = "Mitsubishi Electronics";
"MS_" = "Panasonic";
"NAN" = "Nanao";
"NEC" = "NEC";
"NOK" = "Nokia Data";
"NVD" = "Fujitsu";
"OPT" = "Optoma";
"PHL" = "Philips";
"REL" = "Relisys";
"SAN" = "Samsung";
"SAM" = "Samsung";
"SBI" = "Smarttech";
"SGI" = "SGI";
"SNY" = "Sony";
"SRC" = "Shamrock";
"SUN" = "Sun Microsystems";
"SEC" = "Hewlett-Packard";
"TAT" = "Tatung";
"TOS" = "Toshiba";
"TSB" = "Toshiba";
"VSC" = "ViewSonic";
"ZCM" = "Zenith";
"UNK" = "Unknown";
"_YV" = "Fujitsu";
}
#Takes each computer specified and runs the following code:
ForEach ($Computer in $ComputerName) {
#Grabs the Monitor objects from WMI
$Monitors = Get-WmiObject -Namespace "root\WMI" -Class "WMIMonitorID" -ComputerName $Computer -ErrorAction SilentlyContinue
#Creates an empty array to hold the data
$Monitor_Array = @()
#Takes each monitor object found and runs the following code:
ForEach ($Monitor in $Monitors) {
#Grabs respective data and converts it from ASCII encoding and removes any trailing ASCII null values
If ([System.Text.Encoding]::ASCII.GetString($Monitor.UserFriendlyName) -ne $null) {
$Mon_Model = ([System.Text.Encoding]::ASCII.GetString($Monitor.UserFriendlyName)).Replace("$([char]0x0000)", "")
}
else {
$Mon_Model = $null
}
$Mon_Serial_Number = ([System.Text.Encoding]::ASCII.GetString($Monitor.SerialNumberID)).Replace("$([char]0x0000)", "")
$Mon_Attached_Computer = ($Monitor.PSComputerName).Replace("$([char]0x0000)", "")
$Mon_Manufacturer = ([System.Text.Encoding]::ASCII.GetString($Monitor.ManufacturerName)).Replace("$([char]0x0000)", "")
#Filters out "non monitors". Place any of your own filters here. These two are all-in-one computers with built in displays. I don't need the info from these.
If ($Mon_Model -like "*800 AIO*" -or $Mon_Model -like "*8300 AiO*") { Break }
#Sets a friendly name based on the hash table above. If no entry found sets it to the original 3 character code
$Mon_Manufacturer_Friendly = $ManufacturerHash.$Mon_Manufacturer
If ($Mon_Manufacturer_Friendly -eq $null) {
$Mon_Manufacturer_Friendly = $Mon_Manufacturer
}
#Creates a custom monitor object and fills it with 4 NoteProperty members and the respective data
$Monitor_Obj = [PSCustomObject]@{
Manufacturer = $Mon_Manufacturer_Friendly
Model = $Mon_Model
SerialNumber = $Mon_Serial_Number
AttachedComputer = $Mon_Attached_Computer
}
#Appends the object to the array
$Monitor_Array += $Monitor_Obj
} #End ForEach Monitor
#Outputs the Array
$Monitor_Array
} #End ForEach Computer

View File

@@ -0,0 +1,26 @@
#Identifies Computer RAM capacity and status
[Cmdletbinding()]
Param(
[string]$Computername = "localhost"
)
cls
$PysicalMemory = Get-WmiObject -class "win32_physicalmemory" -namespace "root\CIMV2" -ComputerName $Computername
Write-Host "RAM Modules:" -ForegroundColor Green
$PysicalMemory | Format-Table Tag, BankLabel, @{n = "Capacity(GB)"; e = { $_.Capacity / 1GB } }, Manufacturer, PartNumber, Speed -AutoSize
Write-Host "Total Memory:" -ForegroundColor Green
Write-Host "$((($PysicalMemory).Capacity | Measure-Object -Sum).Sum/1GB)GB"
$TotalSlots = ((Get-WmiObject -Class "win32_PhysicalMemoryArray" -namespace "root\CIMV2" -ComputerName $Computername).MemoryDevices | Measure-Object -Sum).Sum
Write-Host "`nTotal Memory Slots:" -ForegroundColor Green
Write-Host $TotalSlots
$UsedSlots = (($PysicalMemory) | Measure-Object).Count
Write-Host "`nUsed Memory Slots:" -ForegroundColor Green
Write-Host $UsedSlots
If ($UsedSlots -eq $TotalSlots) {
Write-Host "All memory slots are filled up, none is empty!" -ForegroundColor Yellow
}

View File

@@ -0,0 +1,17 @@
$ErrorActionPreference= 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
if (Get-WinEvent -FilterHashtable @{LogName='security';ID='4720','4720','4728','4732','4756','4767';StartTime=$TimeSpan})
{
Write-Output "A change has been made to local users"
Get-WinEvent -FilterHashtable @{LogName='security';ID='4720','4720','4728','4732','4756','4767';StartTime=$TimeSpan}
exit 1
}
else
{
Write-Output "No changes all looks fine"
exit 0
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1,13 @@
@echo off
for /f "tokens=4-5 delims=. " %%i in ('ver') do set VERSION=%%i.%%j
if "%version%" == "6.1" (
rem Windows 7
netsh interface ip set address "Local Area Connection" dhcp
netsh interface ip set dns "Local Area Connection" dhcp
)
if "%version%" == "10.0" (
rem Windows 10
netsh interface ip set address Ethernet dhcp
netsh interface ip set dns Ethernet dhcp
)

View File

@@ -0,0 +1,39 @@
# Cloudflare Family DNS see https://blog.cloudflare.com/introducing-1-1-1-1-for-families/
$ErrorActionPreference = 'SilentlyContinue'
if ((Get-WmiObject -Class Win32_ComputerSystem).PartOfDomain){
write-host "Domain member, we better not update the DNS!!"
exit
}
$PrimaryDNS = '1.1.1.2'
$SecondaryDNS = '1.0.0.2'
$DNSServers = $PrimaryDNS,$SecondaryDNS
$NICs = Get-WMIObject Win32_NetworkAdapterConfiguration | where{$_.IPEnabled -eq "TRUE"}
function get-return-status {
Param ($code)
If ($code -eq 0) {
return "Success."
} elseif ($code -eq 1) {
return "Success, but Restart Required."
} else {
return "Error with Code $($code)!"
}
}
Foreach($NIC in $NICs) {
""
"-------"
"Attempting to modify DNS Servers for the following NIC:"
$NIC
$returnValue = $NIC.SetDNSServerSearchOrder($DNSServers).ReturnValue
$response = get-return-status($returnValue)
Write-Host "Setting DNS Servers to ${$NICs}...$($response)"
$returnValue = $NIC.SetDynamicDNSRegistration("True").ReturnValue
$response = get-return-status($returnValue)
Write-Host "Setting Dynamic DNS Registration to True...$($response)"
}

View File

@@ -0,0 +1,8 @@
#CVE-2020-16898 | Windows TCP/IP Remote Code Execution Vulnerability
#https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-16898
#Disable IPv6 on All Adapers
Disable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6
#Confirm That all NIC's no longer have IPv6 Enabled
(Get-NetAdapterBinding -Name '*' -ComponentID ms_tcpip6).Enabled

View File

@@ -0,0 +1,3 @@
#This script sets current network profile to Private
$net = get-netconnectionprofile;Set-NetConnectionProfile -Name $net.Name -NetworkCategory Private

View File

@@ -0,0 +1 @@
%SYSTEMROOT%\System32\powercfg.exe -H OFF

View File

@@ -0,0 +1,11 @@
# This script will force restart computer. Add command paramter: shutdown to shutdown instead
# Normal restart doesn't install updates before issuing
$param1 = $args[0]
if ($param1 -eq 'shutdown') {
Stop-Computer -ComputerName $env:COMPUTERNAME -Force
}
else {
Restart-Computer -ComputerName $env:COMPUTERNAME -Force
}

View File

@@ -0,0 +1 @@
Clear-RecycleBin -Force

View File

@@ -1,5 +1,5 @@
#Windows Defender Exclusions for Tactical
Add-MpPreference -ExclusionPath "C:\Program Files\Mesh Agent\*"
Add-MpPreference -ExclusionPath "C:\Program Files\TacticalAgent\*"
Add-MpPreference -ExclusionPath "C:\Windows\Temp\trmm\*"
Add-MpPreference -ExclusionPath "C:\Windows\Temp\winagent-v*.exe"
#Windows Defender Exclusions for Tactical
Add-MpPreference -ExclusionPath "C:\Program Files\Mesh Agent\*"
Add-MpPreference -ExclusionPath "C:\Program Files\TacticalAgent\*"
Add-MpPreference -ExclusionPath "C:\Windows\Temp\trmm\*"
Add-MpPreference -ExclusionPath "C:\Windows\Temp\winagent-v*.exe"

View File

@@ -0,0 +1 @@
Remove-Item -Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Tactical RMM Agent.lnk" -Force

View File

@@ -0,0 +1,6 @@
# Add a task to Task Scheduler
$Trigger = New-ScheduledTaskTrigger -At 10:00am Daily # Specify the trigger settings
$User = "NT AUTHORITY\SYSTEM" # Specify the account to run the script
$Action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "YOUR COMMAND HERE" # Specify what program to run and with its parameters
Register-ScheduledTask -TaskName "SomeTaskName" -Trigger $Trigger -User $User -Action $Action -RunLevel Highest Force # Specify the name of the task

View File

@@ -0,0 +1,18 @@
$ErrorActionPreference= 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
if (Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TaskScheduler/Operational';ID='106';StartTime=$TimeSpan} | Where-Object -Property Message -notlike *$env:COMPUTERNAME*)
{
Write-Output "New Task Has Been Added"
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TaskScheduler/Operational';ID='106';StartTime=$TimeSpan}
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TaskScheduler/Operational';ID='141';StartTime=$TimeSpan}
exit 1
}
else
{
Write-Output "No changes with Task Scheduler"
exit 0
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1,22 @@
$ErrorActionPreference = 'silentlycontinue'
$PSDenabled = (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System).PromptOnSecureDesktop
$CPAenabled = (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System).ConsentPromptBehaviorAdmin
if ($PSDenabled -Eq 1 -And $CPAenabled -Eq 5) {
Write-Output "UAC is Enabled"
exit 0
}
elseif ($PSDenabled -Eq 1 -And $CPAenabled -Eq 2) {
Write-Output "UAC is Enabled"
exit 0
}
else {
Write-Output "UAC is Disabled"
exit 1
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1 @@
reg add HKLM\SYSTEM\CurrentControlSet\Services\UsbStor /v "Start" /t REG_DWORD /d "4" /f

View File

@@ -0,0 +1 @@
reg add HKLM\SYSTEM\CurrentControlSet\Services\UsbStor /v "Start" /t REG_DWORD /d "3" /f

View File

@@ -0,0 +1,17 @@
$ErrorActionPreference= 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
if (Get-WinEvent -FilterHashtable @{LogName='Application';ID='1511';StartTime=$TimeSpan})
{
Write-Output "An account has been logged in with a Temporary profile"
Get-WinEvent -FilterHashtable @{LogName='Application';ID='1511';StartTime=$TimeSpan}
exit 1
}
else
{
Write-Output "All looks fine"
exit 0
}
Exit $LASTEXITCODE

View File

@@ -89,7 +89,6 @@ module.exports = function () {
],
config: {
loadingBar: {
color: "red",
size: "4px"
},
notify: {

View File

@@ -303,17 +303,18 @@
/>
</q-td>
<q-td key="checks-status" :props="props">
<q-icon v-if="props.row.maintenance_mode" name="fas fa-check-double" size="1.2em" color="warning">
<q-icon v-if="props.row.maintenance_mode" name="construction" size="1.2em" color="green">
<q-tooltip>Maintenance Mode Enabled</q-tooltip>
</q-icon>
<q-icon
v-else-if="props.row.checks.has_failing_checks"
name="fas fa-check-double"
size="1.2em"
color="negative"
>
<q-icon v-else-if="props.row.checks.failing > 0" name="fas fa-check-double" size="1.2em" color="negative">
<q-tooltip>Checks failing</q-tooltip>
</q-icon>
<q-icon v-else-if="props.row.checks.warning > 0" name="fas fa-check-double" size="1.2em" color="warning">
<q-tooltip>Checks warning</q-tooltip>
</q-icon>
<q-icon v-else-if="props.row.checks.info > 0" name="fas fa-check-double" size="1.2em" color="info">
<q-tooltip>Checks info</q-tooltip>
</q-icon>
<q-icon v-else name="fas fa-check-double" size="1.2em" color="positive">
<q-tooltip>Checks passing</q-tooltip>
</q-icon>
@@ -384,7 +385,7 @@
</q-dialog>
</div>
<!-- send command modal -->
<q-dialog v-model="showSendCommand">
<q-dialog v-model="showSendCommand" persistent>
<SendCommand @close="showSendCommand = false" :pk="selectedAgentPk" />
</q-dialog>
<!-- agent recovery modal -->
@@ -392,7 +393,7 @@
<AgentRecovery @close="showAgentRecovery = false" :pk="selectedAgentPk" />
</q-dialog>
<!-- run script modal -->
<q-dialog v-model="showRunScript">
<q-dialog v-model="showRunScript" persistent>
<RunScript @close="showRunScript = false" :pk="selectedAgentPk" />
</q-dialog>
</div>

View File

@@ -23,6 +23,7 @@
<q-tab name="base_board" label="Motherboard" />
<q-tab name="comp_sys_prod" label="Computer System Product" />
<q-tab name="network_config" label="Network Config" />
<q-tab name="graphics" label="Graphics" />
<q-tab name="desktop_monitor" label="Monitors" />
<q-tab name="network_adapter" label="Network Adapters" />
</q-tabs>
@@ -63,6 +64,9 @@
<q-tab-panel name="desktop_monitor">
<WmiDetail :info="desktop_monitor" />
</q-tab-panel>
<q-tab-panel name="graphics">
<WmiDetail :info="graphics" />
</q-tab-panel>
<q-tab-panel name="network_adapter">
<WmiDetail :info="network_adapter" />
</q-tab-panel>
@@ -125,6 +129,9 @@ export default {
network_adapter() {
return this.assets.network_adapter;
},
graphics() {
return this.assets.graphics;
},
},
};
</script>

View File

@@ -18,6 +18,7 @@
<template v-else>
<q-table
dense
:table-class="{ 'table-bgcolor': !$q.dark.isActive, 'table-bgcolor-dark': $q.dark.isActive }"
class="tabs-tbl-sticky"
:style="{ 'max-height': tabsTableHeight }"
:data="tasks"

View File

@@ -124,6 +124,13 @@
<q-item-section>Delete</q-item-section>
</q-item>
<q-separator></q-separator>
<q-item clickable v-close-popup @click="resetCheck(props.row.id)">
<q-item-section side>
<q-icon name="info" />
</q-item-section>
<q-item-section>Reset Check Status</q-item-section>
</q-item>
<q-separator></q-separator>
<q-item clickable v-close-popup>
<q-item-section>Close</q-item-section>
</q-item>
@@ -431,7 +438,25 @@ export default {
});
});
},
resetCheck(check) {
const data = {
check_reset: true,
status: "passing",
};
axios
.patch(`/checks/${check}/check/`, data)
.then(r => {
this.$emit("refreshEdit");
this.$store.dispatch("loadChecks", this.selectedAgentPk);
this.notifySuccess("The check was reset");
})
.catch(e => {
this.notifyError("There was an issue resetting the check");
});
},
onRefresh(id) {
this.$emit("refreshEdit");
this.$store.dispatch("loadChecks", id);
this.$store.dispatch("loadAutomatedTasks", id);
},

View File

@@ -89,6 +89,10 @@
<q-item clickable v-close-popup @click="showEditCoreSettingsModal = true">
<q-item-section>Global Settings</q-item-section>
</q-item>
<!-- code sign -->
<q-item v-if="!noCodeSigning" clickable v-close-popup @click="showCodeSign = true">
<q-item-section>Code Signing</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
@@ -179,6 +183,10 @@
<q-dialog v-model="showServerMaintenance">
<ServerMaintenance @close="showMaintenance = false" />
</q-dialog>
<!-- Code Sign -->
<q-dialog v-model="showCodeSign">
<CodeSign @close="showCodeSign = false" />
</q-dialog>
</q-bar>
</div>
</template>
@@ -201,6 +209,7 @@ import AuditManager from "@/components/AuditManager";
import BulkAction from "@/components/modals/agents/BulkAction";
import Deployment from "@/components/Deployment";
import ServerMaintenance from "@/components/modals/core/ServerMaintenance";
import CodeSign from "@/components/modals/coresettings/CodeSign";
export default {
name: "FileBar",
@@ -217,6 +226,7 @@ export default {
BulkAction,
Deployment,
ServerMaintenance,
CodeSign,
},
data() {
return {
@@ -233,8 +243,14 @@ export default {
showDeployment: false,
showDebugLog: false,
showScriptManager: false,
showCodeSign: false,
};
},
computed: {
noCodeSigning() {
return this.$store.state.noCodeSign;
},
},
methods: {
showBulkActionModal(mode) {
this.bulkMode = mode;

View File

@@ -283,10 +283,18 @@
<q-tooltip> Batch </q-tooltip>
</q-icon>
</q-td>
<!-- name -->
<q-td>{{ props.row.name }}</q-td>
<!-- args -->
<q-td>
<span v-if="props.row.args.length > 0">{{ props.row.args }}</span>
<span v-if="props.row.args.length > 0">
{{ truncateText(props.row.args.toString()) }}
<q-tooltip v-if="props.row.args.toString().length >= 60" content-style="font-size: 12px">
{{ props.row.args }}
</q-tooltip>
</span>
</q-td>
<q-td>{{ props.row.category }}</q-td>
<q-td>
{{ truncateText(props.row.description) }}
@@ -363,6 +371,7 @@ export default {
label: "Default Args",
field: "args",
align: "left",
sortable: true,
},
{
name: "category",
@@ -516,11 +525,9 @@ export default {
},
categories() {
let list = [];
this.scripts.forEach(script => {
this.visibleScripts.forEach(script => {
if (!!script.category && !list.includes(script.category)) {
if (script.category !== "Community") {
list.push(script.category);
}
list.push(script.category);
}
});
return list;
@@ -538,8 +545,7 @@ export default {
let scriptsTemp = Object.assign([], this.visibleScripts);
let categoriesTemp = Object.assign([], this.categories);
// add Community and Unassigned values and categories array
if (this.showCommunityScripts) categoriesTemp.push("Community");
// add Unassigned category
categoriesTemp.push("Unassigned");
const sorted = categoriesTemp.sort();

View File

@@ -25,7 +25,7 @@
<SummaryTab />
</q-tab-panel>
<q-tab-panel name="checks" class="q-pb-xs q-pt-none">
<ChecksTab />
<ChecksTab @refreshEdit="$emit('refreshEdit')" />
</q-tab-panel>
<q-tab-panel name="tasks" class="q-pb-xs q-pt-none">
<AutomatedTasksTab />

View File

@@ -4,7 +4,7 @@
<q-btn class="q-mr-sm" dense flat push icon="refresh" @click="refreshSummary" />
<span>
<b>{{ summary.hostname }}</b>
<span v-if="summary.maintenance_mode"> &bull; <q-badge color="warning"> Maintenance Mode </q-badge> </span>
<span v-if="summary.maintenance_mode"> &bull; <q-badge color="green"> Maintenance Mode </q-badge> </span>
&bull; {{ summary.operating_system }} &bull; Agent v{{ summary.version }}
</span>
<hr />
@@ -39,6 +39,13 @@
</q-item-section>
<q-item-section>{{ disk }}</q-item-section>
</q-item>
<!-- graphics -->
<q-item>
<q-item-section avatar>
<q-icon name="fas fa-tv" />
</q-item-section>
<q-item-section>{{ summary.graphics }}</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-icon name="fas fa-globe-americas" />
@@ -65,7 +72,24 @@
<q-avatar size="lg" square icon="cancel" color="red" text-color="white" />
<small>{{ summary.checks.failing }} checks failing</small>
</q-chip>
<span v-if="awaitingSync(summary.checks.total, summary.checks.passing, summary.checks.failing)"
<q-chip v-if="summary.checks.warning" square size="lg">
<q-avatar size="lg" square icon="warning" color="warning" text-color="white" />
<small>{{ summary.checks.warning }} checks warning</small>
</q-chip>
<q-chip v-if="summary.checks.info" square size="lg">
<q-avatar size="lg" square icon="info" color="info" text-color="white" />
<small>{{ summary.checks.info }} checks info</small>
</q-chip>
<span
v-if="
awaitingSync(
summary.checks.total,
summary.checks.passing,
summary.checks.failing,
summary.checks.warning,
summary.checks.info
)
"
>{{ summary.checks.total }} checks awaiting first synchronization</span
>
</template>
@@ -98,8 +122,8 @@ export default {
return {};
},
methods: {
awaitingSync(total, passing, failing) {
return total !== 0 && passing === 0 && failing === 0 ? true : false;
awaitingSync(total, passing, failing, warning, info) {
return total !== 0 && passing === 0 && failing === 0 && warning === 0 && info === 0;
},
refreshSummary() {
this.$q.loading.show();

View File

@@ -47,7 +47,11 @@
<q-card-section>
<div class="q-gutter-sm">
<q-checkbox v-model="rdp" dense label="Enable RDP" />
<q-checkbox v-model="ping" dense label="Enable Ping" />
<q-checkbox v-model="ping" dense label="Enable Ping">
<q-tooltip>
Enable ICMP echo requests in the local firewall
</q-tooltip>
</q-checkbox>
<q-checkbox v-model="power" dense v-show="agenttype === 'workstation'" label="Disable sleep/hibernate" />
</div>
</q-card-section>

View File

@@ -93,7 +93,7 @@
<div class="col-3">Day of month to run:</div>
<div class="col-4"></div>
<q-select
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-show="winupdatepolicy.run_time_frequency !== 'inherit'"
dense
class="col-5"
outlined
@@ -103,11 +103,10 @@
map-options
/>
</q-card-section>
<q-card-section class="row">
<q-card-section class="row" v-show="winupdatepolicy.run_time_frequency !== 'inherit'">
<div class="col-3">Scheduled Time:</div>
<div class="col-4"></div>
<q-select
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
dense
class="col-5"
outlined
@@ -118,51 +117,17 @@
/>
</q-card-section>
<q-card-section
v-if="winupdatepolicy.run_time_frequency === 'daily' || winupdatepolicy.run_time_frequency === 'inherit'"
v-if="winupdatepolicy.run_time_frequency === 'daily'"
v-show="winupdatepolicy.run_time_frequency !== 'inherit'"
>
<div class="q-gutter-sm">
<q-checkbox
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-model="winupdatepolicy.run_time_days"
:val="1"
label="Monday"
/>
<q-checkbox
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-model="winupdatepolicy.run_time_days"
:val="2"
label="Tuesday"
/>
<q-checkbox
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-model="winupdatepolicy.run_time_days"
:val="3"
label="Wednesday"
/>
<q-checkbox
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-model="winupdatepolicy.run_time_days"
:val="4"
label="Thursday"
/>
<q-checkbox
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-model="winupdatepolicy.run_time_days"
:val="5"
label="Friday"
/>
<q-checkbox
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-model="winupdatepolicy.run_time_days"
:val="6"
label="Saturday"
/>
<q-checkbox
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-model="winupdatepolicy.run_time_days"
:val="0"
label="Sunday"
/>
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="1" label="Monday" />
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="2" label="Tuesday" />
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="3" label="Wednesday" />
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="4" label="Thursday" />
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="5" label="Friday" />
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="6" label="Saturday" />
<q-checkbox v-model="winupdatepolicy.run_time_days" :val="0" label="Sunday" />
</div>
</q-card-section>
<!-- Reboot After Installation -->
@@ -189,20 +154,15 @@
<q-checkbox v-model="winupdatepolicy.reprocess_failed_inherit" label="Inherit failed patch settings" />
</div>
</q-card-section>
<q-card-section class="row">
<q-card-section class="row" v-show="!winupdatepolicy.reprocess_failed_inherit">
<div class="col-5">
<q-checkbox
:disable="winupdatepolicy.reprocess_failed_inherit"
v-model="winupdatepolicy.reprocess_failed"
label="Reprocess failed patches"
/>
<q-checkbox v-model="winupdatepolicy.reprocess_failed" label="Reprocess failed patches" />
</div>
<div class="col-3">
<q-input
dense
v-model.number="winupdatepolicy.reprocess_failed_times"
:disable="winupdatepolicy.reprocess_failed_inherit"
type="number"
filled
label="Times"
@@ -210,11 +170,7 @@
/>
</div>
<div class="col-3"></div>
<q-checkbox
v-model="winupdatepolicy.email_if_fail"
:disable="winupdatepolicy.reprocess_failed_inherit"
label="Send an email when patch installation fails"
/>
<q-checkbox v-model="winupdatepolicy.email_if_fail" label="Send an email when patch installation fails" />
</q-card-section>
<q-card-actions align="left" v-if="policy">
<q-btn label="Submit" color="primary" @click="submit" />
@@ -249,6 +205,7 @@ export default {
run_time_hour: 3,
run_time_frequency: "daily",
run_time_days: [],
run_time_day: 1,
reboot_after_install: "never",
reprocess_failed_inherit: false,
reprocess_failed: false,

View File

@@ -55,6 +55,7 @@ export default {
return {
siteOptions: [],
selectedSite: null,
agentCount: 0,
};
},
methods: {
@@ -112,6 +113,7 @@ export default {
},
getSites() {
this.$axios.get("/clients/clients/").then(r => {
this.agentCount = this.getAgentCount(r.data, this.type, this.object.id);
r.data.forEach(client => {
// remove client that is being deleted from options
if (this.type === "client") {

View File

@@ -0,0 +1,67 @@
<template>
<q-card style="min-width: 85vh">
<q-card-section class="row items-center">
<div class="text-h6">Code Signing</div>
<q-space />
<q-btn icon="close" flat round dense v-close-popup />
</q-card-section>
<q-form @submit.prevent="editToken">
<q-card-section class="row">
<div class="col-2">Token:</div>
<div class="col-1"></div>
<q-input
outlined
dense
v-model="settings.token"
class="col-9 q-pa-none"
:rules="[val => !!val || 'Token is required']"
/>
</q-card-section>
<q-card-section class="row items-center">
<q-btn label="Save" color="primary" type="submit" />
</q-card-section>
</q-form>
</q-card>
</template>
<script>
import mixins from "@/mixins/mixins";
export default {
name: "CodeSign",
mixins: [mixins],
data() {
return {
settings: {
token: "",
},
};
},
methods: {
getToken() {
this.$axios
.get("/core/codesign/")
.then(r => {
this.settings = r.data;
})
.catch(e => this.notifyError(e.response.data));
},
editToken() {
this.$q.loading.show();
this.$axios
.patch("/core/codesign/", this.settings)
.then(r => {
this.$q.loading.hide();
this.notifySuccess(r.data);
this.$emit("close");
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data, 4000);
});
},
},
created() {
this.getToken();
},
};
</script>

View File

@@ -46,6 +46,18 @@
class="col-4"
/>
</q-card-section>
<q-card-section class="row">
<div class="col-4">Loading Bar Color:</div>
<div class="col-4"></div>
<q-select
outlined
dense
options-dense
v-model="loading_bar_color"
:options="loadingBarColors"
class="col-4"
/>
</q-card-section>
<q-card-section class="row">
<div class="col-2">Client Sort:</div>
<div class="col-2"></div>
@@ -73,6 +85,7 @@
</template>
<script>
import { loadingBarColors } from "@/mixins/data";
import mixins from "@/mixins/mixins";
export default {
@@ -80,11 +93,13 @@ export default {
mixins: [mixins],
data() {
return {
loadingBarColors,
agentDblClickAction: "",
defaultAgentTblTab: "",
clientTreeSort: "",
tab: "ui",
splitterModel: 20,
loading_bar_color: "",
clientTreeSortOptions: [
{
label: "Sort alphabetically, moving failing clients to the top",
@@ -131,6 +146,7 @@ export default {
this.agentDblClickAction = r.data.dbl_click_action;
this.defaultAgentTblTab = r.data.default_agent_tbl_tab;
this.clientTreeSort = r.data.client_tree_sort;
this.loading_bar_color = r.data.loading_bar_color;
});
},
editUserPrefs() {
@@ -138,6 +154,7 @@ export default {
agent_dblclick_action: this.agentDblClickAction,
default_agent_tbl_tab: this.defaultAgentTblTab,
client_tree_sort: this.clientTreeSort,
loading_bar_color: this.loading_bar_color,
};
this.$axios.patch("/accounts/users/ui/", data).then(r => {
this.notifySuccess("Preferences were saved!");

View File

@@ -20,21 +20,6 @@
label="Select script"
map-options
emit-value
/>
</q-card-section>
<q-card-section>
<q-select
dense
label="Script Arguments (press Enter after typing each argument)"
filled
v-model="localTask.script_args"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
@input="setScriptDefaults"
>
<template v-slot:option="scope">
<q-item v-if="!scope.opt.category" v-bind="scope.itemProps" v-on="scope.itemEvents" class="q-pl-lg">
@@ -48,6 +33,21 @@
</template>
</q-select>
</q-card-section>
<q-card-section>
<q-select
dense
label="Script Arguments (press Enter after typing each argument)"
filled
v-model="localTask.script_args"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
@input="setScriptDefaults"
/>
</q-card-section>
<q-card-section>
<q-input
:rules="[val => !!val || '*Required']"
@@ -55,6 +55,7 @@
dense
v-model="localTask.name"
label="Descriptive name of task"
class="q-pb-none"
/>
</q-card-section>
<q-card-section>
@@ -77,6 +78,7 @@
v-model.number="localTask.timeout"
type="number"
label="Maximum permitted execution time (seconds)"
class="q-pb-none"
/>
</q-card-section>
<q-card-actions align="right">

View File

@@ -59,4 +59,6 @@ const monthDays = [
"31"
]
export { scheduledTimes, monthDays };
const loadingBarColors = ["red", "pink", "purple", "deep-purple", "indigo", "blue", "light-blue", "cyan", "teal", "green", "light-green", "lime", "yellow", "amber", "orange", "deep-orange", "brown", "grey", "blue-grey"]
export { scheduledTimes, monthDays, loadingBarColors };

View File

@@ -147,6 +147,19 @@ export default {
this.notifyError("There was an issue getting Custom Fields");
});
},
getAgentCount(data, type, id) {
if (type === "client") {
return data.find(i => id === i.id).agent_count
} else {
const sites = data.map(i => i.sites)
for (let i of sites) {
for (let k of i) {
if (k.id === id) return k.agent_count;
}
}
return 0;
}
},
formatCustomFields(fields, values) {
let tempArray = [];

View File

@@ -33,8 +33,13 @@ export default function () {
agentDblClickAction: "",
defaultAgentTblTab: "server",
clientTreeSort: "alphafail",
clientTreeSplitter: 11,
noCodeSign: false,
},
getters: {
clientTreeSplitterModel(state) {
return state.clientTreeSplitter;
},
loggedIn(state) {
return state.token !== null;
},
@@ -132,6 +137,9 @@ export default function () {
agentHeight <= 15.0 ? state.tableHeight = "15vh" : state.tableHeight = `${agentHeight}vh`;
tabsHeight <= 15.0 ? state.tabHeight = "15vh" : state.tabHeight = `${tabsHeight}vh`;
},
SET_CLIENT_SPLITTER(state, val) {
state.clientTreeSplitter = val;
},
SET_NOTES(state, notes) {
state.notes = notes;
},
@@ -146,9 +154,17 @@ export default function () {
},
SET_CLIENT_TREE_SORT(state, val) {
state.clientTreeSort = val
},
SET_NO_CODE_SIGN(state, val) {
state.noCodeSign = val
}
},
actions: {
setClientTreeSplitter(context, val) {
axios.patch("/accounts/users/ui/", { client_tree_splitter: Math.trunc(val) }).then(r => {
context.commit("SET_CLIENT_SPLITTER", val)
})
},
setShowCommunityScripts(context, data) {
axios.patch("/accounts/users/ui/", { show_community_scripts: data }).then(r => {
context.commit("setShowCommunityScripts", data)
@@ -244,8 +260,9 @@ export default function () {
alert_template: site.alert_template
}
if (site.maintenance_mode) { siteNode["color"] = "warning" }
else if (site.failing_checks) { siteNode["color"] = "negative" }
if (site.maintenance_mode) { siteNode["color"] = "green" }
else if (site.failing_checks.error) { siteNode["color"] = "negative" }
else if (site.failing_checks.warning) { siteNode["color"] = "warning" }
childSites.push(siteNode);
}
@@ -262,8 +279,9 @@ export default function () {
children: childSites
}
if (client.maintenance_mode) clientNode["color"] = "warning"
else if (client.failing_checks) clientNode["color"] = "negative"
if (client.maintenance_mode) clientNode["color"] = "green"
else if (client.failing_checks.error) { clientNode["color"] = "negative" }
else if (client.failing_checks.warning) { clientNode["color"] = "warning" }
output.push(clientNode);
}
@@ -271,7 +289,9 @@ export default function () {
if (state.clientTreeSort === "alphafail") {
// move failing clients to the top
const sortedByFailing = output.sort(a => a.color === "negative" ? -1 : 1);
const failing = output.filter(i => i.color === "negative" || i.color === "warning");
const ok = output.filter(i => i.color !== "negative" && i.color !== "warning");
const sortedByFailing = [...failing, ...ok];
commit("loadTree", sortedByFailing);
} else {
commit("loadTree", output);

View File

@@ -90,7 +90,7 @@
<q-page-container>
<FileBar />
<q-splitter v-model="outsideModel">
<q-splitter v-model="clientTreeSplitter">
<template v-slot:before>
<div v-if="!treeReady" class="q-pa-sm q-gutter-sm text-center" style="height: 30vh">
<q-spinner size="40px" color="primary" />
@@ -343,7 +343,7 @@
<q-avatar color="primary" text-color="white" size="30px" icon="drag_indicator" />
</template>
<template v-slot:after>
<SubTableTabs />
<SubTableTabs @refreshEdit="refreshEntireSite" />
</template>
</q-splitter>
</template>
@@ -397,7 +397,6 @@ export default {
serverOfflineCount: 0,
workstationCount: 0,
workstationOfflineCount: 0,
outsideModel: 11,
selectedTree: "",
innerModel: 50,
clientActive: "",
@@ -712,24 +711,27 @@ export default {
getDashInfo(edited = true) {
this.$store.dispatch("getDashInfo").then(r => {
if (edited) {
this.$q.loadingBar.setDefaults({ color: r.data.loading_bar_color });
this.$store.commit("SET_DEFAULT_AGENT_TBL_TAB", r.data.default_agent_tbl_tab);
this.$store.commit("SET_CLIENT_TREE_SORT", r.data.client_tree_sort);
this.$store.commit("SET_CLIENT_SPLITTER", r.data.client_tree_splitter);
}
this.darkMode = r.data.dark_mode;
this.$q.dark.set(this.darkMode);
this.currentTRMMVersion = r.data.trmm_version;
this.$store.commit("SET_AGENT_DBLCLICK_ACTION", r.data.dbl_click_action);
this.$store.commit("setShowCommunityScripts", r.data.show_community_scripts);
this.$store.commit("SET_NO_CODE_SIGN", r.data.no_code_sign);
});
},
showToggleMaintenance(node) {
let data = {
id: node.id,
type: node.raw.split("|")[0],
action: node.color === "warning" ? false : true,
action: node.color === "green" ? false : true,
};
const text = node.color === "warning" ? "Maintenance mode was disabled" : "Maintenance mode was enabled";
const text = node.color === "green" ? "Maintenance mode was disabled" : "Maintenance mode was enabled";
this.$store
.dispatch("toggleMaintenanceMode", data)
.then(response => {
@@ -741,7 +743,7 @@ export default {
});
},
menuMaintenanceText(node) {
return node.color === "warning" ? "Disable Maintenance Mode" : "Enable Maintenance Mode";
return node.color === "green" ? "Disable Maintenance Mode" : "Enable Maintenance Mode";
},
clearFilter() {
this.filterTextLength = 0;
@@ -802,13 +804,21 @@ export default {
treeReady: state => state.treeReady,
clients: state => state.clients,
}),
...mapGetters(["selectedAgentPk", "needRefresh"]),
...mapGetters(["selectedAgentPk", "needRefresh", "clientTreeSplitterModel"]),
wsUrl() {
return getBaseUrl().split("://")[1];
},
token() {
return this.$store.state.token;
},
clientTreeSplitter: {
get: function () {
return this.clientTreeSplitterModel;
},
set: function (newVal) {
this.$store.dispatch("setClientTreeSplitter", newVal);
},
},
tab: {
get: function () {
return this.$store.state.defaultAgentTblTab;

View File

@@ -72,10 +72,11 @@ export default {
this.title = `${r.data.hostname} - ${r.data.client} - ${r.data.site} | Remote Background`;
});
},
getDark() {
getUI() {
this.$store.dispatch("getDashInfo").then(r => {
this.darkMode = r.data.dark_mode;
this.$q.dark.set(this.darkMode);
this.$q.loadingBar.setDefaults({ color: r.data.loading_bar_color });
});
},
},
@@ -90,7 +91,7 @@ export default {
},
},
created() {
this.getDark();
this.getUI();
this.genURLS();
},
};

View File

@@ -54,6 +54,12 @@ export default {
};
},
methods: {
getUI() {
this.$store.dispatch("getDashInfo").then(r => {
this.$q.dark.set(r.data.dark_mode);
this.$q.loadingBar.setDefaults({ color: r.data.loading_bar_color });
});
},
genURL() {
this.$q.loading.show();
this.visible = false;
@@ -117,6 +123,7 @@ export default {
},
},
created() {
this.getUI();
this.genURL();
},
};