goodbye salt, you've served us well
This commit is contained in:
parent
3282fa803c
commit
903af0c2cf
|
@ -6,7 +6,7 @@
|
|||
[](https://github.com/python/black)
|
||||
|
||||
Tactical RMM is a remote monitoring & management tool for Windows computers, built with Django and Vue.\
|
||||
It uses an [agent](https://github.com/wh1te909/rmmagent) written in golang, as well as the [SaltStack](https://github.com/saltstack/salt) api and [MeshCentral](https://github.com/Ylianst/MeshCentral)
|
||||
It uses an [agent](https://github.com/wh1te909/rmmagent) written in golang and integrates with [MeshCentral](https://github.com/Ylianst/MeshCentral)
|
||||
|
||||
# [LIVE DEMO](https://rmm.xlawgaming.com/)
|
||||
Demo database resets every hour. Alot of features are disabled for obvious reasons due to the nature of this app.
|
||||
|
@ -62,7 +62,6 @@ sudo ufw default allow outgoing
|
|||
sudo ufw allow ssh
|
||||
sudo ufw allow http
|
||||
sudo ufw allow https
|
||||
sudo ufw allow proto tcp from any to any port 4505,4506
|
||||
sudo ufw allow proto tcp from any to any port 4222
|
||||
sudo ufw enable && sudo ufw reload
|
||||
```
|
||||
|
|
|
@ -9,6 +9,7 @@ import validators
|
|||
import msgpack
|
||||
import re
|
||||
from collections import Counter
|
||||
from typing import List
|
||||
from loguru import logger
|
||||
from packaging import version as pyver
|
||||
from distutils.version import LooseVersion
|
||||
|
@ -382,6 +383,13 @@ class Agent(BaseAuditModel):
|
|||
|
||||
return patch_policy
|
||||
|
||||
def get_approved_update_guids(self) -> List[str]:
|
||||
return list(
|
||||
self.winupdates.filter(action="approve", installed=False).values_list(
|
||||
"guid", flat=True
|
||||
)
|
||||
)
|
||||
|
||||
def generate_checks_from_policies(self):
|
||||
from automation.models import Policy
|
||||
|
||||
|
@ -452,77 +460,6 @@ class Agent(BaseAuditModel):
|
|||
await nc.flush()
|
||||
await nc.close()
|
||||
|
||||
def salt_api_cmd(self, **kwargs):
|
||||
|
||||
# salt should always timeout first before the requests' timeout
|
||||
try:
|
||||
timeout = kwargs["timeout"]
|
||||
except KeyError:
|
||||
# default timeout
|
||||
timeout = 15
|
||||
salt_timeout = 12
|
||||
else:
|
||||
if timeout < 8:
|
||||
timeout = 8
|
||||
salt_timeout = 5
|
||||
else:
|
||||
salt_timeout = timeout - 3
|
||||
|
||||
json = {
|
||||
"client": "local",
|
||||
"tgt": self.salt_id,
|
||||
"fun": kwargs["func"],
|
||||
"timeout": salt_timeout,
|
||||
"username": settings.SALT_USERNAME,
|
||||
"password": settings.SALT_PASSWORD,
|
||||
"eauth": "pam",
|
||||
}
|
||||
|
||||
if "arg" in kwargs:
|
||||
json.update({"arg": kwargs["arg"]})
|
||||
if "kwargs" in kwargs:
|
||||
json.update({"kwarg": kwargs["kwargs"]})
|
||||
|
||||
try:
|
||||
resp = requests.post(
|
||||
f"http://{settings.SALT_HOST}:8123/run",
|
||||
json=[json],
|
||||
timeout=timeout,
|
||||
)
|
||||
except Exception:
|
||||
return "timeout"
|
||||
|
||||
try:
|
||||
ret = resp.json()["return"][0][self.salt_id]
|
||||
except Exception as e:
|
||||
logger.error(f"{self.salt_id}: {e}")
|
||||
return "error"
|
||||
else:
|
||||
return ret
|
||||
|
||||
def salt_api_async(self, **kwargs):
|
||||
|
||||
json = {
|
||||
"client": "local_async",
|
||||
"tgt": self.salt_id,
|
||||
"fun": kwargs["func"],
|
||||
"username": settings.SALT_USERNAME,
|
||||
"password": settings.SALT_PASSWORD,
|
||||
"eauth": "pam",
|
||||
}
|
||||
|
||||
if "arg" in kwargs:
|
||||
json.update({"arg": kwargs["arg"]})
|
||||
if "kwargs" in kwargs:
|
||||
json.update({"kwarg": kwargs["kwargs"]})
|
||||
|
||||
try:
|
||||
resp = requests.post(f"http://{settings.SALT_HOST}:8123/run", json=[json])
|
||||
except Exception:
|
||||
return "timeout"
|
||||
|
||||
return resp
|
||||
|
||||
@staticmethod
|
||||
def serialize(agent):
|
||||
# serializes the agent and returns json
|
||||
|
@ -533,32 +470,6 @@ class Agent(BaseAuditModel):
|
|||
del ret["client"]
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def salt_batch_async(**kwargs):
|
||||
assert isinstance(kwargs["minions"], list)
|
||||
|
||||
json = {
|
||||
"client": "local_async",
|
||||
"tgt_type": "list",
|
||||
"tgt": kwargs["minions"],
|
||||
"fun": kwargs["func"],
|
||||
"username": settings.SALT_USERNAME,
|
||||
"password": settings.SALT_PASSWORD,
|
||||
"eauth": "pam",
|
||||
}
|
||||
|
||||
if "arg" in kwargs:
|
||||
json.update({"arg": kwargs["arg"]})
|
||||
if "kwargs" in kwargs:
|
||||
json.update({"kwarg": kwargs["kwargs"]})
|
||||
|
||||
try:
|
||||
resp = requests.post(f"http://{settings.SALT_HOST}:8123/run", json=[json])
|
||||
except Exception:
|
||||
return "timeout"
|
||||
|
||||
return resp
|
||||
|
||||
def delete_superseded_updates(self):
|
||||
try:
|
||||
pks = [] # list of pks to delete
|
||||
|
|
|
@ -177,29 +177,6 @@ def sync_sysinfo_task():
|
|||
sleep(rand)
|
||||
|
||||
|
||||
@app.task
|
||||
def sync_salt_modules_task(pk):
|
||||
agent = Agent.objects.get(pk=pk)
|
||||
r = agent.salt_api_cmd(timeout=35, func="saltutil.sync_modules")
|
||||
# successful sync if new/charnged files: {'return': [{'MINION-15': ['modules.get_eventlog', 'modules.win_agent', 'etc...']}]}
|
||||
# successful sync with no new/changed files: {'return': [{'MINION-15': []}]}
|
||||
if r == "timeout" or r == "error":
|
||||
return f"Unable to sync modules {agent.salt_id}"
|
||||
|
||||
return f"Successfully synced salt modules on {agent.hostname}"
|
||||
|
||||
|
||||
@app.task
|
||||
def batch_sync_modules_task():
|
||||
# sync modules, split into chunks of 50 agents to not overload salt
|
||||
agents = Agent.objects.all()
|
||||
online = [i.salt_id for i in agents]
|
||||
chunks = (online[i : i + 50] for i in range(0, len(online), 50))
|
||||
for chunk in chunks:
|
||||
Agent.salt_batch_async(minions=chunk, func="saltutil.sync_modules")
|
||||
sleep(10)
|
||||
|
||||
|
||||
@app.task
|
||||
def uninstall_agent_task(salt_id, has_nats):
|
||||
attempts = 0
|
||||
|
|
|
@ -14,12 +14,6 @@ from tacticalrmm.test import TacticalTestCase
|
|||
from .serializers import AgentSerializer
|
||||
from winupdate.serializers import WinUpdatePolicySerializer
|
||||
from .models import Agent
|
||||
from .tasks import (
|
||||
agent_recovery_sms_task,
|
||||
auto_self_agent_update_task,
|
||||
sync_salt_modules_task,
|
||||
batch_sync_modules_task,
|
||||
)
|
||||
from winupdate.models import WinUpdatePolicy
|
||||
|
||||
|
||||
|
@ -543,7 +537,7 @@ class TestAgentViews(TacticalTestCase):
|
|||
|
||||
self.check_not_authenticated("get", url)
|
||||
|
||||
@patch("winupdate.tasks.bulk_check_for_updates_task.delay")
|
||||
""" @patch("winupdate.tasks.bulk_check_for_updates_task.delay")
|
||||
@patch("scripts.tasks.handle_bulk_script_task.delay")
|
||||
@patch("scripts.tasks.handle_bulk_command_task.delay")
|
||||
@patch("agents.models.Agent.salt_batch_async")
|
||||
|
@ -585,7 +579,7 @@ class TestAgentViews(TacticalTestCase):
|
|||
r = self.client.post(url, payload, format="json")
|
||||
self.assertEqual(r.status_code, 400)
|
||||
|
||||
""" payload = {
|
||||
payload = {
|
||||
"mode": "command",
|
||||
"monType": "workstations",
|
||||
"target": "client",
|
||||
|
@ -599,7 +593,7 @@ class TestAgentViews(TacticalTestCase):
|
|||
|
||||
r = self.client.post(url, payload, format="json")
|
||||
self.assertEqual(r.status_code, 200)
|
||||
bulk_command.assert_called_with([self.agent.pk], "gpupdate /force", "cmd", 300) """
|
||||
bulk_command.assert_called_with([self.agent.pk], "gpupdate /force", "cmd", 300)
|
||||
|
||||
payload = {
|
||||
"mode": "command",
|
||||
|
@ -657,7 +651,7 @@ class TestAgentViews(TacticalTestCase):
|
|||
|
||||
# TODO mock the script
|
||||
|
||||
self.check_not_authenticated("post", url)
|
||||
self.check_not_authenticated("post", url) """
|
||||
|
||||
@patch("agents.models.Agent.nats_cmd")
|
||||
def test_recover_mesh(self, nats_cmd):
|
||||
|
@ -759,41 +753,6 @@ class TestAgentTasks(TacticalTestCase):
|
|||
self.authenticate()
|
||||
self.setup_coresettings()
|
||||
|
||||
@patch("agents.models.Agent.salt_api_cmd")
|
||||
def test_sync_salt_modules_task(self, salt_api_cmd):
|
||||
self.agent = baker.make_recipe("agents.agent")
|
||||
salt_api_cmd.return_value = {"return": [{f"{self.agent.salt_id}": []}]}
|
||||
ret = sync_salt_modules_task.s(self.agent.pk).apply()
|
||||
salt_api_cmd.assert_called_with(timeout=35, func="saltutil.sync_modules")
|
||||
self.assertEqual(
|
||||
ret.result, f"Successfully synced salt modules on {self.agent.hostname}"
|
||||
)
|
||||
self.assertEqual(ret.status, "SUCCESS")
|
||||
|
||||
salt_api_cmd.return_value = "timeout"
|
||||
ret = sync_salt_modules_task.s(self.agent.pk).apply()
|
||||
self.assertEqual(ret.result, f"Unable to sync modules {self.agent.salt_id}")
|
||||
|
||||
salt_api_cmd.return_value = "error"
|
||||
ret = sync_salt_modules_task.s(self.agent.pk).apply()
|
||||
self.assertEqual(ret.result, f"Unable to sync modules {self.agent.salt_id}")
|
||||
|
||||
@patch("agents.models.Agent.salt_batch_async", return_value=None)
|
||||
@patch("agents.tasks.sleep", return_value=None)
|
||||
def test_batch_sync_modules_task(self, mock_sleep, salt_batch_async):
|
||||
# chunks of 50, should run 4 times
|
||||
baker.make_recipe(
|
||||
"agents.online_agent", last_seen=djangotime.now(), _quantity=60
|
||||
)
|
||||
baker.make_recipe(
|
||||
"agents.overdue_agent",
|
||||
last_seen=djangotime.now() - djangotime.timedelta(minutes=9),
|
||||
_quantity=115,
|
||||
)
|
||||
ret = batch_sync_modules_task.s().apply()
|
||||
self.assertEqual(salt_batch_async.call_count, 4)
|
||||
self.assertEqual(ret.status, "SUCCESS")
|
||||
|
||||
@patch("agents.models.Agent.nats_cmd")
|
||||
def test_agent_update(self, nats_cmd):
|
||||
from agents.tasks import agent_update
|
||||
|
|
|
@ -7,6 +7,7 @@ import random
|
|||
import string
|
||||
import datetime as dt
|
||||
from packaging import version as pyver
|
||||
from typing import List
|
||||
|
||||
from django.conf import settings
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
@ -37,7 +38,7 @@ from .tasks import (
|
|||
send_agent_update_task,
|
||||
run_script_email_results_task,
|
||||
)
|
||||
from winupdate.tasks import bulk_check_for_updates_task
|
||||
from winupdate.tasks import bulk_check_for_updates_task, bulk_install_updates_task
|
||||
from scripts.tasks import handle_bulk_command_task, handle_bulk_script_task
|
||||
|
||||
from tacticalrmm.utils import notify_error, reload_nats
|
||||
|
@ -72,10 +73,6 @@ def ping(request, pk):
|
|||
r = asyncio.run(agent.nats_cmd({"func": "ping"}, timeout=5))
|
||||
if r == "pong":
|
||||
status = "online"
|
||||
else:
|
||||
r = agent.salt_api_cmd(timeout=5, func="test.ping")
|
||||
if isinstance(r, bool) and r:
|
||||
status = "online"
|
||||
|
||||
return Response({"name": agent.hostname, "status": status})
|
||||
|
||||
|
@ -849,8 +846,7 @@ def bulk(request):
|
|||
elif request.data["monType"] == "workstations":
|
||||
q = q.filter(monitoring_type="workstation")
|
||||
|
||||
minions = [agent.salt_id for agent in q]
|
||||
agents = [agent.pk for agent in q]
|
||||
agents: List[int] = [agent.pk for agent in q]
|
||||
|
||||
AuditLog.audit_bulk_action(request.user, request.data["mode"], request.data)
|
||||
|
||||
|
@ -868,14 +864,12 @@ def bulk(request):
|
|||
return Response(f"{script.name} will now be run on {len(agents)} agents")
|
||||
|
||||
elif request.data["mode"] == "install":
|
||||
r = Agent.salt_batch_async(minions=minions, func="win_agent.install_updates")
|
||||
if r == "timeout":
|
||||
return notify_error("Salt API not running")
|
||||
bulk_install_updates_task.delay(agents)
|
||||
return Response(
|
||||
f"Pending updates will now be installed on {len(agents)} agents"
|
||||
)
|
||||
elif request.data["mode"] == "scan":
|
||||
bulk_check_for_updates_task.delay(minions=minions)
|
||||
bulk_check_for_updates_task.delay(agents)
|
||||
return Response(f"Patch status scan will now run on {len(agents)} agents")
|
||||
|
||||
return notify_error("Something went wrong")
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class Apiv2Config(AppConfig):
|
||||
name = "apiv2"
|
|
@ -1,38 +0,0 @@
|
|||
from tacticalrmm.test import TacticalTestCase
|
||||
from unittest.mock import patch
|
||||
from model_bakery import baker
|
||||
from itertools import cycle
|
||||
|
||||
|
||||
class TestAPIv2(TacticalTestCase):
|
||||
def setUp(self):
|
||||
self.authenticate()
|
||||
self.setup_coresettings()
|
||||
|
||||
@patch("agents.models.Agent.salt_api_cmd")
|
||||
def test_sync_modules(self, mock_ret):
|
||||
# setup data
|
||||
agent = baker.make_recipe("agents.agent")
|
||||
url = "/api/v2/saltminion/"
|
||||
payload = {"agent_id": agent.agent_id}
|
||||
|
||||
mock_ret.return_value = "error"
|
||||
r = self.client.patch(url, payload, format="json")
|
||||
self.assertEqual(r.status_code, 400)
|
||||
|
||||
mock_ret.return_value = []
|
||||
r = self.client.patch(url, payload, format="json")
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.data, "Modules are already in sync")
|
||||
|
||||
mock_ret.return_value = ["modules.win_agent"]
|
||||
r = self.client.patch(url, payload, format="json")
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.data, "Successfully synced salt modules")
|
||||
|
||||
mock_ret.return_value = ["askdjaskdjasd", "modules.win_agent"]
|
||||
r = self.client.patch(url, payload, format="json")
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.data, "Successfully synced salt modules")
|
||||
|
||||
self.check_not_authenticated("patch", url)
|
|
@ -1,14 +0,0 @@
|
|||
from django.urls import path
|
||||
from . import views
|
||||
from apiv3 import views as v3_views
|
||||
|
||||
urlpatterns = [
|
||||
path("newagent/", v3_views.NewAgent.as_view()),
|
||||
path("meshexe/", v3_views.MeshExe.as_view()),
|
||||
path("saltminion/", v3_views.SaltMinion.as_view()),
|
||||
path("<str:agentid>/saltminion/", v3_views.SaltMinion.as_view()),
|
||||
path("sysinfo/", v3_views.SysInfo.as_view()),
|
||||
path("hello/", v3_views.Hello.as_view()),
|
||||
path("checkrunner/", views.CheckRunner.as_view()),
|
||||
path("<str:agentid>/checkrunner/", views.CheckRunner.as_view()),
|
||||
]
|
|
@ -1,41 +0,0 @@
|
|||
from django.shortcuts import get_object_or_404
|
||||
from django.utils import timezone as djangotime
|
||||
|
||||
from rest_framework.authentication import TokenAuthentication
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from agents.models import Agent
|
||||
from checks.models import Check
|
||||
|
||||
from checks.serializers import CheckRunnerGetSerializerV2
|
||||
|
||||
|
||||
class CheckRunner(APIView):
|
||||
"""
|
||||
For the windows python agent
|
||||
"""
|
||||
|
||||
authentication_classes = [TokenAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request, agentid):
|
||||
agent = get_object_or_404(Agent, agent_id=agentid)
|
||||
agent.last_seen = djangotime.now()
|
||||
agent.save(update_fields=["last_seen"])
|
||||
checks = Check.objects.filter(agent__pk=agent.pk, overriden_by_policy=False)
|
||||
|
||||
ret = {
|
||||
"agent": agent.pk,
|
||||
"check_interval": agent.check_interval,
|
||||
"checks": CheckRunnerGetSerializerV2(checks, many=True).data,
|
||||
}
|
||||
return Response(ret)
|
||||
|
||||
def patch(self, request):
|
||||
check = get_object_or_404(Check, pk=request.data["id"])
|
||||
check.last_run = djangotime.now()
|
||||
check.save(update_fields=["last_run"])
|
||||
status = check.handle_checkv2(request.data)
|
||||
return Response(status)
|
|
@ -26,23 +26,6 @@ class TestAPIv3(TacticalTestCase):
|
|||
|
||||
self.check_not_authenticated("get", url)
|
||||
|
||||
def test_get_salt_minion(self):
|
||||
url = f"/api/v3/{self.agent.agent_id}/saltminion/"
|
||||
url2 = f"/api/v2/{self.agent.agent_id}/saltminion/"
|
||||
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertIn("latestVer", r.json().keys())
|
||||
self.assertIn("currentVer", r.json().keys())
|
||||
self.assertIn("salt_id", r.json().keys())
|
||||
self.assertIn("downloadURL", r.json().keys())
|
||||
|
||||
r2 = self.client.get(url2)
|
||||
self.assertEqual(r2.status_code, 200)
|
||||
|
||||
self.check_not_authenticated("get", url)
|
||||
self.check_not_authenticated("get", url2)
|
||||
|
||||
def test_get_mesh_info(self):
|
||||
url = f"/api/v3/{self.agent.pk}/meshinfo/"
|
||||
|
||||
|
|
|
@ -7,8 +7,6 @@ urlpatterns = [
|
|||
path("checkrunner/", views.CheckRunner.as_view()),
|
||||
path("<str:agentid>/checkrunner/", views.CheckRunner.as_view()),
|
||||
path("<int:pk>/<str:agentid>/taskrunner/", views.TaskRunner.as_view()),
|
||||
path("saltminion/", views.SaltMinion.as_view()),
|
||||
path("<str:agentid>/saltminion/", views.SaltMinion.as_view()),
|
||||
path("<int:pk>/meshinfo/", views.MeshInfo.as_view()),
|
||||
path("meshexe/", views.MeshExe.as_view()),
|
||||
path("sysinfo/", views.SysInfo.as_view()),
|
||||
|
|
|
@ -29,10 +29,8 @@ from winupdate.serializers import ApprovedUpdateSerializer
|
|||
from agents.tasks import (
|
||||
agent_recovery_email_task,
|
||||
agent_recovery_sms_task,
|
||||
sync_salt_modules_task,
|
||||
install_salt_task,
|
||||
)
|
||||
from winupdate.tasks import check_for_updates_task
|
||||
from checks.utils import bytes2human
|
||||
from tacticalrmm.utils import notify_error, reload_nats, filter_software, SoftwareList
|
||||
|
||||
|
@ -131,12 +129,6 @@ class CheckIn(APIView):
|
|||
serializer = WinAgentSerializer(instance=agent, data=request.data, partial=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save(last_seen=djangotime.now())
|
||||
|
||||
sync_salt_modules_task.delay(agent.pk)
|
||||
check_for_updates_task.apply_async(
|
||||
queue="wupdate", kwargs={"pk": agent.pk, "wait": True}
|
||||
)
|
||||
|
||||
return Response("ok")
|
||||
|
||||
|
||||
|
@ -223,12 +215,6 @@ class Hello(APIView):
|
|||
serializer = WinAgentSerializer(instance=agent, data=request.data, partial=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save(last_seen=djangotime.now())
|
||||
|
||||
sync_salt_modules_task.delay(agent.pk)
|
||||
check_for_updates_task.apply_async(
|
||||
queue="wupdate", kwargs={"pk": agent.pk, "wait": True}
|
||||
)
|
||||
|
||||
return Response("ok")
|
||||
|
||||
|
||||
|
@ -298,77 +284,6 @@ class TaskRunner(APIView):
|
|||
return Response("ok")
|
||||
|
||||
|
||||
class SaltMinion(APIView):
|
||||
authentication_classes = [TokenAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request, agentid):
|
||||
agent = get_object_or_404(Agent, agent_id=agentid)
|
||||
ret = {
|
||||
"latestVer": settings.LATEST_SALT_VER,
|
||||
"currentVer": agent.salt_ver,
|
||||
"salt_id": agent.salt_id,
|
||||
"downloadURL": agent.winsalt_dl,
|
||||
}
|
||||
return Response(ret)
|
||||
|
||||
def post(self, request):
|
||||
# accept the salt key
|
||||
agent = get_object_or_404(Agent, agent_id=request.data["agent_id"])
|
||||
if agent.salt_id != request.data["saltid"]:
|
||||
return notify_error("Salt keys do not match")
|
||||
|
||||
try:
|
||||
resp = requests.post(
|
||||
f"http://{settings.SALT_HOST}:8123/run",
|
||||
json=[
|
||||
{
|
||||
"client": "wheel",
|
||||
"fun": "key.accept",
|
||||
"match": request.data["saltid"],
|
||||
"username": settings.SALT_USERNAME,
|
||||
"password": settings.SALT_PASSWORD,
|
||||
"eauth": "pam",
|
||||
}
|
||||
],
|
||||
timeout=30,
|
||||
)
|
||||
except Exception:
|
||||
return notify_error("No communication between agent and salt-api")
|
||||
|
||||
try:
|
||||
data = resp.json()["return"][0]["data"]
|
||||
minion = data["return"]["minions"][0]
|
||||
except Exception:
|
||||
return notify_error("Key error")
|
||||
|
||||
if data["success"] and minion == request.data["saltid"]:
|
||||
return Response("Salt key was accepted")
|
||||
else:
|
||||
return notify_error("Not accepted")
|
||||
|
||||
def patch(self, request):
|
||||
# sync modules
|
||||
agent = get_object_or_404(Agent, agent_id=request.data["agent_id"])
|
||||
r = agent.salt_api_cmd(timeout=45, func="saltutil.sync_modules")
|
||||
|
||||
if r == "timeout" or r == "error":
|
||||
return notify_error("Failed to sync salt modules")
|
||||
|
||||
if isinstance(r, list) and any("modules" in i for i in r):
|
||||
return Response("Successfully synced salt modules")
|
||||
elif isinstance(r, list) and not r:
|
||||
return Response("Modules are already in sync")
|
||||
else:
|
||||
return notify_error(f"Failed to sync salt modules: {str(r)}")
|
||||
|
||||
def put(self, request):
|
||||
agent = get_object_or_404(Agent, agent_id=request.data["agent_id"])
|
||||
agent.salt_ver = request.data["ver"]
|
||||
agent.save(update_fields=["salt_ver"])
|
||||
return Response("ok")
|
||||
|
||||
|
||||
class WinUpdater(APIView):
|
||||
|
||||
authentication_classes = [TokenAuthentication]
|
||||
|
@ -430,19 +345,10 @@ class WinUpdater(APIView):
|
|||
if reboot:
|
||||
if agent.has_nats:
|
||||
asyncio.run(agent.nats_cmd({"func": "rebootnow"}, wait=False))
|
||||
else:
|
||||
agent.salt_api_async(
|
||||
func="system.reboot",
|
||||
arg=7,
|
||||
kwargs={"in_seconds": True},
|
||||
logger.info(
|
||||
f"{agent.hostname} is rebooting after updates were installed."
|
||||
)
|
||||
|
||||
logger.info(f"{agent.hostname} is rebooting after updates were installed.")
|
||||
else:
|
||||
check_for_updates_task.apply_async(
|
||||
queue="wupdate", kwargs={"pk": agent.pk, "wait": False}
|
||||
)
|
||||
|
||||
return Response("ok")
|
||||
|
||||
|
||||
|
|
|
@ -6,60 +6,26 @@ from scripts.models import Script
|
|||
|
||||
|
||||
@app.task
|
||||
def handle_bulk_command_task(agentpks, cmd, shell, timeout):
|
||||
def handle_bulk_command_task(agentpks, cmd, shell, timeout) -> None:
|
||||
agents = Agent.objects.filter(pk__in=agentpks)
|
||||
|
||||
agents_nats = [agent for agent in agents if agent.has_nats]
|
||||
agents_salt = [agent for agent in agents if not agent.has_nats]
|
||||
minions = [agent.salt_id for agent in agents_salt]
|
||||
|
||||
if minions:
|
||||
Agent.salt_batch_async(
|
||||
minions=minions,
|
||||
func="cmd.run_bg",
|
||||
kwargs={
|
||||
"cmd": cmd,
|
||||
"shell": shell,
|
||||
"timeout": timeout,
|
||||
},
|
||||
)
|
||||
|
||||
if agents_nats:
|
||||
nats_data = {
|
||||
"func": "rawcmd",
|
||||
"timeout": timeout,
|
||||
"payload": {
|
||||
"command": cmd,
|
||||
"shell": shell,
|
||||
},
|
||||
}
|
||||
for agent in agents_nats:
|
||||
asyncio.run(agent.nats_cmd(nats_data, wait=False))
|
||||
nats_data = {
|
||||
"func": "rawcmd",
|
||||
"timeout": timeout,
|
||||
"payload": {
|
||||
"command": cmd,
|
||||
"shell": shell,
|
||||
},
|
||||
}
|
||||
for agent in agents_nats:
|
||||
asyncio.run(agent.nats_cmd(nats_data, wait=False))
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_bulk_script_task(scriptpk, agentpks, args, timeout):
|
||||
def handle_bulk_script_task(scriptpk, agentpks, args, timeout) -> None:
|
||||
script = Script.objects.get(pk=scriptpk)
|
||||
agents = Agent.objects.filter(pk__in=agentpks)
|
||||
|
||||
agents_nats = [agent for agent in agents if agent.has_nats]
|
||||
agents_salt = [agent for agent in agents if not agent.has_nats]
|
||||
minions = [agent.salt_id for agent in agents_salt]
|
||||
|
||||
if minions:
|
||||
Agent.salt_batch_async(
|
||||
minions=minions,
|
||||
func="win_agent.run_script",
|
||||
kwargs={
|
||||
"filepath": script.filepath,
|
||||
"filename": script.filename,
|
||||
"shell": script.shell,
|
||||
"timeout": timeout,
|
||||
"args": args,
|
||||
"bg": True if script.shell == "python" else False, # salt bg script bug
|
||||
},
|
||||
)
|
||||
|
||||
nats_data = {
|
||||
"func": "runscript",
|
||||
"timeout": timeout,
|
||||
|
|
|
@ -16,7 +16,6 @@ def get_debug_info():
|
|||
EXCLUDE_PATHS = (
|
||||
"/natsapi",
|
||||
"/api/v3",
|
||||
"/api/v2",
|
||||
"/logs/auditlogs",
|
||||
f"/{settings.ADMIN_URL}",
|
||||
"/logout",
|
||||
|
|
|
@ -58,7 +58,6 @@ INSTALLED_APPS = [
|
|||
"knox",
|
||||
"corsheaders",
|
||||
"accounts",
|
||||
"apiv2",
|
||||
"apiv3",
|
||||
"clients",
|
||||
"agents",
|
||||
|
|
|
@ -10,7 +10,6 @@ urlpatterns = [
|
|||
path("login/", LoginView.as_view()),
|
||||
path("logout/", knox_views.LogoutView.as_view()),
|
||||
path("logoutall/", knox_views.LogoutAllView.as_view()),
|
||||
path("api/v2/", include("apiv2.urls")),
|
||||
path("api/v3/", include("apiv3.urls")),
|
||||
path("clients/", include("clients.urls")),
|
||||
path("agents/", include("agents.urls")),
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
from time import sleep
|
||||
import asyncio
|
||||
import time
|
||||
from django.utils import timezone as djangotime
|
||||
from django.conf import settings
|
||||
import datetime as dt
|
||||
import pytz
|
||||
from loguru import logger
|
||||
from packaging import version as pyver
|
||||
from typing import List
|
||||
|
||||
from agents.models import Agent
|
||||
from .models import WinUpdate
|
||||
|
@ -23,22 +26,31 @@ def auto_approve_updates_task():
|
|||
except:
|
||||
continue
|
||||
|
||||
online = [i for i in agents if i.status == "online"]
|
||||
online = [
|
||||
i
|
||||
for i in agents
|
||||
if i.status == "online" and pyver.parse(i.version) >= pyver.parse("1.3.0")
|
||||
]
|
||||
|
||||
for agent in online:
|
||||
|
||||
# check for updates on agent
|
||||
check_for_updates_task.apply_async(
|
||||
queue="wupdate",
|
||||
kwargs={"pk": agent.pk, "wait": False, "auto_approve": True},
|
||||
)
|
||||
chunks = (online[i : i + 40] for i in range(0, len(online), 40))
|
||||
for chunk in chunks:
|
||||
for agent in chunk:
|
||||
asyncio.run(agent.nats_cmd({"func": "getwinupdates"}, wait=False))
|
||||
time.sleep(0.05)
|
||||
time.sleep(15)
|
||||
|
||||
|
||||
@app.task
|
||||
def check_agent_update_schedule_task():
|
||||
# scheduled task that installs updates on agents if enabled
|
||||
agents = Agent.objects.all()
|
||||
online = [i for i in agents if i.has_patches_pending and i.status == "online"]
|
||||
online = [
|
||||
i
|
||||
for i in agents
|
||||
if pyver.parse(i.version) >= pyver.parse("1.3.0")
|
||||
and i.has_patches_pending
|
||||
and i.status == "online"
|
||||
]
|
||||
|
||||
for agent in online:
|
||||
install = False
|
||||
|
@ -98,117 +110,38 @@ def check_agent_update_schedule_task():
|
|||
if install:
|
||||
# initiate update on agent asynchronously and don't worry about ret code
|
||||
logger.info(f"Installing windows updates on {agent.salt_id}")
|
||||
agent.salt_api_async(func="win_agent.install_updates")
|
||||
nats_data = {
|
||||
"func": "installwinupdates",
|
||||
"guids": agent.get_approved_update_guids(),
|
||||
}
|
||||
asyncio.run(agent.nats_cmd(nats_data, wait=False))
|
||||
agent.patches_last_installed = djangotime.now()
|
||||
agent.save(update_fields=["patches_last_installed"])
|
||||
|
||||
|
||||
@app.task
|
||||
def check_for_updates_task(pk, wait=False, auto_approve=False):
|
||||
|
||||
if wait:
|
||||
sleep(120)
|
||||
|
||||
agent = Agent.objects.get(pk=pk)
|
||||
ret = agent.salt_api_cmd(
|
||||
timeout=310,
|
||||
func="win_wua.list",
|
||||
arg="skip_installed=False",
|
||||
)
|
||||
|
||||
if ret == "timeout" or ret == "error":
|
||||
return
|
||||
|
||||
if isinstance(ret, str):
|
||||
err = ["unknown failure", "2147352567", "2145107934"]
|
||||
if any(x in ret.lower() for x in err):
|
||||
logger.warning(f"{agent.salt_id}: {ret}")
|
||||
return "failed"
|
||||
|
||||
guids = []
|
||||
try:
|
||||
for k in ret.keys():
|
||||
guids.append(k)
|
||||
except Exception as e:
|
||||
logger.error(f"{agent.salt_id}: {str(e)}")
|
||||
return
|
||||
|
||||
for i in guids:
|
||||
# check if existing update install / download status has changed
|
||||
if WinUpdate.objects.filter(agent=agent).filter(guid=i).exists():
|
||||
|
||||
update = WinUpdate.objects.filter(agent=agent).get(guid=i)
|
||||
|
||||
# salt will report an update as not installed even if it has been installed if a reboot is pending
|
||||
# ignore salt's return if the result field is 'success' as that means the agent has successfully installed the update
|
||||
if update.result != "success":
|
||||
if ret[i]["Installed"] != update.installed:
|
||||
update.installed = not update.installed
|
||||
update.save(update_fields=["installed"])
|
||||
|
||||
if ret[i]["Downloaded"] != update.downloaded:
|
||||
update.downloaded = not update.downloaded
|
||||
update.save(update_fields=["downloaded"])
|
||||
|
||||
# otherwise it's a new update
|
||||
else:
|
||||
WinUpdate(
|
||||
agent=agent,
|
||||
guid=i,
|
||||
kb=ret[i]["KBs"][0],
|
||||
mandatory=ret[i]["Mandatory"],
|
||||
title=ret[i]["Title"],
|
||||
needs_reboot=ret[i]["NeedsReboot"],
|
||||
installed=ret[i]["Installed"],
|
||||
downloaded=ret[i]["Downloaded"],
|
||||
description=ret[i]["Description"],
|
||||
severity=ret[i]["Severity"],
|
||||
).save()
|
||||
|
||||
agent.delete_superseded_updates()
|
||||
|
||||
# win_wua.list doesn't always return everything
|
||||
# use win_wua.installed to check for any updates that it missed
|
||||
# and then change update status to match
|
||||
installed = agent.salt_api_cmd(
|
||||
timeout=60, func="win_wua.installed", arg="kbs_only=True"
|
||||
)
|
||||
|
||||
if installed == "timeout" or installed == "error":
|
||||
pass
|
||||
elif isinstance(installed, list):
|
||||
agent.winupdates.filter(kb__in=installed).filter(installed=False).update(
|
||||
installed=True, downloaded=True
|
||||
)
|
||||
|
||||
# check if reboot needed. returns bool
|
||||
needs_reboot = agent.salt_api_cmd(timeout=30, func="win_wua.get_needs_reboot")
|
||||
|
||||
if needs_reboot == "timeout" or needs_reboot == "error":
|
||||
pass
|
||||
elif isinstance(needs_reboot, bool) and needs_reboot:
|
||||
agent.needs_reboot = True
|
||||
agent.save(update_fields=["needs_reboot"])
|
||||
else:
|
||||
agent.needs_reboot = False
|
||||
agent.save(update_fields=["needs_reboot"])
|
||||
|
||||
# approve updates if specified
|
||||
if auto_approve:
|
||||
agent.approve_updates()
|
||||
|
||||
return "ok"
|
||||
def bulk_install_updates_task(pks: List[int]) -> None:
|
||||
q = Agent.objects.filter(pk__in=pks)
|
||||
agents = [i for i in q if pyver.parse(i.version) >= pyver.parse("1.3.0")]
|
||||
chunks = (agents[i : i + 40] for i in range(0, len(agents), 40))
|
||||
for chunk in chunks:
|
||||
for agent in chunk:
|
||||
nats_data = {
|
||||
"func": "installwinupdates",
|
||||
"guids": agent.get_approved_update_guids(),
|
||||
}
|
||||
asyncio.run(agent.nats_cmd(nats_data, wait=False))
|
||||
time.sleep(0.05)
|
||||
time.sleep(15)
|
||||
|
||||
|
||||
@app.task
|
||||
def bulk_check_for_updates_task(minions):
|
||||
# don't flood the celery queue
|
||||
chunks = (minions[i : i + 30] for i in range(0, len(minions), 30))
|
||||
def bulk_check_for_updates_task(pks: List[int]) -> None:
|
||||
q = Agent.objects.filter(pk__in=pks)
|
||||
agents = [i for i in q if pyver.parse(i.version) >= pyver.parse("1.3.0")]
|
||||
chunks = (agents[i : i + 40] for i in range(0, len(agents), 40))
|
||||
for chunk in chunks:
|
||||
for i in chunk:
|
||||
agent = Agent.objects.get(salt_id=i)
|
||||
check_for_updates_task.apply_async(
|
||||
queue="wupdate",
|
||||
kwargs={"pk": agent.pk, "wait": False, "auto_approve": True},
|
||||
)
|
||||
sleep(30)
|
||||
for agent in chunk:
|
||||
asyncio.run(agent.nats_cmd({"func": "getwinupdates"}, wait=False))
|
||||
time.sleep(0.05)
|
||||
time.sleep(15)
|
||||
|
|
|
@ -29,7 +29,7 @@ class TestWinUpdateViews(TacticalTestCase):
|
|||
|
||||
self.check_not_authenticated("get", url)
|
||||
|
||||
@patch("winupdate.tasks.check_for_updates_task.apply_async")
|
||||
""" @patch("winupdate.tasks.check_for_updates_task.apply_async")
|
||||
def test_run_update_scan(self, mock_task):
|
||||
|
||||
# test a call where agent doesn't exist
|
||||
|
@ -46,9 +46,9 @@ class TestWinUpdateViews(TacticalTestCase):
|
|||
kwargs={"pk": agent.pk, "wait": False, "auto_approve": True},
|
||||
)
|
||||
|
||||
self.check_not_authenticated("get", url)
|
||||
self.check_not_authenticated("get", url) """
|
||||
|
||||
@patch("agents.models.Agent.salt_api_cmd")
|
||||
""" @patch("agents.models.Agent.salt_api_cmd")
|
||||
def test_install_updates(self, mock_cmd):
|
||||
|
||||
# test a call where agent doesn't exist
|
||||
|
@ -84,7 +84,7 @@ class TestWinUpdateViews(TacticalTestCase):
|
|||
resp = self.client.get(url, format="json")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
self.check_not_authenticated("get", url)
|
||||
self.check_not_authenticated("get", url) """
|
||||
|
||||
def test_edit_policy(self):
|
||||
url = "/winupdate/editpolicy/"
|
||||
|
@ -144,7 +144,7 @@ class WinupdateTasks(TacticalTestCase):
|
|||
for update in winupdates:
|
||||
self.assertEqual(update.action, "approve")
|
||||
|
||||
@patch("agents.models.Agent.salt_api_async")
|
||||
""" @patch("agents.models.Agent.salt_api_async")
|
||||
def test_check_agent_update_daily_schedule(self, agent_salt_cmd):
|
||||
from .tasks import check_agent_update_schedule_task
|
||||
|
||||
|
@ -173,7 +173,7 @@ class WinupdateTasks(TacticalTestCase):
|
|||
|
||||
check_agent_update_schedule_task()
|
||||
agent_salt_cmd.assert_called_with(func="win_agent.install_updates")
|
||||
self.assertEquals(agent_salt_cmd.call_count, 2)
|
||||
self.assertEquals(agent_salt_cmd.call_count, 2) """
|
||||
|
||||
""" @patch("agents.models.Agent.salt_api_async")
|
||||
def test_check_agent_update_monthly_schedule(self, agent_salt_cmd):
|
||||
|
@ -204,110 +204,4 @@ class WinupdateTasks(TacticalTestCase):
|
|||
|
||||
check_agent_update_schedule_task()
|
||||
agent_salt_cmd.assert_called_with(func="win_agent.install_updates")
|
||||
self.assertEquals(agent_salt_cmd.call_count, 2) """
|
||||
|
||||
@patch("agents.models.Agent.salt_api_cmd")
|
||||
def test_check_for_updates(self, salt_api_cmd):
|
||||
from .tasks import check_for_updates_task
|
||||
|
||||
# create a matching update returned from salt
|
||||
baker.make_recipe(
|
||||
"winupdate.approved_winupdate",
|
||||
agent=self.online_agents[0],
|
||||
kb="KB12341234",
|
||||
guid="GUID1",
|
||||
downloaded=True,
|
||||
severity="",
|
||||
installed=True,
|
||||
)
|
||||
|
||||
salt_success_return = {
|
||||
"GUID1": {
|
||||
"Title": "Update Title",
|
||||
"KBs": ["KB12341234"],
|
||||
"GUID": "GUID1",
|
||||
"Description": "Description",
|
||||
"Downloaded": False,
|
||||
"Installed": False,
|
||||
"Mandatory": False,
|
||||
"Severity": "",
|
||||
"NeedsReboot": True,
|
||||
},
|
||||
"GUID2": {
|
||||
"Title": "Update Title 2",
|
||||
"KBs": ["KB12341235"],
|
||||
"GUID": "GUID2",
|
||||
"Description": "Description",
|
||||
"Downloaded": False,
|
||||
"Installed": True,
|
||||
"Mandatory": False,
|
||||
"Severity": "",
|
||||
"NeedsReboot": True,
|
||||
},
|
||||
}
|
||||
|
||||
salt_kb_list = ["KB12341235"]
|
||||
|
||||
# mock failed attempt
|
||||
salt_api_cmd.return_value = "timeout"
|
||||
ret = check_for_updates_task(self.online_agents[0].pk)
|
||||
salt_api_cmd.assert_called_with(
|
||||
timeout=310,
|
||||
func="win_wua.list",
|
||||
arg="skip_installed=False",
|
||||
)
|
||||
self.assertFalse(ret)
|
||||
salt_api_cmd.reset_mock()
|
||||
|
||||
# mock failed attempt
|
||||
salt_api_cmd.return_value = "error"
|
||||
ret = check_for_updates_task(self.online_agents[0].pk)
|
||||
salt_api_cmd.assert_called_with(
|
||||
timeout=310,
|
||||
func="win_wua.list",
|
||||
arg="skip_installed=False",
|
||||
)
|
||||
self.assertFalse(ret)
|
||||
salt_api_cmd.reset_mock()
|
||||
|
||||
# mock failed attempt
|
||||
salt_api_cmd.return_value = "unknown failure"
|
||||
ret = check_for_updates_task(self.online_agents[0].pk)
|
||||
salt_api_cmd.assert_called_with(
|
||||
timeout=310,
|
||||
func="win_wua.list",
|
||||
arg="skip_installed=False",
|
||||
)
|
||||
self.assertEquals(ret, "failed")
|
||||
salt_api_cmd.reset_mock()
|
||||
|
||||
# mock failed attempt at salt list updates with reboot
|
||||
salt_api_cmd.side_effect = [salt_success_return, "timeout", True]
|
||||
ret = check_for_updates_task(self.online_agents[0].pk)
|
||||
salt_api_cmd.assert_any_call(
|
||||
timeout=310,
|
||||
func="win_wua.list",
|
||||
arg="skip_installed=False",
|
||||
)
|
||||
salt_api_cmd.assert_any_call(
|
||||
timeout=60, func="win_wua.installed", arg="kbs_only=True"
|
||||
)
|
||||
|
||||
salt_api_cmd.assert_any_call(timeout=30, func="win_wua.get_needs_reboot")
|
||||
|
||||
salt_api_cmd.reset_mock()
|
||||
|
||||
# mock successful attempt without reboot
|
||||
salt_api_cmd.side_effect = [salt_success_return, salt_kb_list, False]
|
||||
ret = check_for_updates_task(self.online_agents[0].pk)
|
||||
salt_api_cmd.assert_any_call(
|
||||
timeout=310,
|
||||
func="win_wua.list",
|
||||
arg="skip_installed=False",
|
||||
)
|
||||
|
||||
salt_api_cmd.assert_any_call(
|
||||
timeout=60, func="win_wua.installed", arg="kbs_only=True"
|
||||
)
|
||||
|
||||
salt_api_cmd.assert_any_call(timeout=30, func="win_wua.get_needs_reboot")
|
||||
self.assertEquals(agent_salt_cmd.call_count, 2) """
|
|
@ -1,10 +1,8 @@
|
|||
import asyncio
|
||||
from packaging import version as pyver
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from rest_framework.decorators import (
|
||||
api_view,
|
||||
authentication_classes,
|
||||
permission_classes,
|
||||
)
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.authentication import TokenAuthentication
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
@ -12,7 +10,6 @@ from rest_framework.permissions import IsAuthenticated
|
|||
from agents.models import Agent
|
||||
from .models import WinUpdate
|
||||
from .serializers import UpdateSerializer, ApprovedUpdateSerializer
|
||||
from .tasks import check_for_updates_task
|
||||
from tacticalrmm.utils import notify_error
|
||||
|
||||
|
||||
|
@ -25,30 +22,24 @@ def get_win_updates(request, pk):
|
|||
@api_view()
|
||||
def run_update_scan(request, pk):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
check_for_updates_task.apply_async(
|
||||
queue="wupdate", kwargs={"pk": agent.pk, "wait": False, "auto_approve": True}
|
||||
)
|
||||
if pyver.parse(agent.version) < pyver.parse("1.3.0"):
|
||||
return notify_error("Requires agent version 1.3.0 or greater")
|
||||
|
||||
asyncio.run(agent.nats_cmd({"func": "getwinupdates"}, wait=False))
|
||||
return Response("ok")
|
||||
|
||||
|
||||
@api_view()
|
||||
def install_updates(request, pk):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
r = agent.salt_api_cmd(timeout=15, func="win_agent.install_updates")
|
||||
|
||||
if r == "timeout":
|
||||
return notify_error("Unable to contact the agent")
|
||||
elif r == "error":
|
||||
return notify_error("Something went wrong")
|
||||
elif r == "running":
|
||||
return notify_error(f"Updates are already being installed on {agent.hostname}")
|
||||
|
||||
# successful response: {'return': [{'SALT-ID': {'pid': 3316}}]}
|
||||
try:
|
||||
r["pid"]
|
||||
except (KeyError):
|
||||
return notify_error(str(r))
|
||||
if pyver.parse(agent.version) < pyver.parse("1.3.0"):
|
||||
return notify_error("Requires agent version 1.3.0 or greater")
|
||||
|
||||
nats_data = {
|
||||
"func": "installwinupdates",
|
||||
"guids": agent.get_approved_update_guids(),
|
||||
}
|
||||
asyncio.run(agent.nats_cmd(nats_data, wait=False))
|
||||
return Response(f"Patches will now be installed on {agent.hostname}")
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
SCRIPT_VERSION="6"
|
||||
SCRIPT_VERSION="7"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh'
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
|
@ -61,7 +61,6 @@ sysd="/etc/systemd/system"
|
|||
|
||||
mkdir -p ${tmp_dir}/meshcentral/mongo
|
||||
mkdir ${tmp_dir}/postgres
|
||||
mkdir ${tmp_dir}/salt
|
||||
mkdir ${tmp_dir}/certs
|
||||
mkdir ${tmp_dir}/nginx
|
||||
mkdir ${tmp_dir}/systemd
|
||||
|
@ -74,16 +73,13 @@ pg_dump --dbname=postgresql://"${POSTGRES_USER}":"${POSTGRES_PW}"@127.0.0.1:5432
|
|||
tar -czvf ${tmp_dir}/meshcentral/mesh.tar.gz --exclude=/meshcentral/node_modules /meshcentral
|
||||
mongodump --gzip --out=${tmp_dir}/meshcentral/mongo
|
||||
|
||||
sudo tar -czvf ${tmp_dir}/salt/etc-salt.tar.gz -C /etc/salt .
|
||||
tar -czvf ${tmp_dir}/salt/srv-salt.tar.gz -C /srv/salt .
|
||||
|
||||
sudo tar -czvf ${tmp_dir}/certs/etc-letsencrypt.tar.gz -C /etc/letsencrypt .
|
||||
|
||||
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 cp ${sysd}/rmm.service ${sysd}/celery.service ${sysd}/celerybeat.service ${sysd}/celery-winupdate.service ${sysd}/meshcentral.service ${sysd}/nats.service ${sysd}/natsapi.service ${tmp_dir}/systemd/
|
||||
sudo cp ${sysd}/rmm.service ${sysd}/celery.service ${sysd}/celerybeat.service ${sysd}/meshcentral.service ${sysd}/nats.service ${sysd}/natsapi.service ${tmp_dir}/systemd/
|
||||
|
||||
cat /rmm/api/tacticalrmm/tacticalrmm/private/log/debug.log | gzip -9 > ${tmp_dir}/rmm/debug.log.gz
|
||||
cp /rmm/api/tacticalrmm/tacticalrmm/local_settings.py /rmm/api/tacticalrmm/app.ini ${tmp_dir}/rmm/
|
||||
|
|
|
@ -18,7 +18,7 @@ sudo certbot certonly --manual -d *.example.com --agree-tos --no-bootstrap --man
|
|||
|
||||
## Configure DNS and firewall
|
||||
|
||||
You will need to add DNS entries so that the three subdomains resolve to the IP of the docker host. There is a reverse proxy running that will route the hostnames to the correct container. On the host, you will need to ensure the firewall is open on tcp ports 80, 443, 4222, 4505, 4506.
|
||||
You will need to add DNS entries so that the three subdomains resolve to the IP of the docker host. There is a reverse proxy running that will route the hostnames to the correct container. On the host, you will need to ensure the firewall is open on tcp ports 80, 443 and 4222.
|
||||
|
||||
## Setting up the environment
|
||||
|
||||
|
|
120
install.sh
120
install.sh
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
SCRIPT_VERSION="33"
|
||||
SCRIPT_VERSION="34"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/install.sh'
|
||||
|
||||
sudo apt install -y curl wget
|
||||
|
@ -184,11 +184,6 @@ CERT_PUB_KEY=/etc/letsencrypt/live/${rootdomain}/fullchain.pem
|
|||
sudo chown ${USER}:${USER} -R /etc/letsencrypt
|
||||
sudo chmod 775 -R /etc/letsencrypt
|
||||
|
||||
print_green 'Creating saltapi user'
|
||||
|
||||
sudo adduser --no-create-home --disabled-password --gecos "" saltapi
|
||||
echo "saltapi:${SALTPW}" | sudo chpasswd
|
||||
|
||||
print_green 'Installing golang'
|
||||
|
||||
sudo mkdir -p /usr/local/rmmgo
|
||||
|
@ -239,7 +234,7 @@ sudo systemctl restart mongod
|
|||
print_green 'Installing python, redis and git'
|
||||
|
||||
sudo apt update
|
||||
sudo apt install -y python3-venv python3-dev python3-pip python3-cherrypy3 python3-setuptools python3-wheel ca-certificates redis git
|
||||
sudo apt install -y python3-venv python3-dev python3-pip python3-setuptools python3-wheel ca-certificates redis git
|
||||
|
||||
print_green 'Installing postgresql'
|
||||
|
||||
|
@ -359,9 +354,6 @@ if not DEBUG:
|
|||
)
|
||||
})
|
||||
|
||||
SALT_USERNAME = "saltapi"
|
||||
SALT_PASSWORD = "${SALTPW}"
|
||||
SALT_HOST = "127.0.0.1"
|
||||
MESH_USERNAME = "${meshusername}"
|
||||
MESH_SITE = "https://${meshdomain}"
|
||||
REDIS_HOST = "localhost"
|
||||
|
@ -602,46 +594,6 @@ echo "${nginxmesh}" | sudo tee /etc/nginx/sites-available/meshcentral.conf > /de
|
|||
sudo ln -s /etc/nginx/sites-available/rmm.conf /etc/nginx/sites-enabled/rmm.conf
|
||||
sudo ln -s /etc/nginx/sites-available/meshcentral.conf /etc/nginx/sites-enabled/meshcentral.conf
|
||||
|
||||
print_green 'Installing Salt Master'
|
||||
wget -O - 'https://repo.saltstack.com/py3/'$osname'/'$fullrelno'/amd64/latest/SALTSTACK-GPG-KEY.pub' | sudo apt-key add -
|
||||
echo 'deb http://repo.saltstack.com/py3/'$osname'/'$fullrelno'/amd64/latest '$codename' main' | sudo tee /etc/apt/sources.list.d/saltstack.list
|
||||
|
||||
sudo apt update
|
||||
sudo apt install -y salt-master
|
||||
|
||||
print_green 'Waiting 10 seconds for salt to start'
|
||||
sleep 10
|
||||
|
||||
saltvars="$(cat << EOF
|
||||
timeout: 20
|
||||
gather_job_timeout: 25
|
||||
max_event_size: 30485760
|
||||
external_auth:
|
||||
pam:
|
||||
saltapi:
|
||||
- .*
|
||||
- '@runner'
|
||||
- '@wheel'
|
||||
- '@jobs'
|
||||
|
||||
rest_cherrypy:
|
||||
port: 8123
|
||||
disable_ssl: True
|
||||
max_request_body_size: 30485760
|
||||
|
||||
EOF
|
||||
)"
|
||||
echo "${saltvars}" | sudo tee /etc/salt/master.d/rmm-salt.conf > /dev/null
|
||||
|
||||
# fix the stupid 1 MB limit present in msgpack 0.6.2, which btw was later changed to 100 MB in msgpack 1.0.0
|
||||
# but 0.6.2 is the default on ubuntu 20
|
||||
sudo sed -i 's/msgpack_kwargs = {"raw": six.PY2}/msgpack_kwargs = {"raw": six.PY2, "max_buffer_size": 2147483647}/g' /usr/lib/python3/dist-packages/salt/transport/ipc.py
|
||||
|
||||
|
||||
|
||||
print_green 'Installing Salt API'
|
||||
sudo apt install -y salt-api
|
||||
|
||||
sudo mkdir /etc/conf.d
|
||||
|
||||
celeryservice="$(cat << EOF
|
||||
|
@ -676,7 +628,7 @@ CELERY_APP="tacticalrmm"
|
|||
|
||||
CELERYD_MULTI="multi"
|
||||
|
||||
CELERYD_OPTS="--time-limit=2900 --autoscale=50,5"
|
||||
CELERYD_OPTS="--time-limit=9999 --autoscale=100,5"
|
||||
|
||||
CELERYD_PID_FILE="/rmm/api/tacticalrmm/%n.pid"
|
||||
CELERYD_LOG_FILE="/var/log/celery/%n%I.log"
|
||||
|
@ -688,44 +640,6 @@ EOF
|
|||
)"
|
||||
echo "${celeryconf}" | sudo tee /etc/conf.d/celery.conf > /dev/null
|
||||
|
||||
celerywinupdatesvc="$(cat << EOF
|
||||
[Unit]
|
||||
Description=Celery WinUpdate Service V2
|
||||
After=network.target redis-server.service postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
User=${USER}
|
||||
Group=${USER}
|
||||
EnvironmentFile=/etc/conf.d/celery-winupdate.conf
|
||||
WorkingDirectory=/rmm/api/tacticalrmm
|
||||
ExecStart=/bin/sh -c '\${CELERY_BIN} -A \$CELERY_APP multi start \$CELERYD_NODES --pidfile=\${CELERYD_PID_FILE} --logfile=\${CELERYD_LOG_FILE} --loglevel="\${CELERYD_LOG_LEVEL}" -Q wupdate \$CELERYD_OPTS'
|
||||
ExecStop=/bin/sh -c '\${CELERY_BIN} multi stopwait \$CELERYD_NODES --pidfile=\${CELERYD_PID_FILE} --loglevel="\${CELERYD_LOG_LEVEL}"'
|
||||
ExecReload=/bin/sh -c '\${CELERY_BIN} -A \$CELERY_APP multi restart \$CELERYD_NODES --pidfile=\${CELERYD_PID_FILE} --logfile=\${CELERYD_LOG_FILE} --loglevel="\${CELERYD_LOG_LEVEL}" -Q wupdate \$CELERYD_OPTS'
|
||||
Restart=always
|
||||
RestartSec=10s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
)"
|
||||
echo "${celerywinupdatesvc}" | sudo tee /etc/systemd/system/celery-winupdate.service > /dev/null
|
||||
|
||||
celerywinupdate="$(cat << EOF
|
||||
CELERYD_NODES="w2"
|
||||
|
||||
CELERY_BIN="/rmm/api/env/bin/celery"
|
||||
CELERY_APP="tacticalrmm"
|
||||
CELERYD_MULTI="multi"
|
||||
|
||||
CELERYD_OPTS="--time-limit=4000 --autoscale=40,1"
|
||||
|
||||
CELERYD_PID_FILE="/rmm/api/tacticalrmm/%n.pid"
|
||||
CELERYD_LOG_FILE="/var/log/celery/%n%I.log"
|
||||
CELERYD_LOG_LEVEL="ERROR"
|
||||
EOF
|
||||
)"
|
||||
echo "${celerywinupdate}" | sudo tee /etc/conf.d/celery-winupdate.conf > /dev/null
|
||||
|
||||
celerybeatservice="$(cat << EOF
|
||||
[Unit]
|
||||
|
@ -748,21 +662,12 @@ EOF
|
|||
)"
|
||||
echo "${celerybeatservice}" | sudo tee /etc/systemd/system/celerybeat.service > /dev/null
|
||||
|
||||
sudo mkdir -p /srv/salt
|
||||
sudo cp -r /rmm/_modules /srv/salt/
|
||||
sudo cp -r /rmm/scripts /srv/salt/
|
||||
sudo mkdir /srv/salt/scripts/userdefined
|
||||
sudo chown ${USER}:${USER} -R /srv/salt/
|
||||
sudo chown ${USER}:www-data /srv/salt/scripts/userdefined
|
||||
sudo chmod 750 /srv/salt/scripts/userdefined
|
||||
sudo chown ${USER}:${USER} -R /etc/conf.d/
|
||||
|
||||
meshservice="$(cat << EOF
|
||||
[Unit]
|
||||
Description=MeshCentral Server
|
||||
After=network.target
|
||||
After=mongod.service
|
||||
After=nginx.service
|
||||
After=network.target mongod.service nginx.service
|
||||
[Service]
|
||||
Type=simple
|
||||
LimitNOFILE=1000000
|
||||
|
@ -782,12 +687,6 @@ echo "${meshservice}" | sudo tee /etc/systemd/system/meshcentral.service > /dev/
|
|||
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
|
||||
sudo systemctl enable salt-master
|
||||
sudo systemctl enable salt-api
|
||||
|
||||
sudo systemctl restart salt-api
|
||||
|
||||
sudo chown -R $USER:$GROUP /home/${USER}/.npm
|
||||
sudo chown -R $USER:$GROUP /home/${USER}/.config
|
||||
|
||||
|
@ -844,7 +743,7 @@ sudo ln -s /etc/nginx/sites-available/frontend.conf /etc/nginx/sites-enabled/fro
|
|||
|
||||
print_green 'Enabling Services'
|
||||
|
||||
for i in rmm.service celery.service celerybeat.service celery-winupdate.service nginx
|
||||
for i in rmm.service celery.service celerybeat.service nginx
|
||||
do
|
||||
sudo systemctl enable ${i}
|
||||
sudo systemctl stop ${i}
|
||||
|
@ -912,17 +811,12 @@ sudo systemctl start nats.service
|
|||
|
||||
|
||||
print_green 'Restarting services'
|
||||
for i in rmm.service celery.service celerybeat.service celery-winupdate.service natsapi.service
|
||||
for i in rmm.service celery.service celerybeat.service natsapi.service
|
||||
do
|
||||
sudo systemctl stop ${i}
|
||||
sudo systemctl start ${i}
|
||||
done
|
||||
|
||||
print_green 'Restarting salt-master and waiting 10 seconds'
|
||||
sudo systemctl restart salt-master
|
||||
sleep 10
|
||||
sudo systemctl restart salt-api
|
||||
|
||||
printf >&2 "${YELLOW}%0.s*${NC}" {1..80}
|
||||
printf >&2 "\n\n"
|
||||
printf >&2 "${YELLOW}Installation complete!${NC}\n\n"
|
||||
|
@ -938,7 +832,7 @@ if [ "$BEHIND_NAT" = true ]; then
|
|||
echo -ne "${GREEN}If you will be accessing the web interface of the RMM from the same LAN as this server,${NC}\n"
|
||||
echo -ne "${GREEN}you'll need to make sure your 3 subdomains resolve to ${IPV4}${NC}\n"
|
||||
echo -ne "${GREEN}This also applies to any agents that will be on the same local network as the rmm.${NC}\n"
|
||||
echo -ne "${GREEN}You'll also need to setup port forwarding in your router on ports 80, 443, 4505, 4506 and 4222 tcp.${NC}\n\n"
|
||||
echo -ne "${GREEN}You'll also need to setup port forwarding in your router on ports 80, 443 and 4222 tcp.${NC}\n\n"
|
||||
fi
|
||||
|
||||
printf >&2 "${YELLOW}Please refer to the github README for next steps${NC}\n\n"
|
||||
|
|
54
restore.sh
54
restore.sh
|
@ -7,7 +7,7 @@ pgpw="hunter2"
|
|||
|
||||
#####################################################
|
||||
|
||||
SCRIPT_VERSION="12"
|
||||
SCRIPT_VERSION="13"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/restore.sh'
|
||||
|
||||
sudo apt install -y curl wget
|
||||
|
@ -166,15 +166,9 @@ print_green 'Restoring systemd services'
|
|||
sudo cp $tmp_dir/systemd/* /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
print_green 'Restoring saltapi user'
|
||||
|
||||
SALTPW=$(grep SALT_PASSWORD $tmp_dir/rmm/local_settings.py | tr -d " \t" | sed 's/.*=//' | tr -d '"')
|
||||
sudo adduser --no-create-home --disabled-password --gecos "" saltapi
|
||||
echo "saltapi:${SALTPW}" | sudo chpasswd
|
||||
|
||||
print_green 'Installing python, redis and git'
|
||||
|
||||
sudo apt install -y python3.8-venv python3.8-dev python3-pip python3-cherrypy3 python3-setuptools python3-wheel ca-certificates redis git
|
||||
sudo apt install -y python3-venv python3-dev python3-pip python3-setuptools python3-wheel ca-certificates redis git
|
||||
|
||||
print_green 'Installing postgresql'
|
||||
|
||||
|
@ -261,40 +255,6 @@ deactivate
|
|||
sudo systemctl enable nats.service
|
||||
sudo systemctl start nats.service
|
||||
|
||||
print_green 'Installing Salt Master'
|
||||
|
||||
wget -O - https://repo.saltstack.com/py3/ubuntu/20.04/amd64/latest/SALTSTACK-GPG-KEY.pub | sudo apt-key add -
|
||||
echo 'deb http://repo.saltstack.com/py3/ubuntu/20.04/amd64/latest focal main' | sudo tee /etc/apt/sources.list.d/saltstack.list
|
||||
|
||||
sudo apt update
|
||||
sudo apt install -y salt-master
|
||||
|
||||
print_green 'Waiting 10 seconds for salt to start'
|
||||
sleep 10
|
||||
|
||||
print_green 'Installing Salt API'
|
||||
sudo apt install -y salt-api
|
||||
|
||||
sudo sed -i 's/msgpack_kwargs = {"raw": six.PY2}/msgpack_kwargs = {"raw": six.PY2, "max_buffer_size": 2147483647}/g' /usr/lib/python3/dist-packages/salt/transport/ipc.py
|
||||
|
||||
sudo systemctl enable salt-master
|
||||
sudo systemctl enable salt-api
|
||||
sudo systemctl restart salt-api
|
||||
sleep 3
|
||||
|
||||
print_green 'Restoring salt keys'
|
||||
|
||||
sudo systemctl stop salt-master
|
||||
sudo systemctl stop salt-api
|
||||
sudo rm -rf /etc/salt
|
||||
sudo mkdir /etc/salt
|
||||
sudo tar -xzf $tmp_dir/salt/etc-salt.tar.gz -C /etc/salt
|
||||
sudo mkdir -p /srv/salt
|
||||
sudo tar -xzf $tmp_dir/salt/srv-salt.tar.gz -C /srv/salt
|
||||
sudo chown ${USER}:${USER} -R /srv/salt/
|
||||
sudo chown ${USER}:www-data /srv/salt/scripts/userdefined
|
||||
sudo chmod 750 /srv/salt/scripts/userdefined
|
||||
|
||||
print_green 'Restoring the frontend'
|
||||
|
||||
sudo chown -R $USER:$GROUP /home/${USER}/.npm
|
||||
|
@ -310,18 +270,15 @@ sudo chown www-data:www-data -R /var/www/rmm/dist
|
|||
# reset perms
|
||||
sudo chown ${USER}:${USER} -R /rmm
|
||||
sudo chown ${USER}:${USER} /var/log/celery
|
||||
sudo chown ${USER}:${USER} -R /srv/salt/
|
||||
sudo chown ${USER}:${USER} -R /etc/conf.d/
|
||||
sudo chown ${USER}:www-data /srv/salt/scripts/userdefined
|
||||
sudo chown -R $USER:$GROUP /home/${USER}/.npm
|
||||
sudo chown -R $USER:$GROUP /home/${USER}/.config
|
||||
sudo chown -R $USER:$GROUP /home/${USER}/.cache
|
||||
sudo chmod 750 /srv/salt/scripts/userdefined
|
||||
|
||||
print_green 'Enabling Services'
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
for i in celery.service celerybeat.service celery-winupdate.service rmm.service nginx
|
||||
for i in celery.service celerybeat.service rmm.service nginx
|
||||
do
|
||||
sudo systemctl enable ${i}
|
||||
sudo systemctl stop ${i}
|
||||
|
@ -337,11 +294,6 @@ print_green 'Starting natsapi'
|
|||
sudo systemctl enable natsapi.service
|
||||
sudo systemctl start natsapi.service
|
||||
|
||||
print_green 'Restarting salt and waiting 10 seconds'
|
||||
sudo systemctl restart salt-master
|
||||
sleep 10
|
||||
sudo systemctl restart salt-api
|
||||
|
||||
printf >&2 "${YELLOW}%0.s*${NC}" {1..80}
|
||||
printf >&2 "\n\n"
|
||||
printf >&2 "${YELLOW}Restore complete!${NC}\n\n"
|
||||
|
|
79
update.sh
79
update.sh
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
SCRIPT_VERSION="103"
|
||||
SCRIPT_VERSION="104"
|
||||
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'
|
||||
YELLOW='\033[1;33m'
|
||||
|
@ -107,41 +107,6 @@ sudo systemctl enable celerybeat.service
|
|||
|
||||
fi
|
||||
|
||||
CHECK_CELERYWINUPDATE_V2=$(grep V2 /etc/systemd/system/celery-winupdate.service)
|
||||
if ! [[ $CHECK_CELERYWINUPDATE_V2 ]]; then
|
||||
printf >&2 "${GREEN}Updating celery-winupdate.service${NC}\n"
|
||||
sudo systemctl stop celery-winupdate.service
|
||||
sudo rm -f /etc/systemd/system/celery-winupdate.service
|
||||
|
||||
celerywinupdatesvc="$(cat << EOF
|
||||
[Unit]
|
||||
Description=Celery WinUpdate Service V2
|
||||
After=network.target redis-server.service postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
User=${USER}
|
||||
Group=${USER}
|
||||
EnvironmentFile=/etc/conf.d/celery-winupdate.conf
|
||||
WorkingDirectory=/rmm/api/tacticalrmm
|
||||
ExecStart=/bin/sh -c '\${CELERY_BIN} -A \$CELERY_APP multi start \$CELERYD_NODES --pidfile=\${CELERYD_PID_FILE} --logfile=\${CELERYD_LOG_FILE} --loglevel="\${CELERYD_LOG_LEVEL}" -Q wupdate \$CELERYD_OPTS'
|
||||
ExecStop=/bin/sh -c '\${CELERY_BIN} multi stopwait \$CELERYD_NODES --pidfile=\${CELERYD_PID_FILE} --loglevel="\${CELERYD_LOG_LEVEL}"'
|
||||
ExecReload=/bin/sh -c '\${CELERY_BIN} -A \$CELERY_APP multi restart \$CELERYD_NODES --pidfile=\${CELERYD_PID_FILE} --logfile=\${CELERYD_LOG_FILE} --loglevel="\${CELERYD_LOG_LEVEL}" -Q wupdate \$CELERYD_OPTS'
|
||||
Restart=always
|
||||
RestartSec=10s
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
)"
|
||||
echo "${celerywinupdatesvc}" | sudo tee /etc/systemd/system/celery-winupdate.service > /dev/null
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable celery-winupdate.service
|
||||
|
||||
fi
|
||||
|
||||
|
||||
TMP_SETTINGS=$(mktemp -p "" "rmmsettings_XXXXXXXXXX")
|
||||
curl -s -L "${LATEST_SETTINGS_URL}" > ${TMP_SETTINGS}
|
||||
SETTINGS_FILE="/rmm/api/tacticalrmm/tacticalrmm/settings.py"
|
||||
|
@ -158,7 +123,6 @@ fi
|
|||
LATEST_MESH_VER=$(grep "^MESH_VER" "$TMP_SETTINGS" | awk -F'[= "]' '{print $5}')
|
||||
LATEST_PIP_VER=$(grep "^PIP_VER" "$TMP_SETTINGS" | awk -F'[= "]' '{print $5}')
|
||||
LATEST_NPM_VER=$(grep "^NPM_VER" "$TMP_SETTINGS" | awk -F'[= "]' '{print $5}')
|
||||
LATEST_SALT_VER=$(grep "^SALT_MASTER_VER" "$TMP_SETTINGS" | awk -F'[= "]' '{print $5}')
|
||||
|
||||
CURRENT_PIP_VER=$(grep "^PIP_VER" "$SETTINGS_FILE" | awk -F'[= "]' '{print $5}')
|
||||
CURRENT_NPM_VER=$(grep "^NPM_VER" "$SETTINGS_FILE" | awk -F'[= "]' '{print $5}')
|
||||
|
@ -187,7 +151,15 @@ sudo systemctl daemon-reload
|
|||
sudo systemctl enable natsapi.service
|
||||
fi
|
||||
|
||||
for i in salt-master salt-api nginx nats natsapi rmm celery celerybeat celery-winupdate
|
||||
if [ -f /etc/systemd/system/celery-winupdate.service ]; then
|
||||
printf >&2 "${GREEN}Removing celery-winupdate.service${NC}\n"
|
||||
sudo systemctl stop celery-winupdate.service
|
||||
sudo systemctl disable celery-winupdate.service
|
||||
sudo rm -f /etc/systemd/system/celery-winupdate.service
|
||||
sudo systemctl daemon-reload
|
||||
fi
|
||||
|
||||
for i in nginx nats natsapi rmm celery celerybeat
|
||||
do
|
||||
printf >&2 "${GREEN}Stopping ${i} service...${NC}\n"
|
||||
sudo systemctl stop ${i}
|
||||
|
@ -208,38 +180,16 @@ git reset --hard FETCH_HEAD
|
|||
git clean -df
|
||||
git pull
|
||||
|
||||
CHECK_SALT=$(sudo salt --version | grep ${LATEST_SALT_VER})
|
||||
if ! [[ $CHECK_SALT ]]; then
|
||||
printf >&2 "${GREEN}Updating salt${NC}\n"
|
||||
sudo apt update
|
||||
sudo apt install -y salt-master salt-api salt-common
|
||||
printf >&2 "${GREEN}Waiting for salt...${NC}\n"
|
||||
sleep 15
|
||||
sudo systemctl stop salt-master
|
||||
sudo systemctl stop salt-api
|
||||
printf >&2 "${GREEN}Fixing msgpack${NC}\n"
|
||||
sudo sed -i 's/msgpack_kwargs = {"raw": six.PY2}/msgpack_kwargs = {"raw": six.PY2, "max_buffer_size": 2147483647}/g' /usr/lib/python3/dist-packages/salt/transport/ipc.py
|
||||
sudo systemctl start salt-master
|
||||
printf >&2 "${GREEN}Waiting for salt...${NC}\n"
|
||||
sleep 15
|
||||
sudo systemctl start salt-api
|
||||
printf >&2 "${GREEN}Salt update finished${NC}\n"
|
||||
fi
|
||||
|
||||
sudo chown ${USER}:${USER} -R /rmm
|
||||
sudo chown ${USER}:${USER} /var/log/celery
|
||||
sudo chown ${USER}:${USER} -R /srv/salt/
|
||||
sudo chown ${USER}:${USER} -R /etc/conf.d/
|
||||
sudo chown ${USER}:www-data /srv/salt/scripts/userdefined
|
||||
sudo chown -R $USER:$GROUP /home/${USER}/.npm
|
||||
sudo chown -R $USER:$GROUP /home/${USER}/.config
|
||||
sudo chown -R $USER:$GROUP /home/${USER}/.cache
|
||||
sudo chmod 750 /srv/salt/scripts/userdefined
|
||||
sudo chown ${USER}:${USER} -R /etc/letsencrypt
|
||||
sudo chmod 775 -R /etc/letsencrypt
|
||||
|
||||
cp /rmm/_modules/* /srv/salt/_modules/
|
||||
cp /rmm/scripts/* /srv/salt/scripts/
|
||||
/usr/local/rmmgo/go/bin/go get github.com/josephspurrier/goversioninfo/cmd/goversioninfo
|
||||
sudo cp /rmm/api/tacticalrmm/core/goinstaller/bin/goversioninfo /usr/local/bin/
|
||||
sudo chown ${USER}:${USER} /usr/local/bin/goversioninfo
|
||||
|
@ -272,7 +222,6 @@ fi
|
|||
python manage.py pre_update_tasks
|
||||
python manage.py migrate
|
||||
python manage.py delete_tokens
|
||||
python manage.py fix_salt_key
|
||||
python manage.py collectstatic --no-input
|
||||
python manage.py reload_nats
|
||||
python manage.py load_chocos
|
||||
|
@ -292,13 +241,7 @@ sudo rm -rf /var/www/rmm/dist
|
|||
sudo cp -pr /rmm/web/dist /var/www/rmm/
|
||||
sudo chown www-data:www-data -R /var/www/rmm/dist
|
||||
|
||||
printf >&2 "${GREEN}Starting salt-master service${NC}\n"
|
||||
sudo systemctl start salt-master
|
||||
sleep 7
|
||||
printf >&2 "${GREEN}Starting salt-api service${NC}\n"
|
||||
sudo systemctl start salt-api
|
||||
|
||||
for i in rmm celery celerybeat celery-winupdate nginx nats natsapi
|
||||
for i in rmm celery celerybeat nginx nats natsapi
|
||||
do
|
||||
printf >&2 "${GREEN}Starting ${i} service${NC}\n"
|
||||
sudo systemctl start ${i}
|
||||
|
|
Loading…
Reference in New Issue