Compare commits

..

25 Commits

Author SHA1 Message Date
wh1te909
59c880dc36 Release 0.19.4 2024-10-23 17:25:12 +00:00
wh1te909
e5c355e8f9 bump version 2024-10-23 17:23:00 +00:00
wh1te909
d36fadf3ca update wording 2024-10-23 17:22:48 +00:00
wh1te909
b618cbdf7c update reqs 2024-10-23 00:56:57 +00:00
wh1te909
15ec7173aa bump web vers 2024-10-23 00:56:46 +00:00
wh1te909
4166e92754 don't trim script whitespace 2024-10-18 06:20:13 +00:00
wh1te909
85166b6e8b add run on server option to run script endpoint #1923 2024-10-17 20:27:15 +00:00
wh1te909
5278599675 update nats 2024-10-17 20:26:04 +00:00
wh1te909
18cac8ba5d show more detail in checks tab #2014 2024-10-15 08:31:06 +00:00
wh1te909
dfccbceea6 bump mesh 2024-10-15 08:28:38 +00:00
wh1te909
fc4b651e46 change to match standard install 2024-10-15 08:25:22 +00:00
wh1te909
fb89922ecf format 2024-10-15 08:24:07 +00:00
wh1te909
8ab23c8cd9 update reqs 2024-10-13 19:51:43 +00:00
wh1te909
787a2c5071 add separate perms for global keystore #1984 2024-10-06 05:58:15 +00:00
wh1te909
da76a20345 forgot to add migration 2024-10-06 03:06:31 +00:00
wh1te909
9688dbdb36 add saving output of bulk script to custom field and agent note closes #1845 2024-10-06 01:49:27 +00:00
wh1te909
6fa16e1a5e update req 2024-10-05 20:25:40 +00:00
wh1te909
71a2e3cfca remove extra mgmt cmd 2024-09-30 19:27:14 +00:00
wh1te909
e9c0f7e200 update reqs 2024-09-30 08:20:22 +00:00
wh1te909
25154a4331 update nats 2024-09-30 07:21:32 +00:00
wh1te909
22c152f600 update reqs 2024-09-04 09:32:37 +00:00
Dan
3eab61cbc3 Merge pull request #1980 from cdp1337/community-scripts-245
Proposed work for amidaware/community-scripts#245
2024-08-20 12:49:17 -07:00
wh1te909
a029c1d0db set alert template when moving site to another client fixes #1975 2024-08-15 18:40:19 +00:00
Charlie Powell
706757d215 Black didn't like the format of that line
whatever, quick fix.
2024-08-15 00:57:04 -04:00
Charlie Powell
9054c233f4 Proposed work for amidaware/community-scripts#245
Modify the load_community_scripts logic to add
env and run_as_user keys.
2024-08-15 00:41:04 -04:00
23 changed files with 296 additions and 62 deletions

View File

@@ -39,7 +39,7 @@ Demo database resets every hour. A lot of features are disabled for obvious reas
## Mac agent versions supported ## Mac agent versions supported
- 64 bit Intel and Apple Silicon (M1, M2) - 64 bit Intel and Apple Silicon (M-Series)
## Installation / Backup / Restore / Usage ## Installation / Backup / Restore / Usage

View File

@@ -0,0 +1,23 @@
# Generated by Django 4.2.16 on 2024-10-06 05:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("accounts", "0037_role_can_run_server_scripts_role_can_use_webterm"),
]
operations = [
migrations.AddField(
model_name="role",
name="can_edit_global_keystore",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="role",
name="can_view_global_keystore",
field=models.BooleanField(default=False),
),
]

View File

@@ -131,6 +131,8 @@ class Role(BaseAuditModel):
can_manage_customfields = models.BooleanField(default=False) can_manage_customfields = models.BooleanField(default=False)
can_run_server_scripts = models.BooleanField(default=False) can_run_server_scripts = models.BooleanField(default=False)
can_use_webterm = models.BooleanField(default=False) can_use_webterm = models.BooleanField(default=False)
can_view_global_keystore = models.BooleanField(default=False)
can_edit_global_keystore = models.BooleanField(default=False)
# checks # checks
can_list_checks = models.BooleanField(default=False) can_list_checks = models.BooleanField(default=False)

View File

@@ -0,0 +1,36 @@
# Generated by Django 4.2.16 on 2024-10-05 20:39
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("core", "0047_alter_coresettings_notify_on_warning_alerts"),
("agents", "0059_alter_agenthistory_id"),
]
operations = [
migrations.AddField(
model_name="agenthistory",
name="collector_all_output",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="agenthistory",
name="custom_field",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="history",
to="core.customfield",
),
),
migrations.AddField(
model_name="agenthistory",
name="save_to_agent_note",
field=models.BooleanField(default=False),
),
]

View File

@@ -1122,6 +1122,15 @@ class AgentHistory(models.Model):
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
) )
script_results = models.JSONField(null=True, blank=True) script_results = models.JSONField(null=True, blank=True)
custom_field = models.ForeignKey(
"core.CustomField",
null=True,
blank=True,
related_name="history",
on_delete=models.SET_NULL,
)
collector_all_output = models.BooleanField(default=False)
save_to_agent_note = models.BooleanField(default=False)
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.agent.hostname} - {self.type}" return f"{self.agent.hostname} - {self.type}"

View File

@@ -2,7 +2,7 @@ import json
import os import os
from itertools import cycle from itertools import cycle
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from unittest.mock import patch from unittest.mock import PropertyMock, patch
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
from django.conf import settings from django.conf import settings
@@ -768,6 +768,67 @@ class TestAgentViews(TacticalTestCase):
self.assertEqual(Note.objects.get(agent=self.agent).note, "ok") self.assertEqual(Note.objects.get(agent=self.agent).note, "ok")
# test run on server
with patch("core.utils.run_server_script") as mock_run_server_script:
mock_run_server_script.return_value = ("output", "error", 1.23456789, 0)
data = {
"script": script.pk,
"output": "wait",
"args": ["arg1", "arg2"],
"timeout": 15,
"run_as_user": False,
"env_vars": ["key1=val1", "key2=val2"],
"run_on_server": True,
}
r = self.client.post(url, data, format="json")
self.assertEqual(r.status_code, 200)
hist = AgentHistory.objects.filter(agent=self.agent, script=script).last()
if not hist:
raise AgentHistory.DoesNotExist
mock_run_server_script.assert_called_with(
body=script.script_body,
args=script.parse_script_args(self.agent, script.shell, data["args"]),
env_vars=script.parse_script_env_vars(
self.agent, script.shell, data["env_vars"]
),
shell=script.shell,
timeout=18,
)
expected_ret = {
"stdout": "output",
"stderr": "error",
"execution_time": "1.2346",
"retcode": 0,
}
self.assertEqual(r.data, expected_ret)
hist.refresh_from_db()
expected_script_results = {**expected_ret, "id": hist.pk}
self.assertEqual(hist.script_results, expected_script_results)
# test run on server with server scripts disabled
with patch(
"core.models.CoreSettings.server_scripts_enabled",
new_callable=PropertyMock,
) as server_scripts_enabled:
server_scripts_enabled.return_value = False
data = {
"script": script.pk,
"output": "wait",
"args": ["arg1", "arg2"],
"timeout": 15,
"run_as_user": False,
"env_vars": ["key1=val1", "key2=val2"],
"run_on_server": True,
}
r = self.client.post(url, data, format="json")
self.assertEqual(r.status_code, 400)
def test_get_notes(self): def test_get_notes(self):
url = f"{base_url}/notes/" url = f"{base_url}/notes/"

View File

@@ -768,6 +768,10 @@ def run_script(request, agent_id):
run_as_user: bool = request.data["run_as_user"] run_as_user: bool = request.data["run_as_user"]
env_vars: list[str] = request.data["env_vars"] env_vars: list[str] = request.data["env_vars"]
req_timeout = int(request.data["timeout"]) + 3 req_timeout = int(request.data["timeout"]) + 3
run_on_server: bool | None = request.data.get("run_on_server")
if run_on_server and not get_core_settings().server_scripts_enabled:
return notify_error("This feature is disabled.")
AuditLog.audit_script_run( AuditLog.audit_script_run(
username=request.user.username, username=request.user.username,
@@ -784,6 +788,29 @@ def run_script(request, agent_id):
) )
history_pk = hist.pk history_pk = hist.pk
if run_on_server:
from core.utils import run_server_script
r = run_server_script(
body=script.script_body,
args=script.parse_script_args(agent, script.shell, args),
env_vars=script.parse_script_env_vars(agent, script.shell, env_vars),
shell=script.shell,
timeout=req_timeout,
)
ret = {
"stdout": r[0],
"stderr": r[1],
"execution_time": "{:.4f}".format(r[2]),
"retcode": r[3],
}
hist.script_results = {**ret, "id": history_pk}
hist.save(update_fields=["script_results"])
return Response(ret)
if output == "wait": if output == "wait":
r = agent.run_script( r = agent.run_script(
scriptpk=script.pk, scriptpk=script.pk,
@@ -1008,6 +1035,16 @@ def bulk(request):
elif request.data["mode"] == "script": elif request.data["mode"] == "script":
script = get_object_or_404(Script, pk=request.data["script"]) script = get_object_or_404(Script, pk=request.data["script"])
# prevent API from breaking for those who haven't updated payload
try:
custom_field_pk = request.data["custom_field"]
collector_all_output = request.data["collector_all_output"]
save_to_agent_note = request.data["save_to_agent_note"]
except KeyError:
custom_field_pk = None
collector_all_output = False
save_to_agent_note = False
bulk_script_task.delay( bulk_script_task.delay(
script_pk=script.pk, script_pk=script.pk,
agent_pks=agents, agent_pks=agents,
@@ -1016,6 +1053,9 @@ def bulk(request):
username=request.user.username[:50], username=request.user.username[:50],
run_as_user=request.data["run_as_user"], run_as_user=request.data["run_as_user"],
env_vars=request.data["env_vars"], env_vars=request.data["env_vars"],
custom_field_pk=custom_field_pk,
collector_all_output=collector_all_output,
save_to_agent_note=save_to_agent_note,
) )
return Response(f"{script.name} will now be run on {len(agents)} agents. {ht}") return Response(f"{script.name} will now be run on {len(agents)} agents. {ht}")

View File

@@ -12,7 +12,7 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from accounts.models import User from accounts.models import User
from agents.models import Agent, AgentHistory from agents.models import Agent, AgentHistory, Note
from agents.serializers import AgentHistorySerializer from agents.serializers import AgentHistorySerializer
from alerts.tasks import cache_agents_alert_template from alerts.tasks import cache_agents_alert_template
from apiv3.utils import get_agent_config from apiv3.utils import get_agent_config
@@ -40,6 +40,7 @@ from tacticalrmm.constants import (
AuditActionType, AuditActionType,
AuditObjType, AuditObjType,
CheckStatus, CheckStatus,
CustomFieldModel,
DebugLogType, DebugLogType,
GoArch, GoArch,
MeshAgentIdent, MeshAgentIdent,
@@ -581,11 +582,39 @@ class AgentHistoryResult(APIView):
request.data["script_results"]["retcode"] = 1 request.data["script_results"]["retcode"] = 1
hist = get_object_or_404( hist = get_object_or_404(
AgentHistory.objects.filter(agent__agent_id=agentid), pk=pk AgentHistory.objects.select_related("custom_field").filter(
agent__agent_id=agentid
),
pk=pk,
) )
s = AgentHistorySerializer(instance=hist, data=request.data, partial=True) s = AgentHistorySerializer(instance=hist, data=request.data, partial=True)
s.is_valid(raise_exception=True) s.is_valid(raise_exception=True)
s.save() s.save()
if hist.custom_field:
if hist.custom_field.model == CustomFieldModel.AGENT:
field = hist.custom_field.get_or_create_field_value(hist.agent)
elif hist.custom_field.model == CustomFieldModel.CLIENT:
field = hist.custom_field.get_or_create_field_value(hist.agent.client)
elif hist.custom_field.model == CustomFieldModel.SITE:
field = hist.custom_field.get_or_create_field_value(hist.agent.site)
r = request.data["script_results"]["stdout"]
value = (
r.strip()
if hist.collector_all_output
else r.strip().split("\n")[-1].strip()
)
field.save_to_field(value)
if hist.save_to_agent_note:
Note.objects.create(
agent=hist.agent,
user=request.user,
note=request.data["script_results"]["stdout"],
)
return Response("ok") return Response("ok")

View File

@@ -365,9 +365,11 @@ class CheckResult(models.Model):
if len(self.history) > 15: if len(self.history) > 15:
self.history = self.history[-15:] self.history = self.history[-15:]
update_fields.extend(["history"]) update_fields.extend(["history", "more_info"])
avg = int(mean(self.history)) avg = int(mean(self.history))
txt = "Memory Usage" if check.check_type == CheckType.MEMORY else "CPU Load"
self.more_info = f"Average {txt}: {avg}%"
if check.error_threshold and avg > check.error_threshold: if check.error_threshold and avg > check.error_threshold:
self.status = CheckStatus.FAILING self.status = CheckStatus.FAILING

View File

@@ -133,6 +133,7 @@ class Site(BaseAuditModel):
old_site.alert_template != self.alert_template old_site.alert_template != self.alert_template
or old_site.workstation_policy != self.workstation_policy or old_site.workstation_policy != self.workstation_policy
or old_site.server_policy != self.server_policy or old_site.server_policy != self.server_policy
or old_site.client != self.client
): ):
cache_agents_alert_template.delay() cache_agents_alert_template.delay()

View File

@@ -11,6 +11,14 @@ class CoreSettingsPerms(permissions.BasePermission):
return _has_perm(r, "can_edit_core_settings") return _has_perm(r, "can_edit_core_settings")
class GlobalKeyStorePerms(permissions.BasePermission):
def has_permission(self, r, view) -> bool:
if r.method == "GET":
return _has_perm(r, "can_view_global_keystore")
return _has_perm(r, "can_edit_global_keystore")
class URLActionPerms(permissions.BasePermission): class URLActionPerms(permissions.BasePermission):
def has_permission(self, r, view) -> bool: def has_permission(self, r, view) -> bool:
if r.method in {"GET", "PATCH"}: if r.method in {"GET", "PATCH"}:

View File

@@ -43,6 +43,7 @@ from .permissions import (
CodeSignPerms, CodeSignPerms,
CoreSettingsPerms, CoreSettingsPerms,
CustomFieldPerms, CustomFieldPerms,
GlobalKeyStorePerms,
RunServerScriptPerms, RunServerScriptPerms,
ServerMaintPerms, ServerMaintPerms,
URLActionPerms, URLActionPerms,
@@ -310,7 +311,7 @@ class CodeSign(APIView):
class GetAddKeyStore(APIView): class GetAddKeyStore(APIView):
permission_classes = [IsAuthenticated, CoreSettingsPerms] permission_classes = [IsAuthenticated, GlobalKeyStorePerms]
def get(self, request): def get(self, request):
keys = GlobalKVStore.objects.all() keys = GlobalKVStore.objects.all()
@@ -325,7 +326,7 @@ class GetAddKeyStore(APIView):
class UpdateDeleteKeyStore(APIView): class UpdateDeleteKeyStore(APIView):
permission_classes = [IsAuthenticated, CoreSettingsPerms] permission_classes = [IsAuthenticated, GlobalKeyStorePerms]
def put(self, request, pk): def put(self, request, pk):
key = get_object_or_404(GlobalKVStore, pk=pk) key = get_object_or_404(GlobalKVStore, pk=pk)

View File

@@ -1,47 +1,46 @@
adrf==0.1.6
asgiref==3.8.1 asgiref==3.8.1
celery==5.4.0 celery==5.4.0
certifi==2024.7.4 certifi==2024.8.30
cffi==1.16.0 cffi==1.17.1
channels==4.1.0 channels==4.1.0
channels_redis==4.2.0 channels_redis==4.2.0
cryptography==42.0.8 cryptography==43.0.3
Django==4.2.14 Django==4.2.16
django-cors-headers==4.4.0 django-cors-headers==4.5.0
django-filter==24.2 django-filter==24.3
django-rest-knox==4.2.0 django-rest-knox==4.2.0
djangorestframework==3.15.2 djangorestframework==3.15.2
drf-spectacular==0.27.2 drf-spectacular==0.27.2
hiredis==2.3.2 hiredis==2.3.2
kombu==5.3.7 kombu==5.3.7
meshctrl==0.1.15 meshctrl==0.1.15
msgpack==1.0.8 msgpack==1.1.0
nats-py==2.8.0 nats-py==2.9.0
packaging==24.1 packaging==24.1
psutil==5.9.8 psutil==6.0.0
psycopg[binary]==3.1.19 psycopg[binary]==3.2.3
pycparser==2.22 pycparser==2.22
pycryptodome==3.20.0 pycryptodome==3.21.0
pyotp==2.9.0 pyotp==2.9.0
pyparsing==3.1.2 pyparsing==3.1.4
python-ipware==2.0.2 python-ipware==2.0.2
qrcode==7.4.2 qrcode==8.0
redis==5.0.7 redis==5.0.8
requests==2.32.3 requests==2.32.3
six==1.16.0 six==1.16.0
sqlparse==0.5.0 sqlparse==0.5.1
twilio==8.13.0 twilio==8.13.0
urllib3==2.2.2 urllib3==2.2.3
uvicorn[standard]==0.30.1 uvicorn[standard]==0.31.1
uWSGI==2.0.26 uWSGI==2.0.27
validators==0.24.0 validators==0.24.0
vine==5.1.0 vine==5.1.0
websockets==12.0 websockets==13.1
zipp==3.19.2 zipp==3.20.2
pandas==2.2.2 pandas==2.2.3
kaleido==0.2.1 kaleido==0.2.1
jinja2==3.1.4 jinja2==3.1.4
markdown==3.6 markdown==3.7
plotly==5.22.0 plotly==5.24.1
weasyprint==62.3 weasyprint==62.3
ocxsect==0.1.5 ocxsect==0.1.5

View File

@@ -118,8 +118,14 @@ class Script(BaseAuditModel):
args = script["args"] if "args" in script.keys() else [] args = script["args"] if "args" in script.keys() else []
env = script["env"] if "env" in script.keys() else []
syntax = script["syntax"] if "syntax" in script.keys() else "" syntax = script["syntax"] if "syntax" in script.keys() else ""
run_as_user = (
script["run_as_user"] if "run_as_user" in script.keys() else False
)
supported_platforms = ( supported_platforms = (
script["supported_platforms"] script["supported_platforms"]
if "supported_platforms" in script.keys() if "supported_platforms" in script.keys()
@@ -135,7 +141,9 @@ class Script(BaseAuditModel):
i.shell = script["shell"] i.shell = script["shell"]
i.default_timeout = default_timeout i.default_timeout = default_timeout
i.args = args i.args = args
i.env_vars = env
i.syntax = syntax i.syntax = syntax
i.run_as_user = run_as_user
i.filename = script["filename"] i.filename = script["filename"]
i.supported_platforms = supported_platforms i.supported_platforms = supported_platforms
@@ -163,8 +171,10 @@ class Script(BaseAuditModel):
category=category, category=category,
default_timeout=default_timeout, default_timeout=default_timeout,
args=args, args=args,
env_vars=env,
filename=script["filename"], filename=script["filename"],
syntax=syntax, syntax=syntax,
run_as_user=run_as_user,
supported_platforms=supported_platforms, supported_platforms=supported_platforms,
) )
# new_script.hash_script_body() # also saves script # new_script.hash_script_body() # also saves script

View File

@@ -48,6 +48,7 @@ class ScriptSerializer(ModelSerializer):
"run_as_user", "run_as_user",
"env_vars", "env_vars",
] ]
extra_kwargs = {"script_body": {"trim_whitespace": False}}
class ScriptCheckSerializer(ModelSerializer): class ScriptCheckSerializer(ModelSerializer):
@@ -63,3 +64,4 @@ class ScriptSnippetSerializer(ModelSerializer):
class Meta: class Meta:
model = ScriptSnippet model = ScriptSnippet
fields = "__all__" fields = "__all__"
extra_kwargs = {"code": {"trim_whitespace": False}}

View File

@@ -54,12 +54,21 @@ def bulk_script_task(
username: str, username: str,
run_as_user: bool = False, run_as_user: bool = False,
env_vars: list[str] = [], env_vars: list[str] = [],
custom_field_pk: int | None,
collector_all_output: bool = False,
save_to_agent_note: bool = False,
) -> None: ) -> None:
script = Script.objects.get(pk=script_pk) script = Script.objects.get(pk=script_pk)
# always override if set on script model # always override if set on script model
if script.run_as_user: if script.run_as_user:
run_as_user = True run_as_user = True
custom_field = None
if custom_field_pk:
from core.models import CustomField
custom_field = CustomField.objects.get(pk=custom_field_pk)
items = [] items = []
agent: "Agent" agent: "Agent"
for agent in Agent.objects.filter(pk__in=agent_pks): for agent in Agent.objects.filter(pk__in=agent_pks):
@@ -68,6 +77,9 @@ def bulk_script_task(
type=AgentHistoryType.SCRIPT_RUN, type=AgentHistoryType.SCRIPT_RUN,
script=script, script=script,
username=username, username=username,
custom_field=custom_field,
collector_all_output=collector_all_output,
save_to_agent_note=save_to_agent_note,
) )
data = { data = {
"func": "runscriptfull", "func": "runscriptfull",

View File

@@ -21,21 +21,21 @@ MAC_UNINSTALL = BASE_DIR / "core" / "mac_uninstall.sh"
AUTH_USER_MODEL = "accounts.User" AUTH_USER_MODEL = "accounts.User"
# latest release # latest release
TRMM_VERSION = "0.19.3" TRMM_VERSION = "0.19.4"
# https://github.com/amidaware/tacticalrmm-web # https://github.com/amidaware/tacticalrmm-web
WEB_VERSION = "0.101.48" WEB_VERSION = "0.101.49"
# 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.194" APP_VER = "0.0.195"
# https://github.com/amidaware/rmmagent # https://github.com/amidaware/rmmagent
LATEST_AGENT_VER = "2.8.0" LATEST_AGENT_VER = "2.8.0"
MESH_VER = "1.1.21" MESH_VER = "1.1.32"
NATS_SERVER_VER = "2.10.17" NATS_SERVER_VER = "2.10.22"
# Install Nushell on the agent # Install Nushell on the agent
# https://github.com/nushell/nushell # https://github.com/nushell/nushell
@@ -81,10 +81,10 @@ INSTALL_DENO_URL = ""
DENO_DEFAULT_PERMISSIONS = "--allow-all" DENO_DEFAULT_PERMISSIONS = "--allow-all"
# for the update script, bump when need to recreate venv # for the update script, bump when need to recreate venv
PIP_VER = "44" PIP_VER = "45"
SETUPTOOLS_VER = "70.2.0" SETUPTOOLS_VER = "75.1.0"
WHEEL_VER = "0.43.0" WHEEL_VER = "0.44.0"
AGENT_BASE_URL = "https://agents.tacticalrmm.com" AGENT_BASE_URL = "https://agents.tacticalrmm.com"

View File

@@ -14,7 +14,7 @@ RUN MESH_VER=$(grep -o 'MESH_VER.*' /tmp/settings.py | cut -d'"' -f 2) && \
cat > package.json <<EOF cat > package.json <<EOF
{ {
"dependencies": { "dependencies": {
"archiver": "5.3.1", "archiver": "7.0.1",
"meshcentral": "$MESH_VER", "meshcentral": "$MESH_VER",
"mongodb": "4.13.0", "mongodb": "4.13.0",
"otplib": "10.2.3", "otplib": "10.2.3",

View File

@@ -25,7 +25,8 @@ if [ ! -f "/home/node/app/meshcentral-data/config.json" ] || [[ "${MESH_PERSISTE
encoded_uri=$(node -p "encodeURI('mongodb://${MONGODB_USER}:${MONGODB_PASSWORD}@${MONGODB_HOST}:${MONGODB_PORT}')") encoded_uri=$(node -p "encodeURI('mongodb://${MONGODB_USER}:${MONGODB_PASSWORD}@${MONGODB_HOST}:${MONGODB_PORT}')")
mesh_config="$(cat << EOF mesh_config="$(
cat <<EOF
{ {
"settings": { "settings": {
"mongodb": "${encoded_uri}", "mongodb": "${encoded_uri}",
@@ -39,8 +40,7 @@ if [ ! -f "/home/node/app/meshcentral-data/config.json" ] || [[ "${MESH_PERSISTE
"aliasPort": 443, "aliasPort": 443,
"allowLoginToken": true, "allowLoginToken": true,
"allowFraming": true, "allowFraming": true,
"_agentPing": 60, "agentPing": 35,
"agentPong": 300,
"allowHighQualityDesktop": true, "allowHighQualityDesktop": true,
"agentCoreDump": false, "agentCoreDump": false,
"compression": ${MESH_COMPRESSION_ENABLED}, "compression": ${MESH_COMPRESSION_ENABLED},
@@ -74,26 +74,26 @@ if [ ! -f "/home/node/app/meshcentral-data/config.json" ] || [[ "${MESH_PERSISTE
} }
} }
EOF EOF
)" )"
echo "${mesh_config}" > /home/node/app/meshcentral-data/config.json echo "${mesh_config}" >/home/node/app/meshcentral-data/config.json
fi fi
node node_modules/meshcentral --createaccount ${MESH_USER} --pass ${MESH_PASS} --email example@example.com node node_modules/meshcentral --createaccount ${MESH_USER} --pass ${MESH_PASS} --email example@example.com
node node_modules/meshcentral --adminaccount ${MESH_USER} node node_modules/meshcentral --adminaccount ${MESH_USER}
if [ ! -f "${TACTICAL_DIR}/tmp/mesh_token" ]; then if [ ! -f "${TACTICAL_DIR}/tmp/mesh_token" ]; then
mesh_token=$(node node_modules/meshcentral --logintokenkey) mesh_token=$(node node_modules/meshcentral --logintokenkey)
if [[ ${#mesh_token} -eq 160 ]]; then if [[ ${#mesh_token} -eq 160 ]]; then
echo ${mesh_token} > /opt/tactical/tmp/mesh_token echo ${mesh_token} >/opt/tactical/tmp/mesh_token
else else
echo "Failed to generate mesh token. Fix the error and restart the mesh container" echo "Failed to generate mesh token. Fix the error and restart the mesh container"
fi fi
fi fi
# wait for nginx container # wait for nginx container
until (echo > /dev/tcp/"${NGINX_HOST_IP}"/${NGINX_HOST_PORT}) &> /dev/null; do until (echo >/dev/tcp/"${NGINX_HOST_IP}"/${NGINX_HOST_PORT}) &>/dev/null; do
echo "waiting for nginx to start..." echo "waiting for nginx to start..."
sleep 5 sleep 5
done done

View File

@@ -1,4 +1,4 @@
FROM nats:2.10.17-alpine FROM nats:2.10.22-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

@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
SCRIPT_VERSION="85" SCRIPT_VERSION="86"
SCRIPT_URL="https://raw.githubusercontent.com/amidaware/tacticalrmm/master/install.sh" SCRIPT_URL="https://raw.githubusercontent.com/amidaware/tacticalrmm/master/install.sh"
sudo apt install -y curl wget dirmngr gnupg lsb-release ca-certificates sudo apt install -y curl wget dirmngr gnupg lsb-release ca-certificates
@@ -420,7 +420,7 @@ mesh_pkg="$(
cat <<EOF cat <<EOF
{ {
"dependencies": { "dependencies": {
"archiver": "5.3.1", "archiver": "7.0.1",
"meshcentral": "${MESH_VER}", "meshcentral": "${MESH_VER}",
"otplib": "10.2.3", "otplib": "10.2.3",
"pg": "8.7.1", "pg": "8.7.1",

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
SCRIPT_VERSION="59" SCRIPT_VERSION="60"
SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/restore.sh' SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/restore.sh'
sudo apt update sudo apt update
@@ -413,7 +413,7 @@ mesh_pkg="$(
cat <<EOF cat <<EOF
{ {
"dependencies": { "dependencies": {
"archiver": "5.3.1", "archiver": "7.0.1",
"meshcentral": "${MESH_VER}", "meshcentral": "${MESH_VER}",
"otplib": "10.2.3", "otplib": "10.2.3",
"pg": "8.7.1", "pg": "8.7.1",
@@ -494,7 +494,6 @@ echo "Running management commands...please wait..."
API=$(python manage.py get_config api) API=$(python manage.py get_config api)
WEB_VERSION=$(python manage.py get_config webversion) WEB_VERSION=$(python manage.py get_config webversion)
FRONTEND=$(python manage.py get_config webdomain) FRONTEND=$(python manage.py get_config webdomain)
webdomain=$(python manage.py get_config webdomain)
meshdomain=$(python manage.py get_config meshdomain) meshdomain=$(python manage.py get_config meshdomain)
WEBTAR_URL=$(python manage.py get_webtar_url) WEBTAR_URL=$(python manage.py get_webtar_url)
CERT_PUB_KEY=$(python manage.py get_config certfile) CERT_PUB_KEY=$(python manage.py get_config certfile)
@@ -620,9 +619,9 @@ sudo ln -s /etc/nginx/sites-available/rmm.conf /etc/nginx/sites-enabled/rmm.conf
HAS_11=$(grep 127.0.1.1 /etc/hosts) HAS_11=$(grep 127.0.1.1 /etc/hosts)
if [[ $HAS_11 ]]; then if [[ $HAS_11 ]]; then
sudo sed -i "/127.0.1.1/s/$/ ${API} ${webdomain} ${meshdomain}/" /etc/hosts sudo sed -i "/127.0.1.1/s/$/ ${API} ${FRONTEND} ${meshdomain}/" /etc/hosts
else else
echo "127.0.1.1 ${API} ${webdomain} ${meshdomain}" | sudo tee --append /etc/hosts >/dev/null echo "127.0.1.1 ${API} ${FRONTEND} ${meshdomain}" | sudo tee --append /etc/hosts >/dev/null
fi fi
sudo systemctl enable nats.service sudo systemctl enable nats.service

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
SCRIPT_VERSION="153" SCRIPT_VERSION="154"
SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/update.sh' SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/update.sh'
LATEST_SETTINGS_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/api/tacticalrmm/tacticalrmm/settings.py' LATEST_SETTINGS_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/api/tacticalrmm/tacticalrmm/settings.py'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
@@ -319,7 +319,7 @@ if [[ "${CURRENT_MESH_VER}" != "${LATEST_MESH_VER}" ]] || [[ "$force" = true ]];
cat <<EOF cat <<EOF
{ {
"dependencies": { "dependencies": {
"archiver": "5.3.1", "archiver": "7.0.1",
"meshcentral": "${LATEST_MESH_VER}", "meshcentral": "${LATEST_MESH_VER}",
"otplib": "10.2.3", "otplib": "10.2.3",
"pg": "8.7.1", "pg": "8.7.1",