Compare commits

...

52 Commits

Author SHA1 Message Date
wh1te909
715982e40a Release 0.6.14 2021-06-11 04:41:48 +00:00
wh1te909
d00cd4453a bump versions 2021-06-11 04:40:57 +00:00
wh1te909
429c08c24a fix width on q-file caused by recent quasar update 2021-06-11 03:58:57 +00:00
wh1te909
6a71490e20 update reqs 2021-06-11 02:40:22 +00:00
Dan
9bceda0646 Merge pull request #562 from diekinderwelt/nginx_enable_ipv6
enable ipv6 in nginx config
2021-06-10 18:59:34 -07:00
Dan
a1027a6773 Merge pull request #565 from silversword411/develop
Docs Update - adding design and tipsntricks
2021-06-10 18:59:12 -07:00
silversword411
302d4b75f9 formatting fix 2021-06-08 15:39:43 -04:00
silversword411
5f6ee0e883 Docs Update - adding design and tipsntricks 2021-06-08 14:45:02 -04:00
Silvio
27f9720de1 enable ipv6 in nginx config
Signed-off-by: Silvio <silvio.zimmer@die-kinderwelt.com>
2021-06-08 11:43:55 +02:00
sadnub
22aa3fdbbc fix bug with policy copy and task that triggers on check failure. Fix check history tests 2021-06-06 23:19:07 -04:00
sadnub
069ecdd33f apply redis configuration after restore 2021-06-06 22:58:32 -04:00
sadnub
dd545ae933 catch an exception that a celery task could potentially throw and configure automation task retries 2021-06-06 22:55:47 -04:00
sadnub
6650b705c4 configure redis to use an appendonly file for celery task reliability 2021-06-06 22:54:52 -04:00
sadnub
59b0350289 fix duplicate tasks when there is an assigned check 2021-06-06 22:54:06 -04:00
sadnub
1ad159f820 remove foreign key from checkhistory to make mass check deletes reliable. (This will not migrate check history data) 2021-06-06 22:53:11 -04:00
Dan
0bf42190e9 Merge pull request #544 from bbrendon/patch-1
check for proper OS support
2021-05-30 23:10:21 -07:00
bbrendon
d2fa836232 check for proper OS support 2021-05-30 10:39:08 -07:00
Dan
c387774093 Merge pull request #543 from bbrendon/develop
fixed an edge case and warning notes
2021-05-29 22:39:52 -07:00
bbrendon
e99736ba3c fixed an edge case and warning notes 2021-05-29 19:25:53 -07:00
wh1te909
16cb54fcc9 fix multiline output not working for automation task 2021-05-29 18:47:09 +00:00
wh1te909
5aa15c51ec Release 0.6.13 2021-05-29 07:35:29 +00:00
wh1te909
a8aedd9cf3 bump version 2021-05-29 07:35:10 +00:00
wh1te909
b851b632bc fix agent_outages_task async error 2021-05-29 07:26:10 +00:00
wh1te909
541e07fb65 Release 0.6.12 2021-05-29 05:16:37 +00:00
wh1te909
6ad16a897d bump versions 2021-05-29 05:15:26 +00:00
wh1te909
72f1053a93 change interval 2021-05-29 04:49:17 +00:00
sadnub
fb15a2762c allow saving multiple script output in custom fields #533 2021-05-28 23:52:23 -04:00
wh1te909
9165248b91 update go/codec 2021-05-29 03:20:12 +00:00
sadnub
add18b29db fix agent dropdown 2021-05-28 22:59:44 -04:00
wh1te909
1971653548 bump nats/mesh 2021-05-29 02:53:16 +00:00
wh1te909
392cd64d7b hide settings in hosted 2021-05-29 02:20:07 +00:00
wh1te909
b5affbb7c8 change function name 2021-05-29 02:18:57 +00:00
wh1te909
71d1206277 more checks rework 2021-05-29 01:37:20 +00:00
wh1te909
26e6a8c409 update reqs 2021-05-28 18:12:32 +00:00
wh1te909
eb54fae11a more checks rework 2021-05-28 17:54:57 +00:00
wh1te909
ee773e5966 remove deprecated func 2021-05-28 17:54:14 +00:00
wh1te909
7218ccdba8 start checks rework 2021-05-27 07:16:06 +00:00
wh1te909
332400e48a autogrow text field fixes #533 2021-05-27 07:09:40 +00:00
Dan
ad1a5d3702 Merge pull request #534 from silversword411/develop
Script library and docs tweaks
2021-05-26 23:59:08 -07:00
silversword411
3006b4184d Docs update on regular patching 2021-05-26 21:36:28 -04:00
silversword411
84eb84a080 Script library adding comments 2021-05-26 10:19:30 -04:00
sadnub
60beea548b Allow clearing resolved/failure actions in alert template 2021-05-24 22:18:12 -04:00
Dan
5f9c149e59 Merge pull request #528 from bbrendon/develop
updated timeouts and fixed one script
2021-05-21 18:36:07 -07:00
bbrendon
53367c6f04 update timeouts on some scripts 2021-05-21 18:01:16 -07:00
bbrendon
d7f817ee44 syntax error fix. 2021-05-21 17:56:53 -07:00
Dan
d33a87da54 Merge pull request #526 from silversword411/develop
script library - Screenconnect collector
2021-05-20 20:13:51 -07:00
silversword411
3aebfb12b7 Merge branch 'develop' of https://github.com/silversword411/tacticalrmm into develop 2021-05-20 21:50:10 -04:00
silversword411
1d6c55ffa6 Script library - screenconnect collector 2021-05-20 21:49:01 -04:00
Dan
5e7080aac3 Merge pull request #522 from silversword411/develop
Docs Example and wip tweaks
2021-05-20 18:37:33 -07:00
silversword411
fad739bc01 Updating script delegated folders 2021-05-20 10:10:59 -04:00
silversword411
c6b7f23884 Adding URL Action Example to docs 2021-05-19 02:46:51 -04:00
silversword411
a6f7e446de tweaking wip scripts 2021-05-18 23:22:45 -04:00
71 changed files with 1408 additions and 1490 deletions

View File

@@ -115,7 +115,10 @@ services:
redis-dev: redis-dev:
container_name: trmm-redis-dev container_name: trmm-redis-dev
restart: always restart: always
command: redis-server --appendonly yes
image: redis:6.0-alpine image: redis:6.0-alpine
volumes:
- redis-data-dev:/data
networks: networks:
dev: dev:
aliases: aliases:
@@ -247,6 +250,7 @@ volumes:
postgres-data-dev: postgres-data-dev:
mongo-dev-data: mongo-dev-data:
mesh-data-dev: mesh-data-dev:
redis-data-dev:
networks: networks:
dev: dev:

View File

@@ -211,6 +211,7 @@ def agent_outages_task() -> None:
agents = Agent.objects.only( agents = Agent.objects.only(
"pk", "pk",
"agent_id",
"last_seen", "last_seen",
"offline_time", "offline_time",
"overdue_time", "overdue_time",

View File

@@ -321,11 +321,16 @@ class CheckRunner(APIView):
def patch(self, request): def patch(self, request):
check = get_object_or_404(Check, pk=request.data["id"]) check = get_object_or_404(Check, pk=request.data["id"])
if pyver.parse(check.agent.version) < pyver.parse("1.5.7"):
return notify_error("unsupported")
check.last_run = djangotime.now() check.last_run = djangotime.now()
check.save(update_fields=["last_run"]) check.save(update_fields=["last_run"])
status = check.handle_checkv2(request.data) status = check.handle_check(request.data)
if status == "failing" and check.assignedtask.exists(): # type: ignore
check.handle_assigned_task()
return Response(status) return Response("ok")
class CheckRunnerInterval(APIView): class CheckRunnerInterval(APIView):
@@ -378,9 +383,18 @@ class TaskRunner(APIView):
) )
# get last line of stdout # get last line of stdout
value = new_task.stdout.split("\n")[-1].strip() value = (
new_task.stdout
if task.collector_all_output
else new_task.stdout.split("\n")[-1].strip()
)
if task.custom_field.type in ["text", "number", "single", "datetime"]: if task.custom_field.type in [
"text",
"number",
"single",
"datetime",
]:
agent_field.string_value = value agent_field.string_value = value
agent_field.save() agent_field.save()
elif task.custom_field.type == "multiple": elif task.custom_field.type == "multiple":

View File

@@ -3,7 +3,7 @@ from typing import Any, Dict, List, Union
from tacticalrmm.celery import app from tacticalrmm.celery import app
@app.task @app.task(retry_backoff=5, retry_jitter=True, retry_kwargs={"max_retries": 5})
def generate_agent_checks_task( def generate_agent_checks_task(
policy: int = None, policy: int = None,
site: int = None, site: int = None,
@@ -57,7 +57,9 @@ def generate_agent_checks_task(
return "ok" return "ok"
@app.task @app.task(
acks_late=True, retry_backoff=5, retry_jitter=True, retry_kwargs={"max_retries": 5}
)
# updates policy managed check fields on agents # updates policy managed check fields on agents
def update_policy_check_fields_task(check: int) -> str: def update_policy_check_fields_task(check: int) -> str:
from checks.models import Check from checks.models import Check
@@ -73,7 +75,7 @@ def update_policy_check_fields_task(check: int) -> str:
return "ok" return "ok"
@app.task @app.task(retry_backoff=5, retry_jitter=True, retry_kwargs={"max_retries": 5})
# generates policy tasks on agents affected by a policy # generates policy tasks on agents affected by a policy
def generate_agent_autotasks_task(policy: int = None) -> str: def generate_agent_autotasks_task(policy: int = None) -> str:
from agents.models import Agent from agents.models import Agent
@@ -100,7 +102,12 @@ def generate_agent_autotasks_task(policy: int = None) -> str:
return "ok" return "ok"
@app.task @app.task(
acks_late=True,
retry_backoff=5,
retry_jitter=True,
retry_kwargs={"max_retries": 5},
)
def delete_policy_autotasks_task(task: int) -> str: def delete_policy_autotasks_task(task: int) -> str:
from autotasks.models import AutomatedTask from autotasks.models import AutomatedTask
@@ -120,7 +127,12 @@ def run_win_policy_autotasks_task(task: int) -> str:
return "ok" return "ok"
@app.task @app.task(
acks_late=True,
retry_backoff=5,
retry_jitter=True,
retry_kwargs={"max_retries": 5},
)
def update_policy_autotasks_fields_task(task: int, update_agent: bool = False) -> str: def update_policy_autotasks_fields_task(task: int, update_agent: bool = False) -> str:
from autotasks.models import AutomatedTask from autotasks.models import AutomatedTask

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.2.1 on 2021-05-29 03:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('autotasks', '0021_alter_automatedtask_custom_field'),
]
operations = [
migrations.AddField(
model_name='automatedtask',
name='collector_all_output',
field=models.BooleanField(default=False),
),
]

View File

@@ -10,6 +10,7 @@ from django.conf import settings
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
from django.db import models from django.db import models
from django.db.models.fields import DateTimeField from django.db.models.fields import DateTimeField
from django.db.utils import DatabaseError
from django.utils import timezone as djangotime from django.utils import timezone as djangotime
from logs.models import BaseAuditModel from logs.models import BaseAuditModel
from loguru import logger from loguru import logger
@@ -104,6 +105,7 @@ class AutomatedTask(BaseAuditModel):
task_type = models.CharField( task_type = models.CharField(
max_length=100, choices=TASK_TYPE_CHOICES, default="manual" max_length=100, choices=TASK_TYPE_CHOICES, default="manual"
) )
collector_all_output = models.BooleanField(default=False)
run_time_date = DateTimeField(null=True, blank=True) run_time_date = DateTimeField(null=True, blank=True)
remove_if_not_scheduled = models.BooleanField(default=False) remove_if_not_scheduled = models.BooleanField(default=False)
run_asap_after_missed = models.BooleanField(default=False) # added in agent v1.4.7 run_asap_after_missed = models.BooleanField(default=False) # added in agent v1.4.7
@@ -182,6 +184,7 @@ class AutomatedTask(BaseAuditModel):
"remove_if_not_scheduled", "remove_if_not_scheduled",
"run_asap_after_missed", "run_asap_after_missed",
"custom_field", "custom_field",
"collector_all_output",
] ]
@staticmethod @staticmethod
@@ -363,9 +366,14 @@ class AutomatedTask(BaseAuditModel):
if r != "ok" and "The system cannot find the file specified" not in r: if r != "ok" and "The system cannot find the file specified" not in r:
self.sync_status = "pendingdeletion" self.sync_status = "pendingdeletion"
self.save(update_fields=["sync_status"])
try:
self.save(update_fields=["sync_status"])
except DatabaseError:
pass
logger.warning( logger.warning(
f"{agent.hostname} task {self.name} was successfully modified" f"{agent.hostname} task {self.name} will be deleted on next checkin"
) )
return "timeout" return "timeout"
else: else:

View File

@@ -0,0 +1,22 @@
# Generated by Django 3.2.1 on 2021-06-06 16:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('checks', '0023_check_run_interval'),
]
operations = [
migrations.RemoveField(
model_name='checkhistory',
name='check_history',
),
migrations.AddField(
model_name='checkhistory',
name='check_id',
field=models.PositiveIntegerField(default=0),
),
]

View File

@@ -1,4 +1,3 @@
import asyncio
import json import json
import os import os
import string import string
@@ -14,9 +13,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from logs.models import BaseAuditModel from logs.models import BaseAuditModel
from loguru import logger from loguru import logger
from packaging import version as pyver
from .utils import bytes2human
logger.configure(**settings.LOG_CONFIG) logger.configure(**settings.LOG_CONFIG)
@@ -316,9 +313,9 @@ class Check(BaseAuditModel):
) )
def add_check_history(self, value: int, more_info: Any = None) -> None: def add_check_history(self, value: int, more_info: Any = None) -> None:
CheckHistory.objects.create(check_history=self, y=value, results=more_info) CheckHistory.objects.create(check_id=self.pk, y=value, results=more_info)
def handle_checkv2(self, data): def handle_check(self, data):
from alerts.models import Alert from alerts.models import Alert
# cpuload or mem checks # cpuload or mem checks
@@ -349,9 +346,6 @@ class Check(BaseAuditModel):
elif self.check_type == "diskspace": elif self.check_type == "diskspace":
if data["exists"]: if data["exists"]:
percent_used = round(data["percent_used"]) percent_used = round(data["percent_used"])
total = bytes2human(data["total"])
free = bytes2human(data["free"])
if self.error_threshold and (100 - percent_used) < self.error_threshold: if self.error_threshold and (100 - percent_used) < self.error_threshold:
self.status = "failing" self.status = "failing"
self.alert_severity = "error" self.alert_severity = "error"
@@ -365,7 +359,7 @@ class Check(BaseAuditModel):
else: else:
self.status = "passing" self.status = "passing"
self.more_info = f"Total: {total}B, Free: {free}B" self.more_info = data["more_info"]
# add check history # add check history
self.add_check_history(100 - percent_used) self.add_check_history(100 - percent_used)
@@ -381,12 +375,7 @@ class Check(BaseAuditModel):
self.stdout = data["stdout"] self.stdout = data["stdout"]
self.stderr = data["stderr"] self.stderr = data["stderr"]
self.retcode = data["retcode"] self.retcode = data["retcode"]
try: self.execution_time = "{:.4f}".format(data["runtime"])
# python agent
self.execution_time = "{:.4f}".format(data["stop"] - data["start"])
except:
# golang agent
self.execution_time = "{:.4f}".format(data["runtime"])
if data["retcode"] in self.info_return_codes: if data["retcode"] in self.info_return_codes:
self.alert_severity = "info" self.alert_severity = "info"
@@ -422,22 +411,8 @@ class Check(BaseAuditModel):
# ping checks # ping checks
elif self.check_type == "ping": elif self.check_type == "ping":
output = data["output"] self.status = data["status"]
self.more_info = data["output"]
if pyver.parse(self.agent.version) <= pyver.parse("1.5.2"):
# DEPRECATED
success = ["Reply", "bytes", "time", "TTL"]
if data["has_stdout"]:
if all(x in output for x in success):
self.status = "passing"
else:
self.status = "failing"
elif data["has_stderr"]:
self.status = "failing"
else:
self.status = data["status"]
self.more_info = output
self.save(update_fields=["more_info"]) self.save(update_fields=["more_info"])
self.add_check_history( self.add_check_history(
@@ -446,41 +421,8 @@ class Check(BaseAuditModel):
# windows service checks # windows service checks
elif self.check_type == "winsvc": elif self.check_type == "winsvc":
svc_stat = data["status"] self.status = data["status"]
self.more_info = f"Status {svc_stat.upper()}" self.more_info = data["more_info"]
if data["exists"]:
if svc_stat == "running":
self.status = "passing"
elif svc_stat == "start_pending" and self.pass_if_start_pending:
self.status = "passing"
else:
if self.agent and self.restart_if_stopped:
nats_data = {
"func": "winsvcaction",
"payload": {"name": self.svc_name, "action": "start"},
}
r = asyncio.run(self.agent.nats_cmd(nats_data, timeout=32))
if r == "timeout" or r == "natsdown":
self.status = "failing"
elif not r["success"] and r["errormsg"]:
self.status = "failing"
elif r["success"]:
self.status = "passing"
self.more_info = f"Status RUNNING"
else:
self.status = "failing"
else:
self.status = "failing"
else:
if self.pass_if_svc_not_exist:
self.status = "passing"
else:
self.status = "failing"
self.more_info = f"Service {self.svc_name} does not exist"
self.save(update_fields=["more_info"]) self.save(update_fields=["more_info"])
self.add_check_history( self.add_check_history(
@@ -488,49 +430,7 @@ class Check(BaseAuditModel):
) )
elif self.check_type == "eventlog": elif self.check_type == "eventlog":
log = [] log = data["log"]
is_wildcard = self.event_id_is_wildcard
eventType = self.event_type
eventID = self.event_id
source = self.event_source
message = self.event_message
r = data["log"]
for i in r:
if i["eventType"] == eventType:
if not is_wildcard and not int(i["eventID"]) == eventID:
continue
if not source and not message:
if is_wildcard:
log.append(i)
elif int(i["eventID"]) == eventID:
log.append(i)
continue
if source and message:
if is_wildcard:
if source in i["source"] and message in i["message"]:
log.append(i)
elif int(i["eventID"]) == eventID:
if source in i["source"] and message in i["message"]:
log.append(i)
continue
if source and source in i["source"]:
if is_wildcard:
log.append(i)
elif int(i["eventID"]) == eventID:
log.append(i)
if message and message in i["message"]:
if is_wildcard:
log.append(i)
elif int(i["eventID"]) == eventID:
log.append(i)
if self.fail_when == "contains": if self.fail_when == "contains":
if log and len(log) >= self.number_of_events_b4_alert: if log and len(log) >= self.number_of_events_b4_alert:
self.status = "failing" self.status = "failing"
@@ -567,6 +467,11 @@ class Check(BaseAuditModel):
return self.status return self.status
def handle_assigned_task(self) -> None:
for task in self.assignedtask.all(): # type: ignore
if task.enabled:
task.run_win_task()
@staticmethod @staticmethod
def serialize(check): def serialize(check):
# serializes the check and returns json # serializes the check and returns json
@@ -604,7 +509,12 @@ class Check(BaseAuditModel):
) )
for task in self.assignedtask.all(): # type: ignore for task in self.assignedtask.all(): # type: ignore
task.create_policy_task(agent=agent, policy=policy, assigned_check=check) if policy or (
agent and not agent.autotasks.filter(parent_task=task.pk).exists()
):
task.create_policy_task(
agent=agent, policy=policy, assigned_check=check
)
for field in self.policy_fields_to_copy: for field in self.policy_fields_to_copy:
setattr(check, field, getattr(self, field)) setattr(check, field, getattr(self, field))
@@ -778,14 +688,10 @@ class Check(BaseAuditModel):
class CheckHistory(models.Model): class CheckHistory(models.Model):
check_history = models.ForeignKey( check_id = models.PositiveIntegerField(default=0)
Check,
related_name="check_history",
on_delete=models.CASCADE,
)
x = models.DateTimeField(auto_now_add=True) x = models.DateTimeField(auto_now_add=True)
y = models.PositiveIntegerField(null=True, blank=True, default=None) y = models.PositiveIntegerField(null=True, blank=True, default=None)
results = models.JSONField(null=True, blank=True) results = models.JSONField(null=True, blank=True)
def __str__(self): def __str__(self):
return self.check_history.readable_desc return self.x

View File

@@ -158,14 +158,8 @@ class AssignedTaskCheckRunnerField(serializers.ModelSerializer):
class CheckRunnerGetSerializer(serializers.ModelSerializer): class CheckRunnerGetSerializer(serializers.ModelSerializer):
# only send data needed for agent to run a check # only send data needed for agent to run a check
assigned_tasks = serializers.SerializerMethodField()
script = ScriptCheckSerializer(read_only=True) script = ScriptCheckSerializer(read_only=True)
def get_assigned_tasks(self, obj):
if obj.assignedtask.exists():
tasks = obj.assignedtask.all()
return AssignedTaskCheckRunnerField(tasks, many=True).data
class Meta: class Meta:
model = Check model = Check
exclude = [ exclude = [
@@ -193,6 +187,7 @@ class CheckRunnerGetSerializer(serializers.ModelSerializer):
"modified_by", "modified_by",
"modified_time", "modified_time",
"history", "history",
"dashboard_alert",
] ]

View File

@@ -363,10 +363,10 @@ class TestCheckViews(TacticalTestCase):
# setup data # setup data
agent = baker.make_recipe("agents.agent") agent = baker.make_recipe("agents.agent")
check = baker.make_recipe("checks.diskspace_check", agent=agent) check = baker.make_recipe("checks.diskspace_check", agent=agent)
baker.make("checks.CheckHistory", check_history=check, _quantity=30) baker.make("checks.CheckHistory", check_id=check.id, _quantity=30)
check_history_data = baker.make( check_history_data = baker.make(
"checks.CheckHistory", "checks.CheckHistory",
check_history=check, check_id=check.id,
_quantity=30, _quantity=30,
) )
@@ -400,17 +400,17 @@ class TestCheckTasks(TacticalTestCase):
def setUp(self): def setUp(self):
self.authenticate() self.authenticate()
self.setup_coresettings() self.setup_coresettings()
self.agent = baker.make_recipe("agents.agent") self.agent = baker.make_recipe("agents.agent", version="1.5.7")
def test_prune_check_history(self): def test_prune_check_history(self):
from .tasks import prune_check_history from .tasks import prune_check_history
# setup data # setup data
check = baker.make_recipe("checks.diskspace_check") check = baker.make_recipe("checks.diskspace_check")
baker.make("checks.CheckHistory", check_history=check, _quantity=30) baker.make("checks.CheckHistory", check_id=check.id, _quantity=30)
check_history_data = baker.make( check_history_data = baker.make(
"checks.CheckHistory", "checks.CheckHistory",
check_history=check, check_id=check.id,
_quantity=30, _quantity=30,
) )
@@ -526,6 +526,7 @@ class TestCheckTasks(TacticalTestCase):
"percent_used": 85, "percent_used": 85,
"total": 500, "total": 500,
"free": 400, "free": 400,
"more_info": "More info",
} }
resp = self.client.patch(url, data, format="json") resp = self.client.patch(url, data, format="json")
@@ -543,6 +544,7 @@ class TestCheckTasks(TacticalTestCase):
"percent_used": 95, "percent_used": 95,
"total": 500, "total": 500,
"free": 400, "free": 400,
"more_info": "More info",
} }
resp = self.client.patch(url, data, format="json") resp = self.client.patch(url, data, format="json")
@@ -573,6 +575,7 @@ class TestCheckTasks(TacticalTestCase):
"percent_used": 95, "percent_used": 95,
"total": 500, "total": 500,
"free": 400, "free": 400,
"more_info": "More info",
} }
resp = self.client.patch(url, data, format="json") resp = self.client.patch(url, data, format="json")
@@ -592,6 +595,7 @@ class TestCheckTasks(TacticalTestCase):
"percent_used": 95, "percent_used": 95,
"total": 500, "total": 500,
"free": 400, "free": 400,
"more_info": "More info",
} }
resp = self.client.patch(url, data, format="json") resp = self.client.patch(url, data, format="json")
@@ -608,6 +612,7 @@ class TestCheckTasks(TacticalTestCase):
"percent_used": 50, "percent_used": 50,
"total": 500, "total": 500,
"free": 400, "free": 400,
"more_info": "More info",
} }
resp = self.client.patch(url, data, format="json") resp = self.client.patch(url, data, format="json")
@@ -791,12 +796,7 @@ class TestCheckTasks(TacticalTestCase):
) )
# test failing info # test failing info
data = { data = {"id": ping.id, "status": "failing", "output": "reply from a.com"}
"id": ping.id,
"output": "Reply from 192.168.1.27: Destination host unreachable",
"has_stdout": True,
"has_stderr": False,
}
resp = self.client.patch(url, data, format="json") resp = self.client.patch(url, data, format="json")
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
@@ -806,13 +806,6 @@ class TestCheckTasks(TacticalTestCase):
self.assertEqual(new_check.alert_severity, "info") self.assertEqual(new_check.alert_severity, "info")
# test failing warning # test failing warning
data = {
"id": ping.id,
"output": "Reply from 192.168.1.27: Destination host unreachable",
"has_stdout": True,
"has_stderr": False,
}
ping.alert_severity = "warning" ping.alert_severity = "warning"
ping.save() ping.save()
@@ -824,13 +817,6 @@ class TestCheckTasks(TacticalTestCase):
self.assertEqual(new_check.alert_severity, "warning") self.assertEqual(new_check.alert_severity, "warning")
# test failing error # test failing error
data = {
"id": ping.id,
"output": "Reply from 192.168.1.27: Destination host unreachable",
"has_stdout": True,
"has_stderr": False,
}
ping.alert_severity = "error" ping.alert_severity = "error"
ping.save() ping.save()
@@ -842,13 +828,6 @@ class TestCheckTasks(TacticalTestCase):
self.assertEqual(new_check.alert_severity, "error") self.assertEqual(new_check.alert_severity, "error")
# test failing error # test failing error
data = {
"id": ping.id,
"output": "some output",
"has_stdout": False,
"has_stderr": True,
}
resp = self.client.patch(url, data, format="json") resp = self.client.patch(url, data, format="json")
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
@@ -857,12 +836,7 @@ class TestCheckTasks(TacticalTestCase):
self.assertEqual(new_check.alert_severity, "error") self.assertEqual(new_check.alert_severity, "error")
# test passing # test passing
data = { data = {"id": ping.id, "status": "passing", "output": "reply from a.com"}
"id": ping.id,
"output": "Reply from 192.168.1.1: bytes=32 time<1ms TTL=64",
"has_stdout": True,
"has_stderr": False,
}
resp = self.client.patch(url, data, format="json") resp = self.client.patch(url, data, format="json")
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
@@ -881,7 +855,7 @@ class TestCheckTasks(TacticalTestCase):
) )
# test passing running # test passing running
data = {"id": winsvc.id, "exists": True, "status": "running"} data = {"id": winsvc.id, "status": "passing", "more_info": "ok"}
resp = self.client.patch(url, data, format="json") resp = self.client.patch(url, data, format="json")
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
@@ -889,20 +863,8 @@ class TestCheckTasks(TacticalTestCase):
new_check = Check.objects.get(pk=winsvc.id) new_check = Check.objects.get(pk=winsvc.id)
self.assertEqual(new_check.status, "passing") self.assertEqual(new_check.status, "passing")
# test passing start pending # test failing
winsvc.pass_if_start_pending = True data = {"id": winsvc.id, "status": "failing", "more_info": "ok"}
winsvc.save()
data = {"id": winsvc.id, "exists": True, "status": "start_pending"}
resp = self.client.patch(url, data, format="json")
self.assertEqual(resp.status_code, 200)
new_check = Check.objects.get(pk=winsvc.id)
self.assertEqual(new_check.status, "passing")
# test failing no start
data = {"id": winsvc.id, "exists": True, "status": "not running"}
resp = self.client.patch(url, data, format="json") resp = self.client.patch(url, data, format="json")
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
@@ -911,7 +873,7 @@ class TestCheckTasks(TacticalTestCase):
self.assertEqual(new_check.status, "failing") self.assertEqual(new_check.status, "failing")
self.assertEqual(new_check.alert_severity, "info") self.assertEqual(new_check.alert_severity, "info")
# test failing and attempt start """ # test failing and attempt start
winsvc.restart_if_stopped = True winsvc.restart_if_stopped = True
winsvc.alert_severity = "warning" winsvc.alert_severity = "warning"
winsvc.save() winsvc.save()
@@ -976,9 +938,9 @@ class TestCheckTasks(TacticalTestCase):
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
new_check = Check.objects.get(pk=winsvc.id) new_check = Check.objects.get(pk=winsvc.id)
self.assertEqual(new_check.status, "passing") self.assertEqual(new_check.status, "passing") """
def test_handle_eventlog_check(self): """ def test_handle_eventlog_check(self):
from checks.models import Check from checks.models import Check
url = "/api/v3/checkrunner/" url = "/api/v3/checkrunner/"
@@ -1180,4 +1142,4 @@ class TestCheckTasks(TacticalTestCase):
new_check = Check.objects.get(pk=eventlog.id) new_check = Check.objects.get(pk=eventlog.id)
self.assertEquals(new_check.status, "passing") self.assertEquals(new_check.status, "passing") """

View File

@@ -8,5 +8,5 @@ urlpatterns = [
path("<pk>/loadchecks/", views.load_checks), path("<pk>/loadchecks/", views.load_checks),
path("getalldisks/", views.get_disks_for_policies), path("getalldisks/", views.get_disks_for_policies),
path("runchecks/<pk>/", views.run_checks), path("runchecks/<pk>/", views.run_checks),
path("history/<int:checkpk>/", views.CheckHistory.as_view()), path("history/<int:checkpk>/", views.GetCheckHistory.as_view()),
] ]

View File

@@ -15,7 +15,7 @@ from automation.models import Policy
from scripts.models import Script from scripts.models import Script
from tacticalrmm.utils import notify_error from tacticalrmm.utils import notify_error
from .models import Check from .models import Check, CheckHistory
from .permissions import ManageChecksPerms, RunChecksPerms from .permissions import ManageChecksPerms, RunChecksPerms
from .serializers import CheckHistorySerializer, CheckSerializer from .serializers import CheckHistorySerializer, CheckSerializer
@@ -146,7 +146,7 @@ class GetUpdateDeleteCheck(APIView):
return Response(f"{check.readable_desc} was deleted!") return Response(f"{check.readable_desc} was deleted!")
class CheckHistory(APIView): class GetCheckHistory(APIView):
def patch(self, request, checkpk): def patch(self, request, checkpk):
check = get_object_or_404(Check, pk=checkpk) check = get_object_or_404(Check, pk=checkpk)
@@ -160,7 +160,7 @@ class CheckHistory(APIView):
- djangotime.timedelta(days=request.data["timeFilter"]), - djangotime.timedelta(days=request.data["timeFilter"]),
) )
check_history = check.check_history.filter(timeFilter).order_by("-x") # type: ignore check_history = CheckHistory.objects.filter(check_id=checkpk).filter(timeFilter).order_by("-x") # type: ignore
return Response( return Response(
CheckHistorySerializer( CheckHistorySerializer(

View File

@@ -1,6 +1,5 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from agents.models import Agent
from logs.models import PendingAction from logs.models import PendingAction
from scripts.models import Script from scripts.models import Script
@@ -9,22 +8,6 @@ class Command(BaseCommand):
help = "Collection of tasks to run after updating the rmm, after migrations" help = "Collection of tasks to run after updating the rmm, after migrations"
def handle(self, *args, **kwargs): def handle(self, *args, **kwargs):
# 10-16-2020 changed the type of the agent's 'disks' model field
# from a dict of dicts, to a list of disks in the golang agent
# the following will convert dicts to lists for agent's still on the python agent
agents = Agent.objects.only("pk", "disks")
for agent in agents:
if agent.disks is not None and isinstance(agent.disks, dict):
new = []
for k, v in agent.disks.items():
new.append(v)
agent.disks = new
agent.save(update_fields=["disks"])
self.stdout.write(
self.style.SUCCESS(f"Migrated disks on {agent.hostname}")
)
# remove task pending actions. deprecated 4/20/2021 # remove task pending actions. deprecated 4/20/2021
PendingAction.objects.filter(action_type="taskaction").delete() PendingAction.objects.filter(action_type="taskaction").delete()

View File

@@ -85,7 +85,7 @@ def dashboard_info(request):
"client_tree_sort": request.user.client_tree_sort, "client_tree_sort": request.user.client_tree_sort,
"client_tree_splitter": request.user.client_tree_splitter, "client_tree_splitter": request.user.client_tree_splitter,
"loading_bar_color": request.user.loading_bar_color, "loading_bar_color": request.user.loading_bar_color,
"no_code_sign": hasattr(settings, "NOCODESIGN") and settings.NOCODESIGN, "hosted": hasattr(settings, "HOSTED") and settings.HOSTED,
} }
) )

View File

@@ -1,19 +1,18 @@
asgiref==3.3.4 asgiref==3.3.4
asyncio-nats-client==0.11.4 asyncio-nats-client==0.11.4
celery==5.0.5 celery==5.1.0
certifi==2020.12.5 certifi==2021.5.30
cffi==1.14.5 cffi==1.14.5
channels==3.0.3 channels==3.0.3
channels_redis==3.2.0 channels_redis==3.2.0
chardet==4.0.0 chardet==4.0.0
cryptography==3.4.7 cryptography==3.4.7
daphne==3.0.2 daphne==3.0.2
Django==3.2.2 Django==3.2.4
django-cors-headers==3.7.0 django-cors-headers==3.7.0
django-rest-knox==4.1.0 django-rest-knox==4.1.0
djangorestframework==3.12.4 djangorestframework==3.12.4
future==0.18.2 future==0.18.2
kombu==5.0.2
loguru==0.5.3 loguru==0.5.3
msgpack==1.0.2 msgpack==1.0.2
packaging==20.9 packaging==20.9
@@ -26,10 +25,10 @@ pytz==2021.1
qrcode==6.1 qrcode==6.1
redis==3.5.3 redis==3.5.3
requests==2.25.1 requests==2.25.1
six==1.15.0 six==1.16.0
sqlparse==0.4.1 sqlparse==0.4.1
twilio==6.57.0 twilio==6.59.1
urllib3==1.26.4 urllib3==1.26.5
uWSGI==2.0.19.1 uWSGI==2.0.19.1
validators==0.18.2 validators==0.18.2
vine==5.0.0 vine==5.0.0

View File

@@ -6,7 +6,9 @@
"name": "Firefox - Clean Cache", "name": "Firefox - Clean Cache",
"description": "This script will clean up Mozilla Firefox for all users.", "description": "This script will clean up Mozilla Firefox for all users.",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):Browsers" "category": "TRMM (Win):Browsers",
"default_timeout": "300"
}, },
{ {
"guid": "3ff6a386-11d1-4f9d-8cca-1b0563bb6443", "guid": "3ff6a386-11d1-4f9d-8cca-1b0563bb6443",
@@ -15,7 +17,8 @@
"name": "Chrome - Clear Cache for All Users", "name": "Chrome - Clear Cache for All Users",
"description": "This script will clean up Google Chrome for all users.", "description": "This script will clean up Google Chrome for all users.",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):Browsers" "category": "TRMM (Win):Browsers",
"default_timeout": "300"
}, },
{ {
"guid": "be1de837-f677-4ac5-aa0c-37a0fc9991fc", "guid": "be1de837-f677-4ac5-aa0c-37a0fc9991fc",
@@ -24,7 +27,8 @@
"name": "Adobe Reader DC - Install", "name": "Adobe Reader DC - Install",
"description": "Installs Adobe Reader DC.", "description": "Installs Adobe Reader DC.",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):3rd Party Software>Chocolatey" "category": "TRMM (Win):3rd Party Software>Chocolatey",
"default_timeout": "300"
}, },
{ {
"guid": "2ee134d5-76aa-4160-b334-a1efbc62079f", "guid": "2ee134d5-76aa-4160-b334-a1efbc62079f",
@@ -33,7 +37,8 @@
"name": "Duplicati - Install", "name": "Duplicati - Install",
"description": "This script installs Duplicati 2.0.5.1 as a service.", "description": "This script installs Duplicati 2.0.5.1 as a service.",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):3rd Party Software" "category": "TRMM (Win):3rd Party Software",
"default_timeout": "300"
}, },
{ {
"guid": "81cc5bcb-01bf-4b0c-89b9-0ac0f3fe0c04", "guid": "81cc5bcb-01bf-4b0c-89b9-0ac0f3fe0c04",
@@ -42,7 +47,8 @@
"name": "Windows Update - Reset", "name": "Windows Update - Reset",
"description": "This script will reset all of the Windows Updates components to DEFAULT SETTINGS.", "description": "This script will reset all of the Windows Updates components to DEFAULT SETTINGS.",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):Updates" "category": "TRMM (Win):Updates",
"default_timeout": "300"
}, },
{ {
"guid": "8db87ff0-a9b4-4d9d-bc55-377bbcb85b6d", "guid": "8db87ff0-a9b4-4d9d-bc55-377bbcb85b6d",
@@ -51,7 +57,8 @@
"name": "Disk - 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.", "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", "shell": "powershell",
"category": "TRMM (Win):Maintenance" "category": "TRMM (Win):Maintenance",
"default_timeout": "25000"
}, },
{ {
"guid": "2f28e8c1-ae0f-4b46-a826-f513974526a3", "guid": "2f28e8c1-ae0f-4b46-a826-f513974526a3",
@@ -78,7 +85,8 @@
"name": "Speed Test - Python", "name": "Speed Test - Python",
"description": "Runs a Speed Test using Python", "description": "Runs a Speed Test using Python",
"shell": "python", "shell": "python",
"category": "TRMM (Win):Network" "category": "TRMM (Win):Network",
"default_timeout": "120"
}, },
{ {
"guid": "9d34f482-1f0c-4b2f-b65f-a9cf3c13ef5f", "guid": "9d34f482-1f0c-4b2f-b65f-a9cf3c13ef5f",
@@ -161,6 +169,18 @@
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):Collectors" "category": "TRMM (Win):Collectors"
}, },
{
"guid": "973c34d7-cab0-4fda-999c-b4933655f946",
"filename": "Win_Screenconnect_GetGUID.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Screenconnect - Get GUID for client",
"description": "Returns Screenconnect GUID for client - Use with Custom Fields for later use. ",
"args": [
"-serviceName {{client.ScreenConnectService}}"
],
"shell": "powershell",
"category": "TRMM (Win):Collectors"
},
{ {
"guid": "95a2ee6f-b89b-4551-856e-3081b041caa7", "guid": "95a2ee6f-b89b-4551-856e-3081b041caa7",
"filename": "Win_Power_Profile_Reset_High_Performance_to_Defaults.ps1", "filename": "Win_Power_Profile_Reset_High_Performance_to_Defaults.ps1",
@@ -186,7 +206,8 @@
"name": "Windows 10 Upgrade", "name": "Windows 10 Upgrade",
"description": "Forces an upgrade to the latest release of Windows 10.", "description": "Forces an upgrade to the latest release of Windows 10.",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):Updates" "category": "TRMM (Win):Updates",
"default_timeout": "25000"
}, },
{ {
"guid": "375323e5-cac6-4f35-a304-bb7cef35902d", "guid": "375323e5-cac6-4f35-a304-bb7cef35902d",
@@ -222,7 +243,8 @@
"name": "SSH - Install Feature and Enable", "name": "SSH - Install Feature and Enable",
"description": "Installs and enabled OpenSSH Server Feature in Win10", "description": "Installs and enabled OpenSSH Server Feature in Win10",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):Windows Features" "category": "TRMM (Win):Windows Features",
"default_timeout": "300"
}, },
{ {
"guid": "2435297a-6263-4e90-8688-1847400d0e22", "guid": "2435297a-6263-4e90-8688-1847400d0e22",
@@ -339,7 +361,8 @@
"name": "Update Installed Apps", "name": "Update Installed Apps",
"description": "Update all apps that were installed using Chocolatey.", "description": "Update all apps that were installed using Chocolatey.",
"shell": "cmd", "shell": "cmd",
"category": "TRMM (Win):3rd Party Software>Chocolatey" "category": "TRMM (Win):3rd Party Software>Chocolatey",
"default_timeout": "3600"
}, },
{ {
"guid": "fff8024d-d72e-4457-84fa-6c780f69a16f", "guid": "fff8024d-d72e-4457-84fa-6c780f69a16f",
@@ -660,4 +683,4 @@
"category": "TRMM (Win):Misc>Reference", "category": "TRMM (Win):Misc>Reference",
"default_timeout": "1" "default_timeout": "1"
} }
] ]

View File

@@ -41,7 +41,7 @@ app.conf.beat_schedule = {
}, },
"get-wmi": { "get-wmi": {
"task": "agents.tasks.get_wmi_task", "task": "agents.tasks.get_wmi_task",
"schedule": crontab(minute="*/18"), "schedule": crontab(minute=18, hour="*/5"),
}, },
} }

View File

@@ -15,22 +15,22 @@ EXE_DIR = os.path.join(BASE_DIR, "tacticalrmm/private/exe")
AUTH_USER_MODEL = "accounts.User" AUTH_USER_MODEL = "accounts.User"
# latest release # latest release
TRMM_VERSION = "0.6.11" TRMM_VERSION = "0.6.14"
# bump this version everytime vue code is changed # bump this version everytime vue code is changed
# to alert user they need to manually refresh their browser # to alert user they need to manually refresh their browser
APP_VER = "0.0.136" APP_VER = "0.0.138"
# https://github.com/wh1te909/rmmagent # https://github.com/wh1te909/rmmagent
LATEST_AGENT_VER = "1.5.6" LATEST_AGENT_VER = "1.5.8"
MESH_VER = "0.8.35" MESH_VER = "0.8.60"
# for the update script, bump when need to recreate venv or npm install # for the update script, bump when need to recreate venv or npm install
PIP_VER = "16" PIP_VER = "18"
NPM_VER = "15" NPM_VER = "17"
SETUPTOOLS_VER = "56.1.0" SETUPTOOLS_VER = "57.0.0"
WHEEL_VER = "0.36.2" WHEEL_VER = "0.36.2"
DL_64 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_VER}/winagent-v{LATEST_AGENT_VER}.exe" DL_64 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_VER}/winagent-v{LATEST_AGENT_VER}.exe"

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
SCRIPT_VERSION="12" SCRIPT_VERSION="13"
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh' SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh'
GREEN='\033[0;32m' GREEN='\033[0;32m'
@@ -59,6 +59,7 @@ mkdir ${tmp_dir}/nginx
mkdir ${tmp_dir}/systemd mkdir ${tmp_dir}/systemd
mkdir ${tmp_dir}/rmm mkdir ${tmp_dir}/rmm
mkdir ${tmp_dir}/confd mkdir ${tmp_dir}/confd
mkdir ${tmp_dir}/redis
pg_dump --dbname=postgresql://"${POSTGRES_USER}":"${POSTGRES_PW}"@127.0.0.1:5432/tacticalrmm | gzip -9 > ${tmp_dir}/postgres/db-${dt_now}.psql.gz pg_dump --dbname=postgresql://"${POSTGRES_USER}":"${POSTGRES_PW}"@127.0.0.1:5432/tacticalrmm | gzip -9 > ${tmp_dir}/postgres/db-${dt_now}.psql.gz
@@ -72,6 +73,8 @@ sudo tar -czvf ${tmp_dir}/nginx/etc-nginx.tar.gz -C /etc/nginx .
sudo tar -czvf ${tmp_dir}/confd/etc-confd.tar.gz -C /etc/conf.d . sudo tar -czvf ${tmp_dir}/confd/etc-confd.tar.gz -C /etc/conf.d .
sudo tar -czvf ${tmp_dir}/redis/etc-redis.tar.gz -C /var/lib/redis/appendonly.aof
sudo cp ${sysd}/rmm.service ${sysd}/celery.service ${sysd}/celerybeat.service ${sysd}/meshcentral.service ${sysd}/nats.service ${tmp_dir}/systemd/ sudo cp ${sysd}/rmm.service ${sysd}/celery.service ${sysd}/celerybeat.service ${sysd}/meshcentral.service ${sysd}/nats.service ${tmp_dir}/systemd/
if [ -f "${sysd}/daphne.service" ]; then if [ -f "${sysd}/daphne.service" ]; then
sudo cp ${sysd}/daphne.service ${tmp_dir}/systemd/ sudo cp ${sysd}/daphne.service ${tmp_dir}/systemd/

View File

@@ -1,4 +1,4 @@
FROM nats:2.2-alpine FROM nats:2.2.6-alpine
ENV TACTICAL_DIR /opt/tactical ENV TACTICAL_DIR /opt/tactical
ENV TACTICAL_READY_FILE ${TACTICAL_DIR}/tmp/tactical.ready ENV TACTICAL_READY_FILE ${TACTICAL_DIR}/tmp/tactical.ready

View File

@@ -18,6 +18,7 @@ volumes:
postgres_data: postgres_data:
mongo_data: mongo_data:
mesh_data: mesh_data:
redis_data:
services: services:
# postgres database for api service # postgres database for api service
@@ -38,7 +39,10 @@ services:
tactical-redis: tactical-redis:
container_name: trmm-redis container_name: trmm-redis
image: redis:6.0-alpine image: redis:6.0-alpine
command: redis-server --appendonly yes
restart: always restart: always
volumes:
- redis_data:/data
networks: networks:
- redis - redis

View File

@@ -61,6 +61,8 @@ Category or Function - What It Does
![json_name_examples](images/community_scripts_name_field_example1.png) ![json_name_examples](images/community_scripts_name_field_example1.png)
*****
## Making Script Files ## Making Script Files
### Good Habits ### Good Habits
@@ -117,6 +119,8 @@ c:\ProgramData\TacticalRMM\
- Doesn't play well with other community scripts (reused names etc.) - Doesn't play well with other community scripts (reused names etc.)
*****
## Useful Reference Script Examples ## Useful Reference Script Examples
RunAsUser (since Tactical RMM runs as system) RunAsUser (since Tactical RMM runs as system)
@@ -128,6 +132,8 @@ Command Paramater Ninja
Optional Command Parameters and testing for errors 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) [https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_Rename_Computer.ps1](https://github.com/wh1te909/tacticalrmm/blob/develop/scripts/Win_Rename_Computer.ps1)
*****
## Volunteers Needed ## Volunteers Needed
If you want to contribute back to the project there are a lot of scripts that need some TLC (Tender Loving Care) please paruse thru them here: [https://github.com/wh1te909/tacticalrmm/tree/develop/scripts_wip](https://github.com/wh1te909/tacticalrmm/tree/develop/scripts_wip) If you want to contribute back to the project there are a lot of scripts that need some TLC (Tender Loving Care) please paruse thru them here: [https://github.com/wh1te909/tacticalrmm/tree/develop/scripts_wip](https://github.com/wh1te909/tacticalrmm/tree/develop/scripts_wip)

View File

@@ -105,6 +105,7 @@ Then you're `push`ing that updated local repo to your online Github fork
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 ## Reference
### Customizing the Admin Web Interface ### Customizing the Admin Web Interface

View File

@@ -0,0 +1,31 @@
# Examples
## Create Run URL Action to Computer support page
This will create a URL link that will take you to the support page for a computer based on the computers Serial Number
1. Goto `Settings | Global Settings | Custom Fields`
Under Agents tab Add Custom Field (CaSe SeNsItIve)
![Custom Field](../images/example1_customfield.png)
2. Create Task (best to use `Settings | Automation Manager` if you want to apply it to all computers). Add script that has an output of the data you want.
![Collector Script](../images/example1_taskcollectorscript.png)
3. Create URL Action (under `Settings | Global Settings | URL ACTIONS`) for Manufacturer websites
![URL Actions](../images/example1_urlaction.png)
Dell Support Page
```
https://www.dell.com/support/home/en-us/product-support/servicetag/{{agent.SerialNumber}}/overview
```
Lenovo Support Page
```
https://www.dell.com/support/home/en-us/product-support/servicetag/{{agent.SerialNumber}}/overview
```

View File

@@ -0,0 +1,58 @@
# How It All Works
INSERT WIREFRAME GRAPHIC HERE USING <https://www.yworks.com/yed-live/>
## Server
## Windows Agent
Found in `%programfiles%\TacticalAgent`
### Services
3 services exist on all clients
* `Mesh Agent`
![MeshService](images/trmm_services_mesh.png)
![MeshAgentTaskManager](images/trmm_services__taskmanager_mesh.png)
**AND**
* `TacticalAgent` and `Tactical RMM RPC Service`
![TacticalAgentServices](images/trmm_services.png)
![TacticalAgentTaskManager](images/trmm_services__taskmanager_agent.png)
The [MeshCentral](https://meshcentral.com/) system which is accessible from <https://mesh.example.com> and is used
`Tactical RMM Agent`
* It runs 2 goroutines
* one is the checkrunner which runs all the checks and then just sleeps until it's time to run more checks
* 2nd goroutine periodically sends info about the agent to the rmm and also handles agent recovery
!!!note
In Task Manager you will see additional `Tactical RMM Agent` processes appear and disappear. These are your Checks and Tasks running at scheduled intervals
`Tactical RMM RPC Service`
* Uses the pub/sub model so anytime you do anything realtime from rmm (like a send command or run script)
* It maintains a persistent connection to your to the api.example.com rmm server on port **443 CONFIRM** and is listening for events
* It handles your Agent updates (Auto triggers at 35mins past every hour or when run manually from server Agents | Update Agents menu)
***
### Agent Installation Process
Adds Defender AV exclusions
Copies
***
### Agent Update Process
Downloads latest `winagent-vx.x.x-x86/64.exe` to `%programfiles%`
Executes the file (INNO setup exe)
Files create `c:\Windows\temp\Tacticalxxxx\` folder for install (and log files)

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -14,6 +14,10 @@
## Install ## Install
!!!info
It is recommended that you keep your server updated regularly (monthly). SSL wildcard certs will expire every 3 months and need manual updating as well. <br/><br/>
Until we reach production release, there may be architectural changes that may be made to Tactical RMM and only a regular patching schedule is supported by developers.
#### Run updates and setup the linux user #### Run updates and setup the linux user
SSH into the server as **root**.<br/><br/> SSH into the server as **root**.<br/><br/>
Download and run the prereqs and latest updates<br/> Download and run the prereqs and latest updates<br/>

52
docs/docs/tipsntricks.md Normal file
View File

@@ -0,0 +1,52 @@
# Tips and Tricks
## Customize User Interface
Top right click Username, look at preferences pane. Set default tab: Servers|Workstations|Mixed
![User Preferences](images/trmm_user_preferences.png)
*****
## Screenconnect / Connectwise Control
### Install Tactical RMM via Screeconnect commands window
1. Create a Deplopment under Agents | Manage Deployments
2. Replace `<deployment URL>` below with your Deployment Download Link.
**x64**
```cmd
#!ps
#maxlength=500000
#timeout=600000
Invoke-WebRequest "<deployment URL>" -OutFile ( New-Item -Path "C:\temp\trmminstallx64.exe" -Force )
$proc = Start-Process c:\temp\trmminstallx64.exe -ArgumentList '-silent' -PassThru
Wait-Process -InputObject $proc
if ($proc.ExitCode -ne 0) {
Write-Warning "$_ exited with status code $($proc.ExitCode)"
}
Remove-Item -Path "c:\temp\trmminstallx64.exe" -Force
```
**x86**
```cmd
#!ps
#maxlength=500000
#timeout=600000
Invoke-WebRequest "<deployment URL>" -OutFile ( New-Item -Path "C:\temp\trmminstallx86.exe" -Force )
$proc = Start-Process c:\temp\trmminstallx86.exe -ArgumentList '-silent' -PassThru
Wait-Process -InputObject $proc
if ($proc.ExitCode -ne 0) {
Write-Warning "$_ exited with status code $($proc.ExitCode)"
}
Remove-Item -Path "c:\temp\trmminstallx86.exe" -Force
```
###

View File

@@ -23,6 +23,7 @@ nav:
- "User Interface Preferences": functions/user_ui.md - "User Interface Preferences": functions/user_ui.md
- "Django Admin": functions/django_admin.md - "Django Admin": functions/django_admin.md
- "Settings Override": functions/settings_override.md - "Settings Override": functions/settings_override.md
- "Examples": functions/examples.md
- Backup: backup.md - Backup: backup.md
- Restore: restore.md - Restore: restore.md
- Troubleshooting: troubleshooting.md - Troubleshooting: troubleshooting.md

2
go.mod
View File

@@ -5,6 +5,6 @@ go 1.16
require ( require (
github.com/nats-io/nats-server/v2 v2.1.8-0.20201129161730-ebe63db3e3ed // indirect github.com/nats-io/nats-server/v2 v2.1.8-0.20201129161730-ebe63db3e3ed // indirect
github.com/nats-io/nats.go v1.11.0 github.com/nats-io/nats.go v1.11.0
github.com/ugorji/go/codec v1.2.5 github.com/ugorji/go/codec v1.2.6
golang.org/x/sys v0.0.0-20210122235752-a8b976e07c7b // indirect golang.org/x/sys v0.0.0-20210122235752-a8b976e07c7b // indirect
) )

8
go.sum
View File

@@ -34,10 +34,10 @@ github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/ugorji/go v1.2.5 h1:NozRHfUeEta89taVkyfsDVSy2f7v89Frft4pjnWuGuc= github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
github.com/ugorji/go v1.2.5/go.mod h1:gat2tIT8KJG8TVI8yv77nEO/KYT6dV7JE1gfUa8Xuls= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
github.com/ugorji/go/codec v1.2.5 h1:8WobZKAk18Msm2CothY2jnztY56YVY8kF1oQrj21iis= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.5/go.mod h1:QPxoTbPKSEAlAHPYt02++xp/en9B/wUdwFCz+hj5caA= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
SCRIPT_VERSION="48" SCRIPT_VERSION="50"
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/install.sh' SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/install.sh'
sudo apt install -y curl wget dirmngr gnupg lsb-release sudo apt install -y curl wget dirmngr gnupg lsb-release
@@ -167,11 +167,11 @@ sudo chmod 775 -R /etc/letsencrypt
print_green 'Downloading NATS' print_green 'Downloading NATS'
nats_tmp=$(mktemp -d -t nats-XXXXXXXXXX) nats_tmp=$(mktemp -d -t nats-XXXXXXXXXX)
wget https://github.com/nats-io/nats-server/releases/download/v2.2.3/nats-server-v2.2.3-linux-amd64.tar.gz -P ${nats_tmp} wget https://github.com/nats-io/nats-server/releases/download/v2.2.6/nats-server-v2.2.6-linux-amd64.tar.gz -P ${nats_tmp}
tar -xzf ${nats_tmp}/nats-server-v2.2.3-linux-amd64.tar.gz -C ${nats_tmp} tar -xzf ${nats_tmp}/nats-server-v2.2.6-linux-amd64.tar.gz -C ${nats_tmp}
sudo mv ${nats_tmp}/nats-server-v2.2.3-linux-amd64/nats-server /usr/local/bin/ sudo mv ${nats_tmp}/nats-server-v2.2.6-linux-amd64/nats-server /usr/local/bin/
sudo chmod +x /usr/local/bin/nats-server sudo chmod +x /usr/local/bin/nats-server
sudo chown ${USER}:${USER} /usr/local/bin/nats-server sudo chown ${USER}:${USER} /usr/local/bin/nats-server
rm -rf ${nats_tmp} rm -rf ${nats_tmp}
@@ -217,6 +217,10 @@ sudo rm -rf Python-3.9.2 Python-3.9.2.tgz
print_green 'Installing redis and git' print_green 'Installing redis and git'
sudo apt install -y ca-certificates redis git sudo apt install -y ca-certificates redis git
# apply redis configuration
sudo redis-cli config set appendonly yes
sudo redis-cli config rewrite
print_green 'Installing postgresql' print_green 'Installing postgresql'
echo "$postgresql_repo" | sudo tee /etc/apt/sources.list.d/pgdg.list echo "$postgresql_repo" | sudo tee /etc/apt/sources.list.d/pgdg.list
@@ -487,12 +491,14 @@ map \$http_user_agent \$ignore_ua {
server { server {
listen 80; listen 80;
listen [::]:80;
server_name ${rmmdomain}; server_name ${rmmdomain};
return 301 https://\$server_name\$request_uri; return 301 https://\$server_name\$request_uri;
} }
server { server {
listen 443 ssl; listen 443 ssl;
listen [::]:443 ssl;
server_name ${rmmdomain}; server_name ${rmmdomain};
client_max_body_size 300M; client_max_body_size 300M;
access_log /rmm/api/tacticalrmm/tacticalrmm/private/log/access.log combined if=\$ignore_ua; access_log /rmm/api/tacticalrmm/tacticalrmm/private/log/access.log combined if=\$ignore_ua;
@@ -549,6 +555,7 @@ echo "${nginxrmm}" | sudo tee /etc/nginx/sites-available/rmm.conf > /dev/null
nginxmesh="$(cat << EOF nginxmesh="$(cat << EOF
server { server {
listen 80; listen 80;
listen [::]:80;
server_name ${meshdomain}; server_name ${meshdomain};
return 301 https://\$server_name\$request_uri; return 301 https://\$server_name\$request_uri;
} }
@@ -556,6 +563,7 @@ server {
server { server {
listen 443 ssl; listen 443 ssl;
listen [::]:443 ssl;
proxy_send_timeout 330s; proxy_send_timeout 330s;
proxy_read_timeout 330s; proxy_read_timeout 330s;
server_name ${meshdomain}; server_name ${meshdomain};
@@ -710,6 +718,7 @@ server {
access_log /var/log/nginx/frontend-access.log; access_log /var/log/nginx/frontend-access.log;
listen 443 ssl; listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate ${CERT_PUB_KEY}; ssl_certificate ${CERT_PUB_KEY};
ssl_certificate_key ${CERT_PRIV_KEY}; ssl_certificate_key ${CERT_PRIV_KEY};
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
@@ -720,7 +729,8 @@ server {
return 301 https://\$host\$request_uri; return 301 https://\$host\$request_uri;
} }
listen 80; listen 80;
listen [::]:80;
server_name ${frontenddomain}; server_name ${frontenddomain};
return 404; return 404;
} }

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
SCRIPT_VERSION="26" SCRIPT_VERSION="28"
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/restore.sh' SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/restore.sh'
sudo apt update sudo apt update
@@ -108,11 +108,11 @@ sudo apt update
print_green 'Downloading NATS' print_green 'Downloading NATS'
nats_tmp=$(mktemp -d -t nats-XXXXXXXXXX) nats_tmp=$(mktemp -d -t nats-XXXXXXXXXX)
wget https://github.com/nats-io/nats-server/releases/download/v2.2.3/nats-server-v2.2.3-linux-amd64.tar.gz -P ${nats_tmp} wget https://github.com/nats-io/nats-server/releases/download/v2.2.6/nats-server-v2.2.6-linux-amd64.tar.gz -P ${nats_tmp}
tar -xzf ${nats_tmp}/nats-server-v2.2.3-linux-amd64.tar.gz -C ${nats_tmp} tar -xzf ${nats_tmp}/nats-server-v2.2.6-linux-amd64.tar.gz -C ${nats_tmp}
sudo mv ${nats_tmp}/nats-server-v2.2.3-linux-amd64/nats-server /usr/local/bin/ sudo mv ${nats_tmp}/nats-server-v2.2.6-linux-amd64/nats-server /usr/local/bin/
sudo chmod +x /usr/local/bin/nats-server sudo chmod +x /usr/local/bin/nats-server
sudo chown ${USER}:${USER} /usr/local/bin/nats-server sudo chown ${USER}:${USER} /usr/local/bin/nats-server
rm -rf ${nats_tmp} rm -rf ${nats_tmp}
@@ -189,6 +189,13 @@ sudo rm -rf Python-3.9.2 Python-3.9.2.tgz
print_green 'Installing redis and git' print_green 'Installing redis and git'
sudo apt install -y ca-certificates redis git sudo apt install -y ca-certificates redis git
# redis configuration
sudo tar -xzf ${tmp_dir}/redis/etc-redis.tar.gz -C /var/lib/redis
sudo redis-check-aof --fix /var/lib/redis/appendonly.aof
sudo redis-cli config set appendonly yes
sudo redis-cli config rewrite
print_green 'Installing postgresql' print_green 'Installing postgresql'
echo "$postgresql_repo" | sudo tee /etc/apt/sources.list.d/pgdg.list echo "$postgresql_repo" | sudo tee /etc/apt/sources.list.d/pgdg.list

View File

@@ -4,6 +4,8 @@
#antivirusName must match the "displayName" exactly #antivirusName must match the "displayName" exactly
#If no antivirusName parameter is specified, the tool returns success if there is any active up to date antivirus on the system #If no antivirusName parameter is specified, the tool returns success if there is any active up to date antivirus on the system
# OS Build must be greater than 14393 to support this script. If it's not it returns exit code 2
param($antivirusName = "*") param($antivirusName = "*")
@@ -93,6 +95,11 @@ function Add-ProductStates {
} }
} }
if ([environment]::OSVersion.Version.Build -le 14393) {
write-host "Antivirus check not supported on this OS. Returning Exit Code 2."
exit 2
}
$return = Get-CimInstance -Namespace root/SecurityCenter2 -className AntivirusProduct | $return = Get-CimInstance -Namespace root/SecurityCenter2 -className AntivirusProduct |
Where-Object { Where-Object {

View File

@@ -1,4 +1,9 @@
## Copied from https://github.com/ThatsNASt/tacticalrmm to add to new pull request for https://github.com/wh1te909/tacticalrmm ## Copied from https://github.com/ThatsNASt/tacticalrmm to add to new pull request for https://github.com/wh1te909/tacticalrmm
#
# WARNING
# 1. Only applies to drive C
# 2. Assumes you're encrypting more than the used space. "Used Space Only Encrypted" is the default windows behavior which is not compatible here.
function Log-Message { function Log-Message {
Param Param
( (
@@ -22,7 +27,7 @@ function Log-Message {
$log = "BitlockerReport.txt" $log = "BitlockerReport.txt"
#Find BL info #Find BL info
$mbde = [string](manage-bde -status) $mbde = [string](manage-bde -status C:)
$mbdeProt = (manage-bde -protectors -get c: | Select-Object -Skip 6) $mbdeProt = (manage-bde -protectors -get c: | Select-Object -Skip 6)
#Dig out the recovery password, check for PIN #Dig out the recovery password, check for PIN
ForEach ($line in $mbdeProt) { ForEach ($line in $mbdeProt) {
@@ -94,4 +99,4 @@ if ($Encrypted -eq "Yes" -and $RecoveryPassword -and $PIN -eq $true) {
Log-Message "SUCCESS: Encrypted, PIN enabled, password is set." $log e Log-Message "SUCCESS: Encrypted, PIN enabled, password is set." $log e
Write-Host "Script check passed" Write-Host "Script check passed"
exit 0 exit 0
} }

View File

@@ -1 +1 @@
cup -y all cup all -y

View File

@@ -31,6 +31,7 @@ else {
Install-Module -Name RunAsUser -Force Install-Module -Name RunAsUser -Force
} }
# Used to pull variables in and use them inside the script block. Contains message to show user
Set-Content -Path c:\windows\temp\message.txt -Value $args Set-Content -Path c:\windows\temp\message.txt -Value $args
Invoke-AsCurrentUser -scriptblock { Invoke-AsCurrentUser -scriptblock {
@@ -55,5 +56,5 @@ Invoke-AsCurrentUser -scriptblock {
Submit-BTNotification -Content $Content Submit-BTNotification -Content $Content
} }
# Cleanup temp file for message variables
Remove-Item -Path c:\windows\temp\message.txt Remove-Item -Path c:\windows\temp\message.txt

View File

@@ -10,28 +10,6 @@
Submitted by: https://github.com/dinger1986 Submitted by: https://github.com/dinger1986
#> #>
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$regpath = HKCU:\Software\Microsoft\Office\16.0\Outlook\Preferences
$regname = DelegateSentItemsStyle
$regvalue = 1
$regproperty = Dword
If (!(test-path '%ProgramData%\Tactical RMM\temp')) {
New-Item -ItemType Directory -Force -Path '%ProgramData%\Tactical RMM\temp'
}
If (!(test-path C:\TEMP\curpsxpolicy.txt)) {
$curexpolicy = Get-ExecutionPolicy
(
echo $curexpolicy
)>"%ProgramData%\Tactical RMM\temp\curpsxpolicy.txt"
}
Set-ExecutionPolicy -ExecutionPolicy Unrestricted
if (Get-PackageProvider -Name NuGet) { if (Get-PackageProvider -Name NuGet) {
Write-Output "NuGet Already Added" Write-Output "NuGet Already Added"
} }
@@ -48,14 +26,30 @@ else {
Install-Module -Name RunAsUser -Force Install-Module -Name RunAsUser -Force
} }
If (!(test-path $env:programdata\TacticalRMM\temp\)) {
New-Item -ItemType Directory -Force -Path $env:programdata\TacticalRMM\temp\
}
If (!(test-path $env:programdata\TacticalRMM\temp\curpsxpolicy.txt)) {
$curexpolicy = Get-ExecutionPolicy
(
Write-Output $curexpolicy
)>$env:programdata\TacticalRMM\temp\curpsxpolicy.txt
}
Set-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell -Name ExecutionPolicy -Value Unrestricted
Invoke-AsCurrentUser -scriptblock { Invoke-AsCurrentUser -scriptblock {
New-ItemProperty -Path "$regpath" -Name "$regname" -Value "$regvalue" -PropertyType "$regproperty" # Modify below for other versions of Office
$regpath = 'Software\Microsoft\Office\16.0\Outlook\Preferences'
$regname = "DelegateSentItemsStyle"
$regvalue = "1"
$regproperty = "Dword"
New-ItemProperty -Path HKCU:\$regpath -Name $regname -Value $regvalue -PropertyType $regproperty
} }
Write-Output "Successfully changed Sent Items for Delegated folders" Write-Output "Successfully changed Sent Items for Delegated folders"
$curpsxpol = Get-Content -Path "%ProgramData%\Tactical RMM\temp\curpsxpolicy.txt"; $curpsxpol = Get-Content -Path $env:programdata\TacticalRMM\temp\curpsxpolicy.txt;
Set-ExecutionPolicy -ExecutionPolicy $curpsxpol Set-ExecutionPolicy -ExecutionPolicy $curpsxpol
del "%ProgramData%\Tactical RMM\temp\curpsxpolicy.txt"

View File

@@ -0,0 +1,26 @@
<#
Requires global variables for serviceName "ScreenConnectService"
serviceName is the name of the ScreenConnect Service once it is installed EG: "ScreenConnect Client (1327465grctq84yrtocq)"
Variable value must start and end with " (Prior to TRMM Version 0.6.5), remove / don't use " on TRMM Version 0.6.5 or later.
Requires Custom Fields Agent entry Name: ScreenConnectGUID Type: text
URL Action entry (check your screenconnect to see what folder name is your "All Machines" folder): https://YOURNAME.screenconnect.com/Host#Access/All%20Machines//{{agent.ScreenConnectGUID}}/Join
or https://YOURNAME.screenconnect.com/Host#Access/All%20Machines%20by%20Company//{{agent.ScreenConnectGUID}}/Join
#>
param (
[string] $serviceName
)
if (!$serviceName) {
write-output "Variable not specified ScreenConnectService, please create a global custom field under Client called ScreenConnectService, Example Value: `"ScreenConnect Client (1327465grctq84yrtocq)`" `n"
$ErrorCount += 1
}
if (!$ErrorCount -eq 0) {
exit 1
}
$imagePath = Get-Itempropertyvalue "HKLM:\SYSTEM\ControlSet001\Services\$serviceName" -Name "ImagePath"
$imagePath2 = ($imagePath -split "&s=")[1]
$machineGUID = ($imagePath2 -split "&k=")[0]
Write-Output $machineGUID

View File

@@ -1,18 +0,0 @@
# Needs to be parameterized: $icofile, $URL, $ShortcutPath
# Need to change paths and test/create if don't exist
wget "https://www.example.com/logos/example.ico" -outfile "c:\agent\example.ico"
$WshShell = New-Object -comObject WScript.Shell
$path = "C:\Users\All Users\desktop\example.url"
$targetpath = "https://example.com/"
$iconlocation = "c:\agent\example.ico"
$iconfile = "IconFile=" + $iconlocation
$Shortcut = $WshShell.CreateShortcut($path)
$Shortcut.TargetPath = $targetpath
$Shortcut.Save()
Add-Content $path "HotKey=0"
Add-Content $path "$iconfile"
Add-Content $path "IconIndex=0"

View File

@@ -1,3 +1,5 @@
REM Print Spooler reset script. Will stop spooler, fix permissions on print folders, clear all files in print queues, and restart spooler service.
REM Stop Print Spooler REM Stop Print Spooler
net stop "Spooler" net stop "Spooler"

View File

@@ -1,2 +0,0 @@
net stop "Print Spooler"
net start "Print Spooler"

View File

@@ -0,0 +1,25 @@
<#
.Synopsis
Restart Print Spooler Service
.DESCRIPTION
Will force-restart the spooler service. With additional command parameter will also delete any pending print jobs
.EXAMPLE
Another example of how to use this cmdlet
.OUTPUTS
Any print jobs that are deleted
.NOTES
v1.0 5/2021
https://github.com/silversword411
.FUNCTIONALITY
Print Spooler Troubleshooting, restarts spooler service. Can also delete all print jobs that are pending
#>
#Restart Spooler service
Restart-Service -Name spooler -Force
#Deletes All print jobs within the last 15 years
$PrintJobs = get-wmiobject -class "Win32_PrintJob" -namespace "root\CIMV2" -computername . | Where-Object { [System.Management.ManagementDateTimeConverter]::ToDateTime($_.TimeSubmitted) -lt (Get-Date).AddDays(-5500) }
foreach ($job in $PrintJobs) {
# Write-Host "Canceling job $($job.JobId)"
$job.Delete()
}

View File

@@ -1,8 +0,0 @@
#Update with command parameters
#Deletes All print jobs within the last 2 days
$PrintJobs = get-wmiobject -class "Win32_PrintJob" -namespace "root\CIMV2" -computername . | Where-Object { [System.Management.ManagementDateTimeConverter]::ToDateTime($_.TimeSubmitted) -lt (Get-Date).AddDays(-2) }
foreach ($job in $PrintJobs) {
# Write-Host "Canceling job $($job.JobId)"
$job.Delete()
}

View File

@@ -1,7 +1,9 @@
md -Path 'C:\agent' -Force
wget "http://www.yourwebsite.com/logos/yourico.ico" -outfile "c:\agent\yourico.ico"
mkdir -Path 'C:\agent' -Force
Invoke-WebRequest "http://www.yourwebsite.com/logos/yourico.ico" -outfile "c:\agent\yourico.ico"
$WshShell = New-Object -comObject WScript.Shell $WshShell = New-Object -comObject WScript.Shell
$path = "C:\Users\All Users\desktop\Jaxsupport.url" $path = "C:\Users\All Users\desktop\Shortcut.url"
$targetpath = "https://yourwebsite.com" $targetpath = "https://yourwebsite.com"
$iconlocation = "c:\agent\yourico.ico" $iconlocation = "c:\agent\yourico.ico"
$iconfile = "IconFile=" + $iconlocation $iconfile = "IconFile=" + $iconlocation

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
SCRIPT_VERSION="121" SCRIPT_VERSION="123"
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/update.sh' SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/update.sh'
LATEST_SETTINGS_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/api/tacticalrmm/tacticalrmm/settings.py' LATEST_SETTINGS_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/api/tacticalrmm/tacticalrmm/settings.py'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
@@ -184,14 +184,14 @@ if ! [[ $HAS_PY39 ]]; then
sudo rm -rf Python-3.9.2 Python-3.9.2.tgz sudo rm -rf Python-3.9.2 Python-3.9.2.tgz
fi fi
HAS_NATS220=$(/usr/local/bin/nats-server -version | grep v2.2.3) HAS_NATS220=$(/usr/local/bin/nats-server -version | grep v2.2.6)
if ! [[ $HAS_NATS220 ]]; then if ! [[ $HAS_NATS220 ]]; then
printf >&2 "${GREEN}Updating nats to v2.2.3${NC}\n" printf >&2 "${GREEN}Updating nats to v2.2.6${NC}\n"
nats_tmp=$(mktemp -d -t nats-XXXXXXXXXX) nats_tmp=$(mktemp -d -t nats-XXXXXXXXXX)
wget https://github.com/nats-io/nats-server/releases/download/v2.2.3/nats-server-v2.2.3-linux-amd64.tar.gz -P ${nats_tmp} wget https://github.com/nats-io/nats-server/releases/download/v2.2.6/nats-server-v2.2.6-linux-amd64.tar.gz -P ${nats_tmp}
tar -xzf ${nats_tmp}/nats-server-v2.2.3-linux-amd64.tar.gz -C ${nats_tmp} tar -xzf ${nats_tmp}/nats-server-v2.2.6-linux-amd64.tar.gz -C ${nats_tmp}
sudo rm -f /usr/local/bin/nats-server sudo rm -f /usr/local/bin/nats-server
sudo mv ${nats_tmp}/nats-server-v2.2.3-linux-amd64/nats-server /usr/local/bin/ sudo mv ${nats_tmp}/nats-server-v2.2.6-linux-amd64/nats-server /usr/local/bin/
sudo chmod +x /usr/local/bin/nats-server sudo chmod +x /usr/local/bin/nats-server
sudo chown ${USER}:${USER} /usr/local/bin/nats-server sudo chown ${USER}:${USER} /usr/local/bin/nats-server
rm -rf ${nats_tmp} rm -rf ${nats_tmp}
@@ -307,5 +307,9 @@ if [[ "${CURRENT_MESH_VER}" != "${LATEST_MESH_VER}" ]] || [[ "$force" = true ]];
sudo systemctl start meshcentral sudo systemctl start meshcentral
fi fi
# apply redis configuration
sudo redis-cli config set appendonly yes
sudo redis-cli config rewrite
rm -f $TMP_SETTINGS rm -f $TMP_SETTINGS
printf >&2 "${GREEN}Update finished!${NC}\n" printf >&2 "${GREEN}Update finished!${NC}\n"

1996
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,33 +10,27 @@
"test:e2e:ci": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress run\"" "test:e2e:ci": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress run\""
}, },
"dependencies": { "dependencies": {
"@quasar/extras": "^1.10.4", "@quasar/extras": "^1.10.6",
"apexcharts": "^3.23.1", "apexcharts": "^3.23.1",
"axios": "^0.21.1", "axios": "^0.21.1",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"prismjs": "^1.22.0", "prismjs": "^1.22.0",
"qrcode.vue": "^1.7.0", "qrcode.vue": "^1.7.0",
"quasar": "^1.15.13", "quasar": "^1.15.20",
"vue-apexcharts": "^1.6.0", "vue-apexcharts": "^1.6.0",
"vue-prism-editor": "^1.2.2" "vue-prism-editor": "^1.2.2"
}, },
"devDependencies": { "devDependencies": {
"@quasar/app": "^2.2.6", "@quasar/app": "^2.2.10",
"@quasar/cli": "^1.1.3", "@quasar/cli": "^1.2.1",
"core-js": "^3.11.2", "core-js": "^3.14.0",
"eslint-plugin-cypress": "^2.11.2", "eslint-plugin-cypress": "^2.11.2",
"flush-promises": "^1.0.2", "flush-promises": "^1.0.2",
"fs-extra": "^9.1.0" "fs-extra": "^9.1.0"
}, },
"browserslist": [ "browserslist": [
"last 4 Chrome versions", "last 3 Chrome versions",
"last 4 Firefox versions", "last 3 Firefox versions",
"last 2 Edge versions", "last 2 Edge versions"
"last 3 Safari versions",
"last 3 Android versions",
"last 3 ChromeAndroid versions",
"last 3 FirefoxAndroid versions",
"last 3 iOS versions",
"last 2 Opera versions"
] ]
} }

View File

@@ -261,6 +261,9 @@
@click="eventLogMoreInfo(props.row)" @click="eventLogMoreInfo(props.row)"
>Last Output</span >Last Output</span
> >
<span v-else-if="props.row.check_type === 'diskspace' || props.row.check_type === 'winsvc'">{{
props.row.more_info
}}</span>
</q-td> </q-td>
<q-td>{{ props.row.last_run || "Never" }}</q-td> <q-td>{{ props.row.last_run || "Never" }}</q-td>
<q-td v-if="props.row.assigned_task !== null && props.row.assigned_task.length > 1" <q-td v-if="props.row.assigned_task !== null && props.row.assigned_task.length > 1"

View File

@@ -12,6 +12,7 @@
@input="value => $emit('input', value)" @input="value => $emit('input', value)"
:rules="[...validationRules]" :rules="[...validationRules]"
reactive-rules reactive-rules
autogrow
/> />
<q-toggle <q-toggle

View File

@@ -94,7 +94,7 @@
<q-item-section>Global Settings</q-item-section> <q-item-section>Global Settings</q-item-section>
</q-item> </q-item>
<!-- code sign --> <!-- code sign -->
<q-item v-if="!noCodeSigning" clickable v-close-popup @click="showCodeSign = true"> <q-item v-if="!hosted" clickable v-close-popup @click="showCodeSign = true">
<q-item-section>Code Signing</q-item-section> <q-item-section>Code Signing</q-item-section>
</q-item> </q-item>
</q-list> </q-list>
@@ -124,7 +124,7 @@
</q-menu> </q-menu>
</q-btn> </q-btn>
<!-- help --> <!-- help -->
<q-btn size="md" dense no-caps flat label="Help"> <q-btn v-if="!hosted" size="md" dense no-caps flat label="Help">
<q-menu auto-close> <q-menu auto-close>
<q-list dense style="min-width: 100px"> <q-list dense style="min-width: 100px">
<q-item clickable v-close-popup @click="openHelp('docs')"> <q-item clickable v-close-popup @click="openHelp('docs')">
@@ -272,8 +272,8 @@ export default {
}; };
}, },
computed: { computed: {
noCodeSigning() { hosted() {
return this.$store.state.noCodeSign; return this.$store.state.hosted;
}, },
}, },
methods: { methods: {

View File

@@ -136,7 +136,7 @@ export default {
getOptions() { getOptions() {
this.getClients(); this.getClients();
this.getSites(); this.getSites();
this.agentOptions = Object.freeze(this.getAgentOptions()); this.agentOptions = this.getAgentOptions();
}, },
show() { show() {
this.$refs.dialog.show(); this.$refs.dialog.show();
@@ -152,7 +152,7 @@ export default {
this.hide(); this.hide();
}, },
}, },
mounted() { created() {
this.getOptions(); this.getOptions();
// copy prop data locally // copy prop data locally

View File

@@ -143,7 +143,7 @@ export default {
getOptions() { getOptions() {
this.getClients(); this.getClients();
this.getSites(); this.getSites();
this.agentOptions = Object.freeze(this.getAgentOptions()); this.agentOptions = this.getAgentOptions();
}, },
show() { show() {
this.$refs.dialog.show(); this.$refs.dialog.show();
@@ -159,7 +159,7 @@ export default {
this.hide(); this.hide();
}, },
}, },
mounted() { created() {
this.getOptions(); this.getOptions();
// copy prop data locally // copy prop data locally

View File

@@ -111,6 +111,7 @@
dense dense
options-dense options-dense
outlined outlined
clearable
v-model="template.action" v-model="template.action"
:options="scriptOptions" :options="scriptOptions"
map-options map-options
@@ -174,6 +175,7 @@
dense dense
options-dense options-dense
outlined outlined
clearable
v-model="template.resolved_action" v-model="template.resolved_action"
:options="scriptOptions" :options="scriptOptions"
map-options map-options
@@ -696,4 +698,4 @@ export default {
if (this.editing) Object.assign(this.template, this.alertTemplate); if (this.editing) Object.assign(this.template, this.alertTemplate);
}, },
}; };
</script> </script>

View File

@@ -19,6 +19,7 @@
stack-label stack-label
filled filled
counter counter
class="full-width"
accept=".exe" accept=".exe"
> >
<template v-slot:prepend> <template v-slot:prepend>

View File

@@ -138,6 +138,7 @@
v-model="localField.default_value_string" v-model="localField.default_value_string"
:rules="[...defaultValueRules]" :rules="[...defaultValueRules]"
reactive-rules reactive-rules
autogrow
/> />
</q-card-section> </q-card-section>
<q-card-section> <q-card-section>

View File

@@ -46,6 +46,7 @@
stack-label stack-label
filled filled
counter counter
class="full-width"
accept=".ps1, .bat, .py" accept=".ps1, .bat, .py"
> >
<template v-slot:prepend> <template v-slot:prepend>

View File

@@ -73,7 +73,12 @@
label="Collector Task" label="Collector Task"
v-model="collector" v-model="collector"
class="q-pb-sm" class="q-pb-sm"
@input="autotask.custom_field = null" @input="
() => {
autotask.custom_field = null;
autotask.collector_all_ouput = false;
}
"
/> />
<q-select <q-select
v-if="collector" v-if="collector"
@@ -87,6 +92,13 @@
options-dense options-dense
hint="The last line of script output will be saved to custom field selected" hint="The last line of script output will be saved to custom field selected"
/> />
<q-checkbox
v-if="collector"
dense
label="Save all output (Only for text area)"
v-model="autotask.collector_all_output"
class="q-py-sm"
/>
</q-card-section> </q-card-section>
<q-card-section> <q-card-section>
<q-select <q-select
@@ -229,6 +241,7 @@ export default {
task_type: "scheduled", task_type: "scheduled",
timeout: 120, timeout: 120,
alert_severity: "info", alert_severity: "info",
collector_all_output: false,
}, },
policyChecks: [], policyChecks: [],
severityOptions: [ severityOptions: [

View File

@@ -90,6 +90,13 @@
options-dense options-dense
hint="The return value of script will be saved to custom field selected" hint="The return value of script will be saved to custom field selected"
/> />
<q-checkbox
v-if="collector"
dense
label="Save all output (Only for text area)"
v-model="autotask.collector_all_output"
class="q-py-sm"
/>
</q-card-section> </q-card-section>
<q-card-section> <q-card-section>
<q-input <q-input
@@ -131,6 +138,7 @@ export default {
alert_severity: null, alert_severity: null,
timeout: 120, timeout: 120,
custom_field: null, custom_field: null,
collector_all_output: false,
}, },
collector: false, collector: false,
customFieldOptions: [], customFieldOptions: [],
@@ -197,6 +205,7 @@ export default {
this.autotask.alert_severity = this.task.alert_severity; this.autotask.alert_severity = this.task.alert_severity;
this.autotask.timeout = this.task.timeout; this.autotask.timeout = this.task.timeout;
this.autotask.custom_field = this.task.custom_field; this.autotask.custom_field = this.task.custom_field;
this.autotask.collector_all_output = this.task.collector_all_output;
}, },
}; };
</script> </script>

View File

@@ -72,18 +72,18 @@ export default {
isValidThreshold(warning, error, diskcheck = false) { isValidThreshold(warning, error, diskcheck = false) {
if (warning === 0 && error === 0) { if (warning === 0 && error === 0) {
Notify.create(notifyErrorConfig("Warning Threshold or Error Threshold need to be set", 2000)); Notify.create({ type: "negative", timeout: 2000, message: "Warning Threshold or Error Threshold need to be set" });
return false return false;
} }
if (!diskcheck && warning > error && warning > 0 && error > 0) { if (!diskcheck && warning > error && warning > 0 && error > 0) {
Notify.create(notifyErrorConfig("Warning Threshold must be less than Error Threshold", 2000)); Notify.create({ type: "negative", timeout: 2000, message: "Warning Threshold must be less than Error Threshold" });
return false return false;
} }
if (diskcheck && warning < error && warning > 0 && error > 0) { if (diskcheck && warning < error && warning > 0 && error > 0) {
Notify.create(notifyErrorConfig("Warning Threshold must be more than Error Threshold", 2000)); Notify.create({ type: "negative", timeout: 2000, message: "Warning Threshold must be more than Error Threshold" });
return false return false;
} }
return true; return true;

View File

@@ -35,7 +35,7 @@ export default function () {
defaultAgentTblTab: "server", defaultAgentTblTab: "server",
clientTreeSort: "alphafail", clientTreeSort: "alphafail",
clientTreeSplitter: 11, clientTreeSplitter: 11,
noCodeSign: false, hosted: false,
}, },
getters: { getters: {
clientTreeSplitterModel(state) { clientTreeSplitterModel(state) {
@@ -159,8 +159,8 @@ export default function () {
SET_CLIENT_TREE_SORT(state, val) { SET_CLIENT_TREE_SORT(state, val) {
state.clientTreeSort = val state.clientTreeSort = val
}, },
SET_NO_CODE_SIGN(state, val) { SET_HOSTED(state, val) {
state.noCodeSign = val state.hosted = val
} }
}, },
actions: { actions: {

View File

@@ -742,7 +742,7 @@ export default {
this.$store.commit("SET_AGENT_DBLCLICK_ACTION", r.data.dbl_click_action); this.$store.commit("SET_AGENT_DBLCLICK_ACTION", r.data.dbl_click_action);
this.$store.commit("SET_URL_ACTION", r.data.url_action); this.$store.commit("SET_URL_ACTION", r.data.url_action);
this.$store.commit("setShowCommunityScripts", r.data.show_community_scripts); this.$store.commit("setShowCommunityScripts", r.data.show_community_scripts);
this.$store.commit("SET_NO_CODE_SIGN", r.data.no_code_sign); this.$store.commit("SET_HOSTED", r.data.hosted);
}); });
}, },
showToggleMaintenance(node) { showToggleMaintenance(node) {

View File

@@ -37,6 +37,7 @@
stack-label stack-label
filled filled
counter counter
class="full-width"
accept=".exe" accept=".exe"
> >
<template v-slot:prepend> <template v-slot:prepend>