Compare commits
37 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4a1f5558b8 | ||
|
608db9889f | ||
|
012b697337 | ||
|
0580506cf3 | ||
|
ff4ab9b661 | ||
|
b7ce5fdd3e | ||
|
a11e617322 | ||
|
d0beac7e2b | ||
|
9db497092f | ||
|
8eb91c08aa | ||
|
ded5437522 | ||
|
9348657951 | ||
|
bca85933f7 | ||
|
c32bb35f1c | ||
|
4b84062d62 | ||
|
d6d0f8fa17 | ||
|
dd72c875d3 | ||
|
1a1df50300 | ||
|
53cbb527b4 | ||
|
8b87b2717e | ||
|
1007d6dac7 | ||
|
6799fac120 | ||
|
558e6288ca | ||
|
d9cb73291b | ||
|
d0f7be3ac3 | ||
|
331e16d3ca | ||
|
0db246c311 | ||
|
94dc62ff58 | ||
|
e68ecf6844 | ||
|
5167b0a8c6 | ||
|
77e3d3786d | ||
|
708d4d39bc | ||
|
2a8cda2a1e | ||
|
8d783840ad | ||
|
abe39d5790 | ||
|
d7868e9e5a | ||
|
7b84e36e15 |
40
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
40
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a bug report
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Server Info (please complete the following information):**
|
||||
- OS: [e.g. Ubuntu 20.04, Debian 10]
|
||||
- Browser: [e.g. chrome, safari]
|
||||
- RMM Version (as shown in top left of web UI):
|
||||
|
||||
**Installation Method:**
|
||||
- [ ] Standard
|
||||
- [ ] Docker
|
||||
|
||||
**Agent Info (please complete the following information):**
|
||||
- Agent version (as shown in the 'Summary' tab of the agent from web UI):
|
||||
- Agent OS: [e.g. Win 10 v2004, Server 2012 R2]
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
@@ -8,7 +8,7 @@
|
||||
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 and integrates with [MeshCentral](https://github.com/Ylianst/MeshCentral)
|
||||
|
||||
# [LIVE DEMO](https://rmm.xlawgaming.com/)
|
||||
# [LIVE DEMO](https://rmm.tacticalrmm.io/)
|
||||
Demo database resets every hour. Alot of features are disabled for obvious reasons due to the nature of this app.
|
||||
|
||||
*Tactical RMM is currently in alpha and subject to breaking changes. Use in production at your own risk.*
|
||||
|
@@ -110,14 +110,6 @@ class Agent(BaseAuditModel):
|
||||
def client(self):
|
||||
return self.site.client
|
||||
|
||||
@property
|
||||
def has_nats(self):
|
||||
return pyver.parse(self.version) >= pyver.parse("1.1.0")
|
||||
|
||||
@property
|
||||
def has_gotasks(self):
|
||||
return pyver.parse(self.version) >= pyver.parse("1.1.1")
|
||||
|
||||
@property
|
||||
def timezone(self):
|
||||
# return the default timezone unless the timezone is explicity set per agent
|
||||
|
@@ -198,11 +198,6 @@ class TestAgentViews(TacticalTestCase):
|
||||
|
||||
@patch("agents.models.Agent.nats_cmd")
|
||||
def test_get_processes(self, mock_ret):
|
||||
agent_old = baker.make_recipe("agents.online_agent", version="1.1.12")
|
||||
url_old = f"/agents/{agent_old.pk}/getprocs/"
|
||||
r = self.client.get(url_old)
|
||||
self.assertEqual(r.status_code, 400)
|
||||
|
||||
agent = baker.make_recipe("agents.online_agent", version="1.2.0")
|
||||
url = f"/agents/{agent.pk}/getprocs/"
|
||||
|
||||
@@ -340,6 +335,7 @@ class TestAgentViews(TacticalTestCase):
|
||||
"func": "schedtask",
|
||||
"schedtaskpayload": {
|
||||
"type": "schedreboot",
|
||||
"deleteafter": True,
|
||||
"trigger": "once",
|
||||
"name": r.data["task_name"], # type: ignore
|
||||
"year": 2025,
|
||||
|
@@ -69,10 +69,9 @@ def update_agents(request):
|
||||
def ping(request, pk):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
status = "offline"
|
||||
if agent.has_nats:
|
||||
r = asyncio.run(agent.nats_cmd({"func": "ping"}, timeout=5))
|
||||
if r == "pong":
|
||||
status = "online"
|
||||
r = asyncio.run(agent.nats_cmd({"func": "ping"}, timeout=5))
|
||||
if r == "pong":
|
||||
status = "online"
|
||||
|
||||
return Response({"name": agent.hostname, "status": status})
|
||||
|
||||
@@ -80,8 +79,7 @@ def ping(request, pk):
|
||||
@api_view(["DELETE"])
|
||||
def uninstall(request):
|
||||
agent = get_object_or_404(Agent, pk=request.data["pk"])
|
||||
if agent.has_nats:
|
||||
asyncio.run(agent.nats_cmd({"func": "uninstall"}, wait=False))
|
||||
asyncio.run(agent.nats_cmd({"func": "uninstall"}, wait=False))
|
||||
|
||||
name = agent.hostname
|
||||
agent.delete()
|
||||
@@ -147,9 +145,6 @@ def agent_detail(request, pk):
|
||||
@api_view()
|
||||
def get_processes(request, pk):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
if pyver.parse(agent.version) < pyver.parse("1.2.0"):
|
||||
return notify_error("Requires agent version 1.2.0 or greater")
|
||||
|
||||
r = asyncio.run(agent.nats_cmd(data={"func": "procs"}, timeout=5))
|
||||
if r == "timeout":
|
||||
return notify_error("Unable to contact the agent")
|
||||
@@ -159,9 +154,6 @@ def get_processes(request, pk):
|
||||
@api_view()
|
||||
def kill_proc(request, pk, pid):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
if not agent.has_nats:
|
||||
return notify_error("Requires agent version 1.1.0 or greater")
|
||||
|
||||
r = asyncio.run(
|
||||
agent.nats_cmd({"func": "killproc", "procpid": int(pid)}, timeout=15)
|
||||
)
|
||||
@@ -177,8 +169,6 @@ def kill_proc(request, pk, pid):
|
||||
@api_view()
|
||||
def get_event_log(request, pk, logtype, days):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
if not agent.has_nats:
|
||||
return notify_error("Requires agent version 1.1.0 or greater")
|
||||
timeout = 180 if logtype == "Security" else 30
|
||||
data = {
|
||||
"func": "eventlog",
|
||||
@@ -198,8 +188,6 @@ def get_event_log(request, pk, logtype, days):
|
||||
@api_view(["POST"])
|
||||
def send_raw_cmd(request):
|
||||
agent = get_object_or_404(Agent, pk=request.data["pk"])
|
||||
if not agent.has_nats:
|
||||
return notify_error("Requires agent version 1.1.0 or greater")
|
||||
timeout = int(request.data["timeout"])
|
||||
data = {
|
||||
"func": "rawcmd",
|
||||
@@ -296,9 +284,6 @@ class Reboot(APIView):
|
||||
# reboot now
|
||||
def post(self, request):
|
||||
agent = get_object_or_404(Agent, pk=request.data["pk"])
|
||||
if not agent.has_nats:
|
||||
return notify_error("Requires agent version 1.1.0 or greater")
|
||||
|
||||
r = asyncio.run(agent.nats_cmd({"func": "rebootnow"}, timeout=10))
|
||||
if r != "ok":
|
||||
return notify_error("Unable to contact the agent")
|
||||
@@ -308,8 +293,6 @@ class Reboot(APIView):
|
||||
# reboot later
|
||||
def patch(self, request):
|
||||
agent = get_object_or_404(Agent, pk=request.data["pk"])
|
||||
if not agent.has_gotasks:
|
||||
return notify_error("Requires agent version 1.1.1 or greater")
|
||||
|
||||
try:
|
||||
obj = dt.datetime.strptime(request.data["datetime"], "%Y-%m-%d %H:%M")
|
||||
@@ -324,6 +307,7 @@ class Reboot(APIView):
|
||||
"func": "schedtask",
|
||||
"schedtaskpayload": {
|
||||
"type": "schedreboot",
|
||||
"deleteafter": True,
|
||||
"trigger": "once",
|
||||
"name": task_name,
|
||||
"year": int(dt.datetime.strftime(obj, "%Y")),
|
||||
@@ -334,9 +318,6 @@ class Reboot(APIView):
|
||||
},
|
||||
}
|
||||
|
||||
if pyver.parse(agent.version) >= pyver.parse("1.1.2"):
|
||||
nats_data["schedtaskpayload"]["deleteafter"] = True
|
||||
|
||||
r = asyncio.run(agent.nats_cmd(nats_data, timeout=10))
|
||||
if r != "ok":
|
||||
return notify_error(r)
|
||||
@@ -561,9 +542,6 @@ def run_script(request):
|
||||
@api_view()
|
||||
def recover_mesh(request, pk):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
if not agent.has_nats:
|
||||
return notify_error("Requires agent version 1.1.0 or greater")
|
||||
|
||||
data = {"func": "recover", "payload": {"mode": "mesh"}}
|
||||
r = asyncio.run(agent.nats_cmd(data, timeout=45))
|
||||
if r != "ok":
|
||||
@@ -743,9 +721,6 @@ def agent_maintenance(request):
|
||||
class WMI(APIView):
|
||||
def get(self, request, pk):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
if pyver.parse(agent.version) < pyver.parse("1.1.2"):
|
||||
return notify_error("Requires agent version 1.1.2 or greater")
|
||||
|
||||
r = asyncio.run(agent.nats_cmd({"func": "sysinfo"}, timeout=20))
|
||||
if r != "ok":
|
||||
return notify_error("Unable to contact the agent")
|
||||
|
@@ -112,6 +112,23 @@ class TestAPIv3(TacticalTestCase):
|
||||
{"agent": self.agent.pk, "check_interval": 15},
|
||||
)
|
||||
|
||||
def test_run_checks(self):
|
||||
# force run all checks regardless of interval
|
||||
agent = baker.make_recipe("agents.online_agent")
|
||||
baker.make_recipe("checks.ping_check", agent=agent)
|
||||
baker.make_recipe("checks.diskspace_check", agent=agent)
|
||||
baker.make_recipe("checks.cpuload_check", agent=agent)
|
||||
baker.make_recipe("checks.memory_check", agent=agent)
|
||||
baker.make_recipe("checks.eventlog_check", agent=agent)
|
||||
for _ in range(10):
|
||||
baker.make_recipe("checks.script_check", agent=agent)
|
||||
|
||||
url = f"/api/v3/{agent.agent_id}/runchecks/"
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.json()["agent"], agent.pk)
|
||||
self.assertIsInstance(r.json()["check_interval"], int)
|
||||
self.assertEqual(len(r.json()["checks"]), 15)
|
||||
|
||||
def test_checkin_patch(self):
|
||||
from logs.models import PendingAction
|
||||
|
||||
|
@@ -29,7 +29,6 @@ class TestAutotaskViews(TacticalTestCase):
|
||||
agent = baker.make_recipe("agents.agent")
|
||||
policy = baker.make("automation.Policy")
|
||||
check = baker.make_recipe("checks.diskspace_check", agent=agent)
|
||||
old_agent = baker.make_recipe("agents.agent", version="1.1.0")
|
||||
|
||||
# test script set to invalid pk
|
||||
data = {"autotask": {"script": 500}}
|
||||
@@ -52,15 +51,6 @@ class TestAutotaskViews(TacticalTestCase):
|
||||
resp = self.client.post(url, data, format="json")
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
|
||||
# test old agent version
|
||||
data = {
|
||||
"autotask": {"script": script.id},
|
||||
"agent": old_agent.id,
|
||||
}
|
||||
|
||||
resp = self.client.post(url, data, format="json")
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
|
||||
# test add task to agent
|
||||
data = {
|
||||
"autotask": {
|
||||
@@ -203,13 +193,6 @@ class TestAutotaskViews(TacticalTestCase):
|
||||
nats_cmd.assert_called_with({"func": "runtask", "taskpk": task.id}, wait=False)
|
||||
nats_cmd.reset_mock()
|
||||
|
||||
old_agent = baker.make_recipe("agents.agent", version="1.0.2")
|
||||
task2 = baker.make("autotasks.AutomatedTask", agent=old_agent)
|
||||
url = f"/tasks/runwintask/{task2.id}/"
|
||||
resp = self.client.get(url, format="json")
|
||||
self.assertEqual(resp.status_code, 400)
|
||||
nats_cmd.assert_not_called()
|
||||
|
||||
self.check_not_authenticated("get", url)
|
||||
|
||||
|
||||
|
@@ -34,9 +34,6 @@ class AddAutoTask(APIView):
|
||||
parent = {"policy": policy}
|
||||
else:
|
||||
agent = get_object_or_404(Agent, pk=data["agent"])
|
||||
if not agent.has_gotasks:
|
||||
return notify_error("Requires agent version 1.1.1 or greater")
|
||||
|
||||
parent = {"agent": agent}
|
||||
|
||||
check = None
|
||||
@@ -128,8 +125,5 @@ class AutoTask(APIView):
|
||||
@api_view()
|
||||
def run_task(request, pk):
|
||||
task = get_object_or_404(AutomatedTask, pk=pk)
|
||||
if not task.agent.has_nats:
|
||||
return notify_error("Requires agent version 1.1.0 or greater")
|
||||
|
||||
asyncio.run(task.agent.nats_cmd({"func": "runtask", "taskpk": task.pk}, wait=False))
|
||||
return Response(f"{task.name} will now be run on {task.agent.hostname}")
|
||||
|
@@ -310,14 +310,8 @@ class TestCheckViews(TacticalTestCase):
|
||||
@patch("agents.models.Agent.nats_cmd")
|
||||
def test_run_checks(self, nats_cmd):
|
||||
agent = baker.make_recipe("agents.agent", version="1.4.1")
|
||||
agent_old = baker.make_recipe("agents.agent", version="1.0.2")
|
||||
agent_b4_141 = baker.make_recipe("agents.agent", version="1.4.0")
|
||||
|
||||
url = f"/checks/runchecks/{agent_old.pk}/"
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 400)
|
||||
self.assertEqual(r.json(), "Requires agent version 1.1.0 or greater")
|
||||
|
||||
url = f"/checks/runchecks/{agent_b4_141.pk}/"
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
@@ -161,8 +161,6 @@ class CheckHistory(APIView):
|
||||
@api_view()
|
||||
def run_checks(request, pk):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
if not agent.has_nats:
|
||||
return notify_error("Requires agent version 1.1.0 or greater")
|
||||
|
||||
if pyver.parse(agent.version) >= pyver.parse("1.4.1"):
|
||||
r = asyncio.run(agent.nats_cmd({"func": "runchecks"}, timeout=15))
|
||||
|
@@ -215,5 +215,33 @@
|
||||
"name": "Create User Logon Script",
|
||||
"description": "Creates a powershell script that runs at logon of any user on the machine in the security context of the user.",
|
||||
"shell": "powershell"
|
||||
},
|
||||
{
|
||||
"filename": "Chocolatey_Update_Installed.bat",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "Chocolatey Update Installed Apps",
|
||||
"description": "Update all apps that were installed using Chocolatey.",
|
||||
"shell": "cmd"
|
||||
},
|
||||
{
|
||||
"filename": "AD_Check_And_Enable_AD_Recycle_Bin.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "AD - Check and Enable AD Recycle Bin",
|
||||
"description": "Only run on Domain Controllers, checks for Active Directory Recycle Bin and enables if not already enabled",
|
||||
"shell": "powershell"
|
||||
},
|
||||
{
|
||||
"filename": "Check_Events_for_Bluescreens.ps1",
|
||||
"submittedBy": "https://github.com/dinger1986",
|
||||
"name": "Event Viewer - Check for Bluescreens",
|
||||
"description": "This will check for Bluescreen events on your system",
|
||||
"shell": "powershell"
|
||||
},
|
||||
{
|
||||
"filename": "Rename_Computer.ps1",
|
||||
"submittedBy": "https://github.com/silversword411",
|
||||
"name": "Rename Computer",
|
||||
"description": "Rename computer. First parameter will be new PC name. 2nd parameter if yes will auto-reboot machine",
|
||||
"shell": "powershell"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
@@ -7,8 +7,6 @@ from tacticalrmm.celery import app
|
||||
|
||||
@app.task
|
||||
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]
|
||||
nats_data = {
|
||||
"func": "rawcmd",
|
||||
"timeout": timeout,
|
||||
@@ -17,15 +15,13 @@ def handle_bulk_command_task(agentpks, cmd, shell, timeout) -> None:
|
||||
"shell": shell,
|
||||
},
|
||||
}
|
||||
for agent in agents_nats:
|
||||
for agent in Agent.objects.filter(pk__in=agentpks):
|
||||
asyncio.run(agent.nats_cmd(nats_data, wait=False))
|
||||
|
||||
|
||||
@app.task
|
||||
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]
|
||||
nats_data = {
|
||||
"func": "runscript",
|
||||
"timeout": timeout,
|
||||
@@ -35,5 +31,5 @@ def handle_bulk_script_task(scriptpk, agentpks, args, timeout) -> None:
|
||||
"shell": script.shell,
|
||||
},
|
||||
}
|
||||
for agent in agents_nats:
|
||||
for agent in Agent.objects.filter(pk__in=agentpks):
|
||||
asyncio.run(agent.nats_cmd(nats_data, wait=False))
|
||||
|
@@ -18,8 +18,6 @@ logger.configure(**settings.LOG_CONFIG)
|
||||
@api_view()
|
||||
def get_services(request, pk):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
if not agent.has_nats:
|
||||
return notify_error("Requires agent version 1.1.0 or greater")
|
||||
r = asyncio.run(agent.nats_cmd(data={"func": "winservices"}, timeout=10))
|
||||
|
||||
if r == "timeout":
|
||||
@@ -38,8 +36,6 @@ def default_services(request):
|
||||
@api_view(["POST"])
|
||||
def service_action(request):
|
||||
agent = get_object_or_404(Agent, pk=request.data["pk"])
|
||||
if not agent.has_nats:
|
||||
return notify_error("Requires agent version 1.1.0 or greater")
|
||||
action = request.data["sv_action"]
|
||||
data = {
|
||||
"func": "winsvcaction",
|
||||
@@ -80,8 +76,6 @@ def service_action(request):
|
||||
@api_view()
|
||||
def service_detail(request, pk, svcname):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
if not agent.has_nats:
|
||||
return notify_error("Requires agent version 1.1.0 or greater")
|
||||
data = {"func": "winsvcdetail", "payload": {"name": svcname}}
|
||||
r = asyncio.run(agent.nats_cmd(data, timeout=10))
|
||||
if r == "timeout":
|
||||
@@ -93,8 +87,6 @@ def service_detail(request, pk, svcname):
|
||||
@api_view(["POST"])
|
||||
def edit_service(request):
|
||||
agent = get_object_or_404(Agent, pk=request.data["pk"])
|
||||
if not agent.has_nats:
|
||||
return notify_error("Requires agent version 1.1.0 or greater")
|
||||
data = {
|
||||
"func": "editwinsvc",
|
||||
"payload": {
|
||||
|
@@ -63,8 +63,6 @@ def get_installed(request, pk):
|
||||
@api_view()
|
||||
def refresh_installed(request, pk):
|
||||
agent = get_object_or_404(Agent, pk=pk)
|
||||
if not agent.has_nats:
|
||||
return notify_error("Requires agent version 1.1.0 or greater")
|
||||
|
||||
r: Any = asyncio.run(agent.nats_cmd({"func": "softwarelist"}, timeout=15))
|
||||
if r == "timeout" or r == "natsdown":
|
||||
|
@@ -15,16 +15,16 @@ EXE_DIR = os.path.join(BASE_DIR, "tacticalrmm/private/exe")
|
||||
AUTH_USER_MODEL = "accounts.User"
|
||||
|
||||
# latest release
|
||||
TRMM_VERSION = "0.4.24"
|
||||
TRMM_VERSION = "0.4.28"
|
||||
|
||||
# bump this version everytime vue code is changed
|
||||
# to alert user they need to manually refresh their browser
|
||||
APP_VER = "0.0.121"
|
||||
APP_VER = "0.0.122"
|
||||
|
||||
# https://github.com/wh1te909/rmmagent
|
||||
LATEST_AGENT_VER = "1.4.11"
|
||||
LATEST_AGENT_VER = "1.4.13"
|
||||
|
||||
MESH_VER = "0.7.84"
|
||||
MESH_VER = "0.7.93"
|
||||
|
||||
# for the update script, bump when need to recreate venv or npm install
|
||||
PIP_VER = "11"
|
||||
|
@@ -3,6 +3,7 @@ FROM node:12-alpine AS builder
|
||||
WORKDIR /home/node/app
|
||||
|
||||
COPY ./web/package.json .
|
||||
RUN npm install -g npm
|
||||
RUN npm install
|
||||
|
||||
COPY ./web .
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM nats:2.1-alpine
|
||||
FROM nats:2.2-alpine
|
||||
|
||||
ENV TACTICAL_DIR /opt/tactical
|
||||
ENV TACTICAL_READY_FILE ${TACTICAL_DIR}/tmp/tactical.ready
|
||||
|
@@ -15,7 +15,7 @@ SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
|
||||
COPY api/tacticalrmm/requirements.txt ${TACTICAL_TMP_DIR}/api/requirements.txt
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends gcc libc6-dev && \
|
||||
apt-get install -y --no-install-recommends gcc libc6-dev rsync && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
pip install --upgrade pip && \
|
||||
pip install --no-cache-dir setuptools wheel gunicorn && \
|
||||
|
@@ -35,7 +35,11 @@ if [ "$1" = 'tactical-init' ]; then
|
||||
test -f "${TACTICAL_READY_FILE}" && rm "${TACTICAL_READY_FILE}"
|
||||
|
||||
# copy container data to volume
|
||||
cp -af ${TACTICAL_TMP_DIR}/. ${TACTICAL_DIR}/
|
||||
# bad
|
||||
#cp -af ${TACTICAL_TMP_DIR}/. ${TACTICAL_DIR}/
|
||||
|
||||
# good
|
||||
rsync -a --no-perms --no-owner --delete "${TACTICAL_TMP_DIR}/" "${TACTICAL_DIR}/"
|
||||
|
||||
until (echo > /dev/tcp/"${POSTGRES_HOST}"/"${POSTGRES_PORT}") &> /dev/null; do
|
||||
echo "waiting for postgresql container to be ready..."
|
||||
|
@@ -8,7 +8,7 @@
|
||||
Tactical RMM is a remote monitoring & management tool for Windows computers, built with Django, Vue and Golang.
|
||||
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/)
|
||||
## [LIVE DEMO](https://rmm.tacticalrmm.io/)
|
||||
|
||||
*Tactical RMM is currently in alpha and subject to breaking changes. Use in production at your own risk.*
|
||||
|
||||
|
@@ -20,7 +20,7 @@ SSH into your server as the linux user you created during install.<br/><br/>
|
||||
__Never__ run any update scripts or commands as the `root` user.<br/>This will mess up permissions and break your installation.<br/><br/>
|
||||
Download the update script and run it:<br/>
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/update.sh
|
||||
wget -N https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/update.sh
|
||||
chmod +x update.sh
|
||||
./update.sh
|
||||
```
|
||||
|
17
install.sh
17
install.sh
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_VERSION="42"
|
||||
SCRIPT_VERSION="43"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/install.sh'
|
||||
|
||||
sudo apt install -y curl wget dirmngr gnupg lsb-release
|
||||
@@ -185,9 +185,9 @@ print_green 'Installing golang'
|
||||
|
||||
sudo mkdir -p /usr/local/rmmgo
|
||||
go_tmp=$(mktemp -d -t rmmgo-XXXXXXXXXX)
|
||||
wget https://golang.org/dl/go1.16.linux-amd64.tar.gz -P ${go_tmp}
|
||||
wget https://golang.org/dl/go1.16.2.linux-amd64.tar.gz -P ${go_tmp}
|
||||
|
||||
tar -xzf ${go_tmp}/go1.16.linux-amd64.tar.gz -C ${go_tmp}
|
||||
tar -xzf ${go_tmp}/go1.16.2.linux-amd64.tar.gz -C ${go_tmp}
|
||||
|
||||
sudo mv ${go_tmp}/go /usr/local/rmmgo/
|
||||
rm -rf ${go_tmp}
|
||||
@@ -195,11 +195,11 @@ rm -rf ${go_tmp}
|
||||
print_green 'Downloading NATS'
|
||||
|
||||
nats_tmp=$(mktemp -d -t nats-XXXXXXXXXX)
|
||||
wget https://github.com/nats-io/nats-server/releases/download/v2.1.9/nats-server-v2.1.9-linux-amd64.tar.gz -P ${nats_tmp}
|
||||
wget https://github.com/nats-io/nats-server/releases/download/v2.2.0/nats-server-v2.2.0-linux-amd64.tar.gz -P ${nats_tmp}
|
||||
|
||||
tar -xzf ${nats_tmp}/nats-server-v2.1.9-linux-amd64.tar.gz -C ${nats_tmp}
|
||||
tar -xzf ${nats_tmp}/nats-server-v2.2.0-linux-amd64.tar.gz -C ${nats_tmp}
|
||||
|
||||
sudo mv ${nats_tmp}/nats-server-v2.1.9-linux-amd64/nats-server /usr/local/bin/
|
||||
sudo mv ${nats_tmp}/nats-server-v2.2.0-linux-amd64/nats-server /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/nats-server
|
||||
sudo chown ${USER}:${USER} /usr/local/bin/nats-server
|
||||
rm -rf ${nats_tmp}
|
||||
@@ -216,6 +216,7 @@ curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
|
||||
sudo apt update
|
||||
sudo apt install -y gcc g++ make
|
||||
sudo apt install -y nodejs
|
||||
sudo npm install -g npm
|
||||
|
||||
print_green 'Installing MongoDB'
|
||||
|
||||
@@ -251,6 +252,10 @@ echo "$postgresql_repo" | sudo tee /etc/apt/sources.list.d/pgdg.list
|
||||
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
||||
sudo apt update
|
||||
sudo apt install -y postgresql-13
|
||||
sleep 2
|
||||
sudo systemctl enable postgresql
|
||||
sudo systemctl restart postgresql
|
||||
sleep 5
|
||||
|
||||
print_green 'Creating database for the rmm'
|
||||
|
||||
|
18
restore.sh
18
restore.sh
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_VERSION="20"
|
||||
SCRIPT_VERSION="21"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/restore.sh'
|
||||
|
||||
sudo apt update
|
||||
@@ -108,9 +108,9 @@ print_green 'Installing golang'
|
||||
sudo apt update
|
||||
sudo mkdir -p /usr/local/rmmgo
|
||||
go_tmp=$(mktemp -d -t rmmgo-XXXXXXXXXX)
|
||||
wget https://golang.org/dl/go1.16.linux-amd64.tar.gz -P ${go_tmp}
|
||||
wget https://golang.org/dl/go1.16.2.linux-amd64.tar.gz -P ${go_tmp}
|
||||
|
||||
tar -xzf ${go_tmp}/go1.16.linux-amd64.tar.gz -C ${go_tmp}
|
||||
tar -xzf ${go_tmp}/go1.16.2.linux-amd64.tar.gz -C ${go_tmp}
|
||||
|
||||
sudo mv ${go_tmp}/go /usr/local/rmmgo/
|
||||
rm -rf ${go_tmp}
|
||||
@@ -118,11 +118,11 @@ rm -rf ${go_tmp}
|
||||
print_green 'Downloading NATS'
|
||||
|
||||
nats_tmp=$(mktemp -d -t nats-XXXXXXXXXX)
|
||||
wget https://github.com/nats-io/nats-server/releases/download/v2.1.9/nats-server-v2.1.9-linux-amd64.tar.gz -P ${nats_tmp}
|
||||
wget https://github.com/nats-io/nats-server/releases/download/v2.2.0/nats-server-v2.2.0-linux-amd64.tar.gz -P ${nats_tmp}
|
||||
|
||||
tar -xzf ${nats_tmp}/nats-server-v2.1.9-linux-amd64.tar.gz -C ${nats_tmp}
|
||||
tar -xzf ${nats_tmp}/nats-server-v2.2.0-linux-amd64.tar.gz -C ${nats_tmp}
|
||||
|
||||
sudo mv ${nats_tmp}/nats-server-v2.1.9-linux-amd64/nats-server /usr/local/bin/
|
||||
sudo mv ${nats_tmp}/nats-server-v2.2.0-linux-amd64/nats-server /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/nats-server
|
||||
sudo chown ${USER}:${USER} /usr/local/bin/nats-server
|
||||
rm -rf ${nats_tmp}
|
||||
@@ -133,6 +133,7 @@ curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
|
||||
sudo apt update
|
||||
sudo apt install -y gcc g++ make
|
||||
sudo apt install -y nodejs
|
||||
sudo npm install -g npm
|
||||
|
||||
print_green 'Restoring Nginx'
|
||||
|
||||
@@ -205,9 +206,8 @@ wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-
|
||||
sudo apt update
|
||||
sudo apt install -y postgresql-13
|
||||
sleep 2
|
||||
|
||||
|
||||
|
||||
sudo systemctl enable postgresql
|
||||
sudo systemctl restart postgresql
|
||||
|
||||
print_green 'Restoring MongoDB'
|
||||
|
||||
|
17
scripts/AD_Check_And_Enable_AD_Recycle_Bin.ps1
Normal file
17
scripts/AD_Check_And_Enable_AD_Recycle_Bin.ps1
Normal file
@@ -0,0 +1,17 @@
|
||||
#Please only run on a domain controller
|
||||
#This script will first check if there are any AD Recycle Bin scopes set up - if there are no scopes it is assumed recycle bin feature is not enabled for the domain
|
||||
#The script then pulls the domain that the machine running the script is on - queries the domain for the Infrastructure Master and then will attempt to enable the feature
|
||||
|
||||
$adRecycleBinScope = Get-ADOptionalFeature -Identity 'Recycle Bin Feature' | Select -ExpandProperty EnabledScopes
|
||||
$ADDomain = Get-ADDomain | Select -ExpandProperty Forest
|
||||
$ADInfraMaster = Get-ADDomain | Select-Object InfrastructureMaster
|
||||
|
||||
if ($adRecycleBinScope -eq $null){
|
||||
Write-Host "Recycle Bin Disabled"
|
||||
Write-Host "Attempting to enable AD Recycle Bin"
|
||||
Enable-ADOptionalFeature -Identity 'Recycle Bin Feature' -Scope ForestOrConfigurationSet -Target $ADDomain -Server $ADInfraMaster.InfrastructureMaster -Confirm:$false
|
||||
Write-Host "AD Recycle Bin enabled for domain $($ADDomain)"
|
||||
}
|
||||
else{
|
||||
Write-Host "Recycle Bin already Enabled For: $($ADDomain)`n Scope: $($adRecycleBinScope)"
|
||||
}
|
20
scripts/Check_Events_for_Bluescreens.ps1
Normal file
20
scripts/Check_Events_for_Bluescreens.ps1
Normal file
@@ -0,0 +1,20 @@
|
||||
# This will check for Bluescreen events on your system
|
||||
|
||||
$ErrorActionPreference= 'silentlycontinue'
|
||||
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
|
||||
|
||||
if (Get-WinEvent -FilterHashtable @{LogName='application';ID='1001';ProviderName='Windows Error Reporting';Level=4;Data='BlueScreen';StartTime=$TimeSpan})
|
||||
|
||||
{
|
||||
Write-Output "There has been bluescreen events detected on your system"
|
||||
Get-WinEvent -FilterHashtable @{LogName='application';ID='1001';ProviderName='Windows Error Reporting';Level=4;Data='BlueScreen';StartTime=$TimeSpan}
|
||||
exit 1
|
||||
}
|
||||
|
||||
{
|
||||
else
|
||||
Write-Output "No bluescreen events detected"
|
||||
exit 0
|
||||
}
|
||||
|
||||
Exit $LASTEXITCODE
|
1
scripts/Chocolatey_Update_Installed.bat
Normal file
1
scripts/Chocolatey_Update_Installed.bat
Normal file
@@ -0,0 +1 @@
|
||||
cup -y all
|
19
scripts/Rename_Computer.ps1
Normal file
19
scripts/Rename_Computer.ps1
Normal file
@@ -0,0 +1,19 @@
|
||||
# Chanage the computer name in Windows
|
||||
# v1.0
|
||||
# First Command Parameter will be new computer name
|
||||
# Second Command Parameter if yes will auto-restart computer
|
||||
|
||||
if ($Args.Count -eq 0) {
|
||||
Write-Output "Computer name arg is required"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$param1=$args[0]
|
||||
$ToRestartTypeYes=$args[1]
|
||||
|
||||
Rename-Computer -newname "$param1"
|
||||
|
||||
# Restart the computer for rename to take effect
|
||||
if ($ToRestartTypeYes -eq 'yes') {
|
||||
Restart-Computer -Force
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
#Windows Defender Exclusions for Tactical
|
||||
Add-MpPreference -ExclusionPath 'C:\Program Files\TacticalAgent\*'
|
||||
Add-MpPreference -ExclusionPath 'C:\Windows\Temp\winagent-v*.exe'
|
||||
Add-MpPreference -ExclusionPath 'C:\Program Files\Mesh Agent\*'
|
||||
Add-MpPreference -ExclusionPath 'C:\salt\*'
|
||||
Add-MpPreference -ExclusionPath "C:\Program Files\Mesh Agent\*"
|
||||
Add-MpPreference -ExclusionPath "C:\Program Files\TacticalAgent\*"
|
||||
Add-MpPreference -ExclusionPath "C:\Windows\Temp\trmm\*"
|
||||
Add-MpPreference -ExclusionPath "C:\Windows\Temp\winagent-v*.exe"
|
||||
|
21
update.sh
21
update.sh
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_VERSION="113"
|
||||
SCRIPT_VERSION="114"
|
||||
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'
|
||||
@@ -172,6 +172,10 @@ printf >&2 "${GREEN}Stopping ${i} service...${NC}\n"
|
||||
sudo systemctl stop ${i}
|
||||
done
|
||||
|
||||
printf >&2 "${GREEN}Restarting postgresql database${NC}\n"
|
||||
sudo systemctl restart postgresql
|
||||
sleep 5
|
||||
|
||||
rm -f /rmm/api/tacticalrmm/app.ini
|
||||
|
||||
numprocs=$(nproc)
|
||||
@@ -242,6 +246,21 @@ if ! [[ $HAS_PY39 ]]; then
|
||||
sudo rm -rf Python-3.9.2 Python-3.9.2.tgz
|
||||
fi
|
||||
|
||||
HAS_NATS220=$(/usr/local/bin/nats-server -version | grep v2.2.0)
|
||||
if ! [[ $HAS_NATS220 ]]; then
|
||||
printf >&2 "${GREEN}Updating nats to v2.2.0${NC}\n"
|
||||
nats_tmp=$(mktemp -d -t nats-XXXXXXXXXX)
|
||||
wget https://github.com/nats-io/nats-server/releases/download/v2.2.0/nats-server-v2.2.0-linux-amd64.tar.gz -P ${nats_tmp}
|
||||
tar -xzf ${nats_tmp}/nats-server-v2.2.0-linux-amd64.tar.gz -C ${nats_tmp}
|
||||
sudo rm -f /usr/local/bin/nats-server
|
||||
sudo mv ${nats_tmp}/nats-server-v2.2.0-linux-amd64/nats-server /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/nats-server
|
||||
sudo chown ${USER}:${USER} /usr/local/bin/nats-server
|
||||
rm -rf ${nats_tmp}
|
||||
fi
|
||||
|
||||
sudo npm install -g npm
|
||||
|
||||
cd /rmm
|
||||
git config user.email "admin@example.com"
|
||||
git config user.name "Bob"
|
||||
|
@@ -66,7 +66,7 @@
|
||||
<q-tooltip>Missing</q-tooltip>
|
||||
</q-icon>
|
||||
</q-td>
|
||||
<q-td>{{ props.row.severity }}</q-td>
|
||||
<q-td>{{ formatSeverity(props.row.severity) }}</q-td>
|
||||
<q-td>{{ formatMessage(props.row.title) }}</q-td>
|
||||
<q-td
|
||||
@click.native="
|
||||
@@ -155,6 +155,9 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
formatSeverity(severity) {
|
||||
return !severity ? "Other" : severity;
|
||||
},
|
||||
editPolicy(pk, policy) {
|
||||
const data = { pk: pk, policy: policy };
|
||||
axios.patch(`/winupdate/editpolicy/`, data).then(r => {
|
||||
|
@@ -254,7 +254,7 @@ export default {
|
||||
}
|
||||
|
||||
.prism-editor__container {
|
||||
height: 60000em;
|
||||
height: 30000em;
|
||||
}
|
||||
|
||||
/* optional class for removing the outline */
|
||||
@@ -264,7 +264,7 @@ export default {
|
||||
|
||||
.prism-editor__textarea,
|
||||
.prism-editor__container {
|
||||
width: 10000em !important;
|
||||
width: 1000em !important;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
Reference in New Issue
Block a user