Пример #1
0
def recover(request):
    agent = get_object_or_404(Agent, pk=request.data["pk"])
    mode = request.data["mode"]

    # attempt a realtime recovery, otherwise fall back to old recovery method
    if mode == "tacagent" or mode == "mesh":
        data = {"func": "recover", "payload": {"mode": mode}}
        r = asyncio.run(agent.nats_cmd(data, timeout=10))
        if r == "ok":
            return Response("Successfully completed recovery")

    if agent.recoveryactions.filter(last_run=None).exists():  # type: ignore
        return notify_error(
            "A recovery action is currently pending. Please wait for the next agent check-in."
        )

    if mode == "command" and not request.data["cmd"]:
        return notify_error("Command is required")

    # if we've made it this far and realtime recovery didn't work,
    # tacagent service is the fallback recovery so we obv can't use that to recover itself if it's down
    if mode == "tacagent":
        return notify_error(
            "Requires RPC service to be functional. Please recover that first")

    # we should only get here if all other methods fail
    RecoveryAction(
        agent=agent,
        mode=mode,
        command=request.data["cmd"] if mode == "command" else None,
    ).save()

    return Response("Recovery will be attempted on the agent's next check-in")
Пример #2
0
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",
        "timeout": timeout,
        "payload": {
            "command": request.data["cmd"],
            "shell": request.data["shell"],
        },
    }
    r = asyncio.run(agent.nats_cmd(data, timeout=timeout + 2))

    if r == "timeout":
        return notify_error("Unable to contact the agent")

    AuditLog.audit_raw_command(
        username=request.user.username,
        hostname=agent.hostname,
        cmd=request.data["cmd"],
        shell=request.data["shell"],
    )

    return Response(r)
Пример #3
0
def install(request):
    agent = get_object_or_404(Agent, pk=request.data["pk"])
    if pyver.parse(agent.version) < pyver.parse("1.4.8"):
        return notify_error("Requires agent v1.4.8")

    name = request.data["name"]

    action = PendingAction.objects.create(
        agent=agent,
        action_type="chocoinstall",
        details={
            "name": name,
            "output": None,
            "installed": False
        },
    )

    nats_data = {
        "func": "installwithchoco",
        "choco_prog_name": name,
        "pending_action_pk": action.pk,
    }

    r = asyncio.run(agent.nats_cmd(nats_data, timeout=2))
    if r != "ok":
        action.delete()
        return notify_error("Unable to contact the agent")

    return Response(
        f"{name} will be installed shortly on {agent.hostname}. Check the Pending Actions menu to see the status/output"
    )
Пример #4
0
def edit_service(request):
    data = request.data
    pk = data["pk"]
    service_name = data["sv_name"]
    edit_action = data["edit_action"]

    agent = get_object_or_404(Agent, pk=pk)

    if edit_action == "autodelay":
        kwargs = {"start_type": "auto", "start_delayed": True}
    elif edit_action == "auto":
        kwargs = {"start_type": "auto", "start_delayed": False}
    else:
        kwargs = {"start_type": edit_action}

    r = agent.salt_api_cmd(
        timeout=20, func="service.modify", arg=service_name, kwargs=kwargs,
    )

    if r == "timeout":
        return notify_error("Unable to contact the agent")
    elif r == "error" or not r:
        return notify_error("Something went wrong")

    return Response("ok")
Пример #5
0
def send_raw_cmd(request):
    agent = get_object_or_404(Agent, pk=request.data["pk"])

    r = agent.salt_api_cmd(
        timeout=request.data["timeout"],
        func="cmd.run",
        kwargs={
            "cmd": request.data["cmd"],
            "shell": request.data["shell"],
            "timeout": request.data["timeout"],
        },
    )

    if r == "timeout":
        return notify_error("Unable to contact the agent")
    elif r == "error" or not r:
        return notify_error("Something went wrong")

    AuditLog.audit_raw_command(
        username=request.user.username,
        hostname=agent.hostname,
        cmd=request.data["cmd"],
        shell=request.data["shell"],
    )

    logger.info(f"The command {request.data['cmd']} was sent on agent {agent.hostname}")
    return Response(r)
Пример #6
0
def refresh_installed(request, pk):
    agent = get_object_or_404(Agent, pk=pk)
    r = agent.salt_api_cmd(
        timeout=20,
        func="pkg.list_pkgs",
        kwargs={
            "include_components": False,
            "include_updates": False
        },
    )

    if r == "timeout":
        return notify_error("Unable to contact the agent")
    elif r == "error":
        return notify_error("Something went wrong")

    printable = set(string.printable)

    try:
        software = [{
            "name": "".join(filter(lambda x: x in printable, k)),
            "version": "".join(filter(lambda x: x in printable, v)),
        } for k, v in r.items()]
    except Exception:
        return notify_error("Something went wrong")

    if not InstalledSoftware.objects.filter(agent=agent).exists():
        InstalledSoftware(agent=agent, software=software).save()
    else:
        s = agent.installedsoftware_set.get()
        s.software = software
        s.save(update_fields=["software"])

    return Response("ok")
Пример #7
0
    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")
Пример #8
0
def run_script(request):
    agent = get_object_or_404(Agent, pk=request.data["pk"])
    script = get_object_or_404(Script, pk=request.data["scriptPK"])

    output = request.data["output"]
    args = request.data["args"]

    req_timeout = int(request.data["timeout"]) + 3

    AuditLog.audit_script_run(
        username=request.user.username,
        hostname=agent.hostname,
        script=script.name,
    )

    if output == "wait":
        r = agent.salt_api_cmd(
            timeout=req_timeout,
            func="win_agent.run_script",
            kwargs={
                "filepath": script.filepath,
                "filename": script.filename,
                "shell": script.shell,
                "timeout": request.data["timeout"],
                "args": args,
            },
        )

        if isinstance(r, dict):
            if r["stdout"]:
                return Response(r["stdout"])
            elif r["stderr"]:
                return Response(r["stderr"])
            else:
                try:
                    r["retcode"]
                except KeyError:
                    return notify_error("Something went wrong")

                return Response(f"Return code: {r['retcode']}")

        else:
            if r == "timeout":
                return notify_error("Unable to contact the agent")
            elif r == "error":
                return notify_error("Something went wrong")
            else:
                return notify_error(str(r))

    else:
        data = {
            "agentpk": agent.pk,
            "scriptpk": script.pk,
            "timeout": request.data["timeout"],
            "args": args,
        }
        run_script_bg_task.delay(data)
        return Response(f"{script.name} will now be run on {agent.hostname}")
Пример #9
0
    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")
        return Response("ok")
Пример #10
0
def restart_mesh(request, pk):
    agent = get_object_or_404(Agent, pk=pk)
    r = agent.salt_api_cmd(func="service.restart", arg="mesh agent", timeout=30)
    if r == "timeout" or r == "error":
        return notify_error("Unable to contact the agent")
    elif isinstance(r, bool) and r:
        return Response(f"Restarted Mesh Agent on {agent.hostname}")
    else:
        return notify_error(f"Failed to restart the Mesh Agent on {agent.hostname}")
Пример #11
0
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")
    return Response(r)
Пример #12
0
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":
        return notify_error("Unable to contact the agent")

    return Response(r)
Пример #13
0
def get_processes(request, pk):
    agent = get_object_or_404(Agent, pk=pk)
    r = agent.salt_api_cmd(timeout=20, func="win_agent.get_procs")

    if r == "timeout":
        return notify_error("Unable to contact the agent")
    elif r == "error":
        return notify_error("Something went wrong")

    return Response(r)
Пример #14
0
    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")

        return Response("ok")
Пример #15
0
def service_detail(request, pk, svcname):
    agent = get_object_or_404(Agent, pk=pk)
    r = agent.salt_api_cmd(timeout=20, func="service.info", arg=svcname)

    if r == "timeout":
        return notify_error("Unable to contact the agent")
    elif r == "error" or not r:
        return notify_error("Something went wrong")

    return Response(r)
Пример #16
0
    def post(self, request):
        if "version" not in request.data:
            return notify_error("Invalid data")

        ver = request.data["version"]
        if pyver.parse(ver) < pyver.parse(settings.LATEST_AGENT_VER):
            return notify_error(
                f"Old installer detected (version {ver} ). Latest version is {settings.LATEST_AGENT_VER} Please generate a new installer from the RMM"
            )

        return Response("ok")
Пример #17
0
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":
        return notify_error("Unable to contact the agent")

    return Response(f"Repaired mesh agent on {agent.hostname}")
Пример #18
0
def server_maintenance(request):
    from tacticalrmm.utils import reload_nats

    if "action" not in request.data:
        return notify_error("The data is incorrect")

    if request.data["action"] == "reload_nats":
        reload_nats()
        return Response("Nats configuration was reloaded successfully.")

    if request.data["action"] == "rm_orphaned_tasks":
        from agents.models import Agent
        from autotasks.tasks import remove_orphaned_win_tasks

        agents = Agent.objects.only("pk", "last_seen", "overdue_time",
                                    "offline_time")
        online = [i for i in agents if i.status == "online"]
        for agent in online:
            remove_orphaned_win_tasks.delay(agent.pk)

        return Response(
            "The task has been initiated. Check the Debug Log in the UI for progress."
        )

    if request.data["action"] == "prune_db":
        from logs.models import AuditLog, PendingAction

        if "prune_tables" not in request.data:
            return notify_error("The data is incorrect.")

        tables = request.data["prune_tables"]
        records_count = 0
        if "audit_logs" in tables:
            auditlogs = AuditLog.objects.filter(action="check_run")
            records_count += auditlogs.count()
            auditlogs.delete()

        if "pending_actions" in tables:
            pendingactions = PendingAction.objects.filter(status="completed")
            records_count += pendingactions.count()
            pendingactions.delete()

        if "alerts" in tables:
            from alerts.models import Alert

            alerts = Alert.objects.all()
            records_count += alerts.count()
            alerts.delete()

        return Response(
            f"{records_count} records were pruned from the database")

    return notify_error("The data is incorrect")
Пример #19
0
def get_refreshed_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":
        return notify_error("Unable to contact the agent")

    agent.services = r
    agent.save(update_fields=["services"])
    return Response(ServicesSerializer(agent).data)
Пример #20
0
def get_refreshed_services(request, pk):
    agent = get_object_or_404(Agent, pk=pk)
    r = agent.salt_api_cmd(timeout=15, func="win_agent.get_services")

    if r == "timeout":
        return notify_error("Unable to contact the agent")
    elif r == "error" or not r:
        return notify_error("Something went wrong")

    agent.services = r
    agent.save(update_fields=["services"])
    return Response(ServicesSerializer(agent).data)
Пример #21
0
def kill_proc(request, pk, pid):
    agent = get_object_or_404(Agent, pk=pk)
    r = asyncio.run(
        agent.nats_cmd({"func": "killproc", "procpid": int(pid)}, timeout=15)
    )

    if r == "timeout":
        return notify_error("Unable to contact the agent")
    elif r != "ok":
        return notify_error(r)

    return Response("ok")
Пример #22
0
def kill_proc(request, pk, pid):
    agent = get_object_or_404(Agent, pk=pk)
    r = agent.salt_api_cmd(timeout=25, func="ps.kill_pid", arg=int(pid))

    if r == "timeout":
        return notify_error("Unable to contact the agent")
    elif r == "error":
        return notify_error("Something went wrong")

    if isinstance(r, bool) and not r:
        return notify_error("Unable to kill the process")

    return Response("ok")
Пример #23
0
    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)}")
Пример #24
0
    def delete(self, request, pk):
        site = get_object_or_404(Site, pk=pk)
        if site.client.sites.count() == 1:
            return notify_error(f"A client must have at least 1 site.")

        agent_count = Agent.objects.filter(site=site).count()

        if agent_count > 0:
            return notify_error(
                f"Cannot delete {site.name} while {agent_count} agents exist in it. Move the agents to another site first."
            )

        site.delete()
        return Response(f"{site.name} was deleted!")
Пример #25
0
def run_checks(request, pk):
    agent = get_object_or_404(Agent, pk=pk)

    if pyver.parse(agent.version) >= pyver.parse("1.4.1"):
        r = asyncio.run(agent.nats_cmd({"func": "runchecks"}, timeout=15))
        if r == "busy":
            return notify_error(f"Checks are already running on {agent.hostname}")
        elif r == "ok":
            return Response(f"Checks will now be re-run on {agent.hostname}")
        else:
            return notify_error("Unable to contact the agent")
    else:
        asyncio.run(agent.nats_cmd({"func": "runchecks"}, wait=False))
        return Response(f"Checks will now be re-run on {agent.hostname}")
Пример #26
0
    def patch(self, request, pk):
        check = get_object_or_404(Check, pk=pk)

        # remove fields that should not be changed when editing a check from the frontend
        if "check_alert" not in request.data.keys():
            [request.data.pop(i) for i in check.non_editable_fields]

        # set event id to 0 if wildcard because it needs to be an integer field for db
        # will be ignored anyway by the agent when doing wildcard check
        if check.check_type == "eventlog":
            try:
                request.data["event_id_is_wildcard"]
            except KeyError:
                pass
            else:
                if request.data["event_id_is_wildcard"]:
                    if check.agent.not_supported(version_added="0.10.2"):
                        return notify_error({
                            "non_field_errors":
                            "Wildcard is only available in agent 0.10.2 or greater"
                        })

                    request.data["event_id"] = 0

        elif check.check_type == "script":
            added = "0.11.0"
            try:
                request.data["script_args"]
            except KeyError:
                pass
            else:
                if request.data["script_args"] and check.agent.not_supported(
                        version_added=added):
                    return notify_error({
                        "non_field_errors":
                        f"Script arguments only available in agent {added} or greater"
                    })

        serializer = CheckSerializer(instance=check,
                                     data=request.data,
                                     partial=True)
        serializer.is_valid(raise_exception=True)
        obj = serializer.save()

        # Update policy check fields
        if check.policy:
            update_policy_check_fields_task(checkpk=pk)

        return Response(f"{obj.readable_desc} was edited!")
Пример #27
0
def delete_site(request):
    client = get_object_or_404(Client, client=request.data["client"])
    if client.sites.count() == 1:
        return notify_error(f"A client must have at least 1 site.")

    site = Site.objects.filter(client=client).filter(site=request.data["site"]).get()
    agents = Agent.objects.filter(client=client.client).filter(site=site.site)

    if agents.exists():
        return notify_error(
            f"Cannot delete {site} while {agents.count()} agents exist in it. Move the agents to another site first."
        )

    site.delete()
    return Response(f"{site} was deleted!")
Пример #28
0
def bulk(request):
    if request.data["target"] == "agents" and not request.data["agentPKs"]:
        return notify_error("Must select at least 1 agent")

    if request.data["target"] == "client":
        q = Agent.objects.filter(site__client_id=request.data["client"])
    elif request.data["target"] == "site":
        q = Agent.objects.filter(site_id=request.data["site"])
    elif request.data["target"] == "agents":
        q = Agent.objects.filter(pk__in=request.data["agentPKs"])
    elif request.data["target"] == "all":
        q = Agent.objects.only("pk", "monitoring_type")
    else:
        return notify_error("Something went wrong")

    if request.data["monType"] == "servers":
        q = q.filter(monitoring_type="server")
    elif request.data["monType"] == "workstations":
        q = q.filter(monitoring_type="workstation")

    agents: list[int] = [agent.pk for agent in q]

    AuditLog.audit_bulk_action(request.user, request.data["mode"],
                               request.data)

    if request.data["mode"] == "command":
        handle_bulk_command_task.delay(agents, request.data["cmd"],
                                       request.data["shell"],
                                       request.data["timeout"])
        return Response(f"Command will now be run on {len(agents)} agents")

    elif request.data["mode"] == "script":
        script = get_object_or_404(Script, pk=request.data["scriptPK"])
        handle_bulk_script_task.delay(script.pk, agents, request.data["args"],
                                      request.data["timeout"])
        return Response(
            f"{script.name} will now be run on {len(agents)} agents")

    elif request.data["mode"] == "install":
        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(agents)
        return Response(
            f"Patch status scan will now run on {len(agents)} agents")

    return notify_error("Something went wrong")
Пример #29
0
def service_action(request):
    data = request.data
    pk = data["pk"]
    service_name = data["sv_name"]
    service_action = data["sv_action"]
    agent = get_object_or_404(Agent, pk=pk)
    r = agent.salt_api_cmd(
        timeout=45, func=f"service.{service_action}", arg=service_name,
    )

    if r == "timeout":
        return notify_error("Unable to contact the agent")
    elif r == "error" or not r:
        return notify_error("Something went wrong")

    return Response("ok")
Пример #30
0
def meshcentral(request, pk):
    agent = get_object_or_404(Agent, pk=pk)
    core = CoreSettings.objects.first()

    token = agent.get_login_token(key=core.mesh_token,
                                  user=f"user//{core.mesh_username}")

    if token == "err":
        return notify_error("Invalid mesh token")

    control = f"{core.mesh_site}/?login={token}&gotonode={agent.mesh_node_id}&viewmode=11&hide=31"
    terminal = f"{core.mesh_site}/?login={token}&gotonode={agent.mesh_node_id}&viewmode=12&hide=31"
    file = f"{core.mesh_site}/?login={token}&gotonode={agent.mesh_node_id}&viewmode=13&hide=31"

    AuditLog.audit_mesh_session(username=request.user.username,
                                hostname=agent.hostname)

    ret = {
        "hostname": agent.hostname,
        "control": control,
        "terminal": terminal,
        "file": file,
        "status": agent.status,
        "client": agent.client.name,
        "site": agent.site.name,
    }
    return Response(ret)