Release 0.15.0

This commit is contained in:
wh1te909
2022-09-24 03:09:12 +00:00
13 changed files with 211 additions and 72 deletions

4
.github/FUNDING.yml vendored
View File

@@ -1,9 +1,9 @@
# These are supported funding model platforms
github: wh1te909
github: amidaware
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: tacticalrmm
ko_fi: # tacticalrmm
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username

View File

@@ -35,6 +35,9 @@ Demo database resets every hour. A lot of features are disabled for obvious reas
## Linux agent versions supported
- Any distro with systemd which includes but is not limited to: Debian (10, 11), Ubuntu x86_64 (18.04, 20.04, 22.04), Synology 7, centos, freepbx and more!
## Mac agent versions supported
- 64 bit Intel and Apple Silicon (M1, M2)
## Installation / Backup / Restore / Usage
### Refer to the [documentation](https://docs.tacticalrmm.com)

View File

@@ -2,6 +2,10 @@ SECRET_KEY = "{{ django_secret }}"
DEBUG = True
ALLOWED_HOSTS = ['{{ api }}']
ADMIN_URL = "admin/"
CORS_ORIGIN_WHITELIST = [
"http://{{ rmm }}:8080",
"https://{{ rmm }}:8080",
]
CORS_ORIGIN_ALLOW_ALL = True
DATABASES = {
'default': {

View File

@@ -835,9 +835,12 @@ class Agent(BaseAuditModel):
Return type: tuple(message: str, error: bool)
"""
if mode == "tacagent":
if self.is_posix:
if self.plat == AgentPlat.LINUX:
cmd = "systemctl restart tacticalagent.service"
shell = 3
elif self.plat == AgentPlat.DARWIN:
cmd = "launchctl kickstart -k system/tacticalagent"
shell = 3
else:
cmd = "net stop tacticalrmm & taskkill /F /IM tacticalrmm.exe & net start tacticalrmm"
shell = 1

View File

@@ -30,9 +30,9 @@ from scripts.models import Script
from scripts.tasks import handle_bulk_command_task, handle_bulk_script_task
from tacticalrmm.constants import (
AGENT_DEFER,
AGENT_TABLE_DEFER,
AGENT_STATUS_OFFLINE,
AGENT_STATUS_ONLINE,
AGENT_TABLE_DEFER,
AgentHistoryType,
AgentMonType,
AgentPlat,
@@ -174,6 +174,7 @@ class GetUpdateDeleteAgent(APIView):
fields = [
"maintenance_mode", # TODO separate this
"policy", # TODO separate this
"block_policy_inheritance", # TODO separate this
"monitoring_type",
"description",
"overdue_email_alert",
@@ -236,10 +237,13 @@ class GetUpdateDeleteAgent(APIView):
def delete(self, request, agent_id):
agent = get_object_or_404(Agent, agent_id=agent_id)
code = "foo"
code = "foo" # stub for windows
if agent.plat == AgentPlat.LINUX:
with open(settings.LINUX_AGENT_SCRIPT, "r") as f:
code = f.read()
elif agent.plat == AgentPlat.DARWIN:
with open(settings.MAC_UNINSTALL, "r") as f:
code = f.read()
asyncio.run(agent.nats_cmd({"func": "uninstall", "code": code}, wait=False))
name = agent.hostname
@@ -550,7 +554,15 @@ def install_agent(request):
codesign_token, is_valid = token_is_valid()
inno = f"tacticalagent-v{version}-{plat}-{goarch}.exe"
if request.data["installMethod"] in {"bash", "mac"} and not is_valid:
return notify_error(
"Missing code signing token, or token is no longer valid. Please read the docs for more info."
)
inno = f"tacticalagent-v{version}-{plat}-{goarch}"
if plat == AgentPlat.WINDOWS:
inno += ".exe"
download_url = get_agent_url(goarch=goarch, plat=plat, token=codesign_token)
installer_user = User.objects.filter(is_installer_user=True).first()
@@ -559,6 +571,21 @@ def install_agent(request):
user=installer_user, expiry=dt.timedelta(hours=request.data["expires"])
)
install_flags = [
"-m",
"install",
"--api",
request.data["api"],
"--client-id",
client_id,
"--site-id",
site_id,
"--agent-type",
request.data["agenttype"],
"--auth",
token,
]
if request.data["installMethod"] == "exe":
from tacticalrmm.utils import generate_winagent_exe
@@ -576,14 +603,6 @@ def install_agent(request):
)
elif request.data["installMethod"] == "bash":
# TODO
# linux agents are in beta for now, only available for sponsors for testing
# remove this after it's out of beta
if not is_valid:
return notify_error(
"Missing code signing token, or token is no longer valid. Please read the docs for more info."
)
from agents.utils import generate_linux_install
@@ -597,43 +616,39 @@ def install_agent(request):
download_url=download_url,
)
elif request.data["installMethod"] == "manual":
cmd = [
inno,
"/VERYSILENT",
"/SUPPRESSMSGBOXES",
"&&",
"ping",
"127.0.0.1",
"-n",
"5",
"&&",
r'"C:\Program Files\TacticalAgent\tacticalrmm.exe"',
"-m",
"install",
"--api",
request.data["api"],
"--client-id",
client_id,
"--site-id",
site_id,
"--agent-type",
request.data["agenttype"],
"--auth",
token,
]
elif request.data["installMethod"] in {"manual", "mac"}:
resp = {}
if request.data["installMethod"] == "manual":
cmd = [
inno,
"/VERYSILENT",
"/SUPPRESSMSGBOXES",
"&&",
"ping",
"127.0.0.1",
"-n",
"5",
"&&",
r'"C:\Program Files\TacticalAgent\tacticalrmm.exe"',
] + install_flags
if int(request.data["rdp"]):
cmd.append("--rdp")
if int(request.data["ping"]):
cmd.append("--ping")
if int(request.data["power"]):
cmd.append("--power")
if int(request.data["rdp"]):
cmd.append("--rdp")
if int(request.data["ping"]):
cmd.append("--ping")
if int(request.data["power"]):
cmd.append("--power")
resp = {
"cmd": " ".join(str(i) for i in cmd),
"url": download_url,
}
resp["cmd"] = " ".join(str(i) for i in cmd)
else:
install_flags.insert(0, f"sudo ./{inno}")
cmd = install_flags.copy()
dl = f"curl -L -o {inno} '{download_url}'"
resp["cmd"] = (
dl + f" && chmod +x {inno} && " + " ".join(str(i) for i in cmd)
)
resp["url"] = download_url
return Response(resp)
@@ -912,6 +927,8 @@ def bulk(request):
q = q.filter(plat=AgentPlat.WINDOWS)
elif request.data["osType"] == AgentPlat.LINUX:
q = q.filter(plat=AgentPlat.LINUX)
elif request.data["osType"] == AgentPlat.DARWIN:
q = q.filter(plat=AgentPlat.DARWIN)
agents: list[int] = [agent.pk for agent in q]

View File

@@ -24,6 +24,7 @@ from core.utils import (
get_core_settings,
get_mesh_device_id,
get_mesh_ws_url,
get_meshagent_url,
)
from logs.models import DebugLog, PendingAction
from software.models import InstalledSoftware
@@ -398,26 +399,33 @@ class MeshExe(APIView):
def post(self, request):
match request.data:
case {"goarch": GoArch.AMD64, "plat": AgentPlat.WINDOWS}:
arch = MeshAgentIdent.WIN64
ident = MeshAgentIdent.WIN64
case {"goarch": GoArch.i386, "plat": AgentPlat.WINDOWS}:
arch = MeshAgentIdent.WIN32
ident = MeshAgentIdent.WIN32
case {"goarch": GoArch.AMD64, "plat": AgentPlat.DARWIN} | {
"goarch": GoArch.ARM64,
"plat": AgentPlat.DARWIN,
}:
ident = MeshAgentIdent.DARWIN_UNIVERSAL
case _:
return notify_error("Arch not specified")
return notify_error("Arch not supported")
core = get_core_settings()
try:
uri = get_mesh_ws_url()
mesh_id = asyncio.run(get_mesh_device_id(uri, core.mesh_device_group))
mesh_device_id: str = asyncio.run(
get_mesh_device_id(uri, core.mesh_device_group)
)
except:
return notify_error("Unable to connect to mesh to get group id information")
if settings.DOCKER_BUILD:
dl_url = f"{settings.MESH_WS_URL.replace('ws://', 'http://')}/meshagents?id={arch}&meshid={mesh_id}&installflags=0"
else:
dl_url = (
f"{core.mesh_site}/meshagents?id={arch}&meshid={mesh_id}&installflags=0"
)
dl_url = get_meshagent_url(
ident=ident,
plat=request.data["plat"],
mesh_site=core.mesh_site,
mesh_device_id=mesh_device_id,
)
try:
return download_mesh_agent(dl_url)

View File

@@ -0,0 +1,8 @@
#!/bin/bash
/usr/local/mesh_services/meshagent/meshagent -fulluninstall
launchctl bootout system /Library/LaunchDaemons/tacticalagent.plist
rm -rf /usr/local/mesh_services
rm -f /etc/tacticalagent
rm -rf /opt/tacticalagent
rm -f /Library/LaunchDaemons/tacticalagent.plist

View File

@@ -5,13 +5,20 @@ from channels.db import database_sync_to_async
from channels.testing import WebsocketCommunicator
from django.conf import settings
from django.core.management import call_command
from django.test import override_settings
from model_bakery import baker
from rest_framework.authtoken.models import Token
from agents.models import Agent
from core.utils import get_core_settings
from core.utils import get_core_settings, get_meshagent_url
from logs.models import PendingAction
from tacticalrmm.constants import CONFIG_MGMT_CMDS, CustomFieldModel, PAAction, PAStatus
from tacticalrmm.constants import (
CONFIG_MGMT_CMDS,
CustomFieldModel,
MeshAgentIdent,
PAAction,
PAStatus,
)
from tacticalrmm.test import TacticalTestCase
from .consumers import DashInfo
@@ -444,3 +451,58 @@ class TestCorePermissions(TacticalTestCase):
def setUp(self):
self.setup_client()
self.setup_coresettings()
class TestCoreUtils(TacticalTestCase):
def setUp(self):
self.setup_coresettings()
def test_get_meshagent_url_standard(self):
r = get_meshagent_url(
ident=MeshAgentIdent.DARWIN_UNIVERSAL,
plat="darwin",
mesh_site="https://mesh.example.com",
mesh_device_id="abc123",
)
self.assertEqual(
r,
"https://mesh.example.com/meshagents?id=abc123&installflags=2&meshinstall=10005",
)
r = get_meshagent_url(
ident=MeshAgentIdent.WIN64,
plat="windows",
mesh_site="https://mesh.example.com",
mesh_device_id="abc123",
)
self.assertEqual(
r,
"https://mesh.example.com/meshagents?id=4&meshid=abc123&installflags=0",
)
@override_settings(DOCKER_BUILD=True)
@override_settings(MESH_WS_URL="ws://tactical-meshcentral:4443")
def test_get_meshagent_url_docker(self):
r = get_meshagent_url(
ident=MeshAgentIdent.DARWIN_UNIVERSAL,
plat="darwin",
mesh_site="https://mesh.example.com",
mesh_device_id="abc123",
)
self.assertEqual(
r,
"http://tactical-meshcentral:4443/meshagents?id=abc123&installflags=2&meshinstall=10005",
)
r = get_meshagent_url(
ident=MeshAgentIdent.WIN64,
plat="windows",
mesh_site="https://mesh.example.com",
mesh_device_id="abc123",
)
self.assertEqual(
r,
"http://tactical-meshcentral:4443/meshagents?id=4&meshid=abc123&installflags=0",
)

View File

@@ -1,6 +1,7 @@
import json
import subprocess
import tempfile
import urllib.parse
from base64 import b64encode
from typing import TYPE_CHECKING, Optional, cast
@@ -11,7 +12,12 @@ from django.core.cache import cache
from django.http import FileResponse
from meshctrl.utils import get_auth_token
from tacticalrmm.constants import CORESETTINGS_CACHE_KEY, ROLE_CACHE_PREFIX
from tacticalrmm.constants import (
CORESETTINGS_CACHE_KEY,
ROLE_CACHE_PREFIX,
AgentPlat,
MeshAgentIdent,
)
if TYPE_CHECKING:
from core.models import CodeSignToken, CoreSettings
@@ -142,3 +148,28 @@ def sysd_svc_is_running(svc: str) -> bool:
cmd = ["systemctl", "is-active", "--quiet", svc]
r = subprocess.run(cmd, capture_output=True)
return not r.returncode
def get_meshagent_url(
*, ident: "MeshAgentIdent", plat: str, mesh_site: str, mesh_device_id: str
) -> str:
if settings.DOCKER_BUILD:
base = settings.MESH_WS_URL.replace("ws://", "http://")
else:
base = mesh_site
if plat == AgentPlat.WINDOWS:
params = {
"id": ident,
"meshid": mesh_device_id,
"installflags": 0,
}
else:
params = {
"id": mesh_device_id,
"installflags": 2,
"meshinstall": ident,
}
return base + "/meshagents?" + urllib.parse.urlencode(params)

View File

@@ -1,6 +1,6 @@
asgiref==3.5.2
celery==5.2.7
certifi==2022.6.15.1
certifi==2022.9.14
cffi==1.15.1
channels==3.0.5
channels_redis==3.4.1
@@ -11,7 +11,8 @@ Django==4.1.1
django-cors-headers==3.13.0
django-ipware==4.0.2
django-rest-knox==4.2.0
djangorestframework==3.13.1
djangorestframework==3.14.0
drf-spectacular==0.24.1
future==0.18.2
msgpack==1.0.4
nats-py==2.1.7
@@ -28,12 +29,11 @@ hiredis==2.0.0
requests==2.28.1
six==1.16.0
sqlparse==0.4.2
twilio==7.14.0
twilio==7.14.1
urllib3==1.26.12
uWSGI==2.0.20
validators==0.20.0
vine==5.0.0
websockets==10.3
zipp==3.8.1
drf-spectacular==0.23.1
meshctrl==0.1.15

View File

@@ -10,6 +10,7 @@ class MeshAgentIdent(Enum):
LINUX64 = 6
LINUX_ARM_64 = 26
LINUX_ARM_HF = 25
DARWIN_UNIVERSAL = 10005
def __str__(self):
return str(self.value)

View File

@@ -14,24 +14,26 @@ EXE_DIR = os.path.join(BASE_DIR, "tacticalrmm/private/exe")
LINUX_AGENT_SCRIPT = BASE_DIR / "core" / "agent_linux.sh"
MAC_UNINSTALL = BASE_DIR / "core" / "mac_uninstall.sh"
AUTH_USER_MODEL = "accounts.User"
# latest release
TRMM_VERSION = "0.14.8"
TRMM_VERSION = "0.15.0"
# https://github.com/amidaware/tacticalrmm-web
WEB_VERSION = "0.100.9"
WEB_VERSION = "0.101.0"
# bump this version everytime vue code is changed
# to alert user they need to manually refresh their browser
APP_VER = "0.0.170"
APP_VER = "0.0.171"
# https://github.com/amidaware/rmmagent
LATEST_AGENT_VER = "2.3.1"
LATEST_AGENT_VER = "2.4.0"
MESH_VER = "1.0.85"
NATS_SERVER_VER = "2.9.0"
NATS_SERVER_VER = "2.9.1"
# for the update script, bump when need to recreate venv
PIP_VER = "32"

View File

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