Compare commits
	
		
			35 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					7d8c783a7d | ||
| 
						 | 
					a2e996b550 | ||
| 
						 | 
					cfc1c31050 | ||
| 
						 | 
					45106bf6f9 | ||
| 
						 | 
					6e3cfe491b | ||
| 
						 | 
					12f2158afd | ||
| 
						 | 
					6d78773c55 | ||
| 
						 | 
					43a62d4eb6 | ||
| 
						 | 
					cc08dfda96 | ||
| 
						 | 
					622e33588e | ||
| 
						 | 
					67980b58a0 | ||
| 
						 | 
					027e444955 | ||
| 
						 | 
					d838750389 | ||
| 
						 | 
					71d8bd5266 | ||
| 
						 | 
					ec4ae24bbd | ||
| 
						 | 
					1128149359 | ||
| 
						 | 
					bdfc6634ec | ||
| 
						 | 
					ca4d19667b | ||
| 
						 | 
					c71aa7baa7 | ||
| 
						 | 
					fd80ccd2c5 | ||
| 
						 | 
					9dc0b24399 | ||
| 
						 | 
					747954e6fb | ||
| 
						 | 
					274f4f227e | ||
| 
						 | 
					92197d8d49 | ||
| 
						 | 
					aee06920eb | ||
| 
						 | 
					5111b17d3c | ||
| 
						 | 
					2849d8f45d | ||
| 
						 | 
					bac60d9bd4 | ||
| 
						 | 
					9c797162f4 | ||
| 
						 | 
					09d184e2f8 | ||
| 
						 | 
					7bca618906 | ||
| 
						 | 
					67607103e9 | ||
| 
						 | 
					73c9956fe4 | ||
| 
						 | 
					b42f2ffe33 | ||
| 
						 | 
					30a3f185ef | 
@@ -556,6 +556,7 @@ class Agent(BaseAuditModel):
 | 
				
			|||||||
            run_as_user = True
 | 
					            run_as_user = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        parsed_args = script.parse_script_args(self, script.shell, args)
 | 
					        parsed_args = script.parse_script_args(self, script.shell, args)
 | 
				
			||||||
 | 
					        parsed_env_vars = script.parse_script_env_vars(self, script.shell, env_vars)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        data = {
 | 
					        data = {
 | 
				
			||||||
            "func": "runscriptfull" if full else "runscript",
 | 
					            "func": "runscriptfull" if full else "runscript",
 | 
				
			||||||
@@ -566,7 +567,7 @@ class Agent(BaseAuditModel):
 | 
				
			|||||||
                "shell": script.shell,
 | 
					                "shell": script.shell,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "run_as_user": run_as_user,
 | 
					            "run_as_user": run_as_user,
 | 
				
			||||||
            "env_vars": env_vars,
 | 
					            "env_vars": parsed_env_vars,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if history_pk != 0:
 | 
					        if history_pk != 0:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -570,6 +570,13 @@ def install_agent(request):
 | 
				
			|||||||
    from agents.utils import get_agent_url
 | 
					    from agents.utils import get_agent_url
 | 
				
			||||||
    from core.utils import token_is_valid
 | 
					    from core.utils import token_is_valid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    insecure = getattr(settings, "TRMM_INSECURE", False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if insecure and request.data["installMethod"] in {"exe", "powershell"}:
 | 
				
			||||||
 | 
					        return notify_error(
 | 
				
			||||||
 | 
					            "Not available in insecure mode. Please use the 'Manual' method."
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # TODO rework this ghetto validation hack
 | 
					    # TODO rework this ghetto validation hack
 | 
				
			||||||
    # https://github.com/amidaware/tacticalrmm/issues/1461
 | 
					    # https://github.com/amidaware/tacticalrmm/issues/1461
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
@@ -672,6 +679,9 @@ def install_agent(request):
 | 
				
			|||||||
            if int(request.data["power"]):
 | 
					            if int(request.data["power"]):
 | 
				
			||||||
                cmd.append("--power")
 | 
					                cmd.append("--power")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if insecure:
 | 
				
			||||||
 | 
					                cmd.append("--insecure")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            resp["cmd"] = " ".join(str(i) for i in cmd)
 | 
					            resp["cmd"] = " ".join(str(i) for i in cmd)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            install_flags.insert(0, f"sudo ./{inno}")
 | 
					            install_flags.insert(0, f"sudo ./{inno}")
 | 
				
			||||||
@@ -680,6 +690,8 @@ def install_agent(request):
 | 
				
			|||||||
            resp["cmd"] = (
 | 
					            resp["cmd"] = (
 | 
				
			||||||
                dl + f" && chmod +x {inno} && " + " ".join(str(i) for i in cmd)
 | 
					                dl + f" && chmod +x {inno} && " + " ".join(str(i) for i in cmd)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					            if insecure:
 | 
				
			||||||
 | 
					                resp["cmd"] += " --insecure"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        resp["url"] = download_url
 | 
					        resp["url"] = download_url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -627,8 +627,7 @@ class Alert(models.Model):
 | 
				
			|||||||
        pattern = re.compile(".*\\{\\{alert\\.(.*)\\}\\}.*")
 | 
					        pattern = re.compile(".*\\{\\{alert\\.(.*)\\}\\}.*")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for arg in args:
 | 
					        for arg in args:
 | 
				
			||||||
            match = pattern.match(arg)
 | 
					            if match := pattern.match(arg):
 | 
				
			||||||
            if match:
 | 
					 | 
				
			||||||
                name = match.group(1)
 | 
					                name = match.group(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # check if attr exists and isn't a function
 | 
					                # check if attr exists and isn't a function
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -252,7 +252,11 @@ class TaskGOGetSerializer(serializers.ModelSerializer):
 | 
				
			|||||||
                        "shell": script.shell,
 | 
					                        "shell": script.shell,
 | 
				
			||||||
                        "timeout": action["timeout"],
 | 
					                        "timeout": action["timeout"],
 | 
				
			||||||
                        "run_as_user": script.run_as_user,
 | 
					                        "run_as_user": script.run_as_user,
 | 
				
			||||||
                        "env_vars": env_vars,
 | 
					                        "env_vars": Script.parse_script_env_vars(
 | 
				
			||||||
 | 
					                            agent=agent,
 | 
				
			||||||
 | 
					                            shell=script.shell,
 | 
				
			||||||
 | 
					                            env_vars=env_vars,
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
        if actions_to_remove:
 | 
					        if actions_to_remove:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										0
									
								
								api/tacticalrmm/beta/v1/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								api/tacticalrmm/beta/v1/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								api/tacticalrmm/beta/v1/agent/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								api/tacticalrmm/beta/v1/agent/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										37
									
								
								api/tacticalrmm/beta/v1/agent/filter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								api/tacticalrmm/beta/v1/agent/filter.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					import django_filters
 | 
				
			||||||
 | 
					from agents.models import Agent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AgentFilter(django_filters.FilterSet):
 | 
				
			||||||
 | 
					    last_seen_range = django_filters.DateTimeFromToRangeFilter(field_name="last_seen")
 | 
				
			||||||
 | 
					    total_ram_range = django_filters.NumericRangeFilter(field_name="total_ram")
 | 
				
			||||||
 | 
					    patches_last_installed_range = django_filters.DateTimeFromToRangeFilter(
 | 
				
			||||||
 | 
					        field_name="patches_last_installed"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    client_id = django_filters.NumberFilter(method="client_id_filter")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Agent
 | 
				
			||||||
 | 
					        fields = [
 | 
				
			||||||
 | 
					            "id",
 | 
				
			||||||
 | 
					            "hostname",
 | 
				
			||||||
 | 
					            "agent_id",
 | 
				
			||||||
 | 
					            "operating_system",
 | 
				
			||||||
 | 
					            "plat",
 | 
				
			||||||
 | 
					            "monitoring_type",
 | 
				
			||||||
 | 
					            "needs_reboot",
 | 
				
			||||||
 | 
					            "logged_in_username",
 | 
				
			||||||
 | 
					            "last_logged_in_user",
 | 
				
			||||||
 | 
					            "alert_template",
 | 
				
			||||||
 | 
					            "site",
 | 
				
			||||||
 | 
					            "policy",
 | 
				
			||||||
 | 
					            "last_seen_range",
 | 
				
			||||||
 | 
					            "total_ram_range",
 | 
				
			||||||
 | 
					            "patches_last_installed_range",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def client_id_filter(self, queryset, name, value):
 | 
				
			||||||
 | 
					        if value:
 | 
				
			||||||
 | 
					            return queryset.filter(site__client__id=value)
 | 
				
			||||||
 | 
					        return queryset
 | 
				
			||||||
							
								
								
									
										40
									
								
								api/tacticalrmm/beta/v1/agent/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								api/tacticalrmm/beta/v1/agent/views.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					from rest_framework import viewsets
 | 
				
			||||||
 | 
					from rest_framework.permissions import IsAuthenticated
 | 
				
			||||||
 | 
					from django_filters.rest_framework import DjangoFilterBackend
 | 
				
			||||||
 | 
					from rest_framework.filters import SearchFilter, OrderingFilter
 | 
				
			||||||
 | 
					from rest_framework.request import Request
 | 
				
			||||||
 | 
					from rest_framework.serializers import BaseSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from agents.models import Agent
 | 
				
			||||||
 | 
					from agents.permissions import AgentPerms
 | 
				
			||||||
 | 
					from beta.v1.agent.filter import AgentFilter
 | 
				
			||||||
 | 
					from beta.v1.pagination import StandardResultsSetPagination
 | 
				
			||||||
 | 
					from ..serializers import DetailAgentSerializer, ListAgentSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AgentViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
 | 
					    permission_classes = [IsAuthenticated, AgentPerms]
 | 
				
			||||||
 | 
					    queryset = Agent.objects.all()
 | 
				
			||||||
 | 
					    pagination_class = StandardResultsSetPagination
 | 
				
			||||||
 | 
					    http_method_names = ["get", "put"]
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
 | 
				
			||||||
 | 
					    filterset_class = AgentFilter
 | 
				
			||||||
 | 
					    search_fields = ["hostname", "services"]
 | 
				
			||||||
 | 
					    ordering_fields = ["id"]
 | 
				
			||||||
 | 
					    ordering = ["id"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_permissions(self, request: Request) -> None:
 | 
				
			||||||
 | 
					        if "agent_id" in request.query_params:
 | 
				
			||||||
 | 
					            self.kwargs["agent_id"] = request.query_params["agent_id"]
 | 
				
			||||||
 | 
					        super().check_permissions(request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_permissions(self):
 | 
				
			||||||
 | 
					        if self.request.method == "POST":
 | 
				
			||||||
 | 
					            self.permission_classes = [IsAuthenticated]
 | 
				
			||||||
 | 
					        return super().get_permissions()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_serializer_class(self) -> type[BaseSerializer]:
 | 
				
			||||||
 | 
					        if self.kwargs:
 | 
				
			||||||
 | 
					            if self.kwargs["pk"]:
 | 
				
			||||||
 | 
					                return DetailAgentSerializer
 | 
				
			||||||
 | 
					        return ListAgentSerializer
 | 
				
			||||||
							
								
								
									
										0
									
								
								api/tacticalrmm/beta/v1/client/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								api/tacticalrmm/beta/v1/client/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										13
									
								
								api/tacticalrmm/beta/v1/client/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								api/tacticalrmm/beta/v1/client/views.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					from rest_framework import viewsets
 | 
				
			||||||
 | 
					from rest_framework.permissions import IsAuthenticated
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from clients.models import Client
 | 
				
			||||||
 | 
					from clients.permissions import ClientsPerms
 | 
				
			||||||
 | 
					from ..serializers import ClientSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ClientViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
 | 
					    permission_classes = [IsAuthenticated, ClientsPerms]
 | 
				
			||||||
 | 
					    queryset = Client.objects.all()
 | 
				
			||||||
 | 
					    serializer_class = ClientSerializer
 | 
				
			||||||
 | 
					    http_method_names = ["get", "put"]
 | 
				
			||||||
							
								
								
									
										7
									
								
								api/tacticalrmm/beta/v1/pagination.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								api/tacticalrmm/beta/v1/pagination.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					from rest_framework.pagination import PageNumberPagination
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StandardResultsSetPagination(PageNumberPagination):
 | 
				
			||||||
 | 
					    page_size = 100
 | 
				
			||||||
 | 
					    page_size_query_param = "page_size"
 | 
				
			||||||
 | 
					    max_page_size = 1000
 | 
				
			||||||
							
								
								
									
										73
									
								
								api/tacticalrmm/beta/v1/serializers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								api/tacticalrmm/beta/v1/serializers.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					from rest_framework import serializers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from agents.models import Agent
 | 
				
			||||||
 | 
					from clients.models import Client, Site
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ListAgentSerializer(serializers.ModelSerializer[Agent]):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Agent
 | 
				
			||||||
 | 
					        fields = "__all__"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DetailAgentSerializer(serializers.ModelSerializer[Agent]):
 | 
				
			||||||
 | 
					    status = serializers.ReadOnlyField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Agent
 | 
				
			||||||
 | 
					        fields = (
 | 
				
			||||||
 | 
					            "version",
 | 
				
			||||||
 | 
					            "operating_system",
 | 
				
			||||||
 | 
					            "plat",
 | 
				
			||||||
 | 
					            "goarch",
 | 
				
			||||||
 | 
					            "hostname",
 | 
				
			||||||
 | 
					            "agent_id",
 | 
				
			||||||
 | 
					            "last_seen",
 | 
				
			||||||
 | 
					            "services",
 | 
				
			||||||
 | 
					            "public_ip",
 | 
				
			||||||
 | 
					            "total_ram",
 | 
				
			||||||
 | 
					            "disks",
 | 
				
			||||||
 | 
					            "boot_time",
 | 
				
			||||||
 | 
					            "logged_in_username",
 | 
				
			||||||
 | 
					            "last_logged_in_user",
 | 
				
			||||||
 | 
					            "monitoring_type",
 | 
				
			||||||
 | 
					            "description",
 | 
				
			||||||
 | 
					            "mesh_node_id",
 | 
				
			||||||
 | 
					            "overdue_email_alert",
 | 
				
			||||||
 | 
					            "overdue_text_alert",
 | 
				
			||||||
 | 
					            "overdue_dashboard_alert",
 | 
				
			||||||
 | 
					            "offline_time",
 | 
				
			||||||
 | 
					            "overdue_time",
 | 
				
			||||||
 | 
					            "check_interval",
 | 
				
			||||||
 | 
					            "needs_reboot",
 | 
				
			||||||
 | 
					            "choco_installed",
 | 
				
			||||||
 | 
					            "wmi_detail",
 | 
				
			||||||
 | 
					            "patches_last_installed",
 | 
				
			||||||
 | 
					            "time_zone",
 | 
				
			||||||
 | 
					            "maintenance_mode",
 | 
				
			||||||
 | 
					            "block_policy_inheritance",
 | 
				
			||||||
 | 
					            "alert_template",
 | 
				
			||||||
 | 
					            "site",
 | 
				
			||||||
 | 
					            "policy",
 | 
				
			||||||
 | 
					            "status",
 | 
				
			||||||
 | 
					            "checks",
 | 
				
			||||||
 | 
					            "pending_actions_count",
 | 
				
			||||||
 | 
					            "cpu_model",
 | 
				
			||||||
 | 
					            "graphics",
 | 
				
			||||||
 | 
					            "local_ips",
 | 
				
			||||||
 | 
					            "make_model",
 | 
				
			||||||
 | 
					            "physical_disks",
 | 
				
			||||||
 | 
					            "serial_number",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ClientSerializer(serializers.ModelSerializer[Client]):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Client
 | 
				
			||||||
 | 
					        fields = "__all__"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SiteSerializer(serializers.ModelSerializer[Site]):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Site
 | 
				
			||||||
 | 
					        fields = "__all__"
 | 
				
			||||||
							
								
								
									
										21
									
								
								api/tacticalrmm/beta/v1/site/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								api/tacticalrmm/beta/v1/site/views.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					from rest_framework import viewsets
 | 
				
			||||||
 | 
					from rest_framework.permissions import IsAuthenticated
 | 
				
			||||||
 | 
					from django_filters.rest_framework import DjangoFilterBackend
 | 
				
			||||||
 | 
					from rest_framework.filters import SearchFilter, OrderingFilter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from clients.models import Site
 | 
				
			||||||
 | 
					from clients.permissions import SitesPerms
 | 
				
			||||||
 | 
					from beta.v1.pagination import StandardResultsSetPagination
 | 
				
			||||||
 | 
					from ..serializers import SiteSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SiteViewSet(viewsets.ModelViewSet):
 | 
				
			||||||
 | 
					    permission_classes = [IsAuthenticated, SitesPerms]
 | 
				
			||||||
 | 
					    queryset = Site.objects.all()
 | 
				
			||||||
 | 
					    serializer_class = SiteSerializer
 | 
				
			||||||
 | 
					    pagination_class = StandardResultsSetPagination
 | 
				
			||||||
 | 
					    http_method_names = ["get", "put"]
 | 
				
			||||||
 | 
					    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
 | 
				
			||||||
 | 
					    search_fields = ["name"]
 | 
				
			||||||
 | 
					    ordering_fields = ["id"]
 | 
				
			||||||
 | 
					    ordering = ["id"]
 | 
				
			||||||
							
								
								
									
										12
									
								
								api/tacticalrmm/beta/v1/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								api/tacticalrmm/beta/v1/urls.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					from rest_framework import routers
 | 
				
			||||||
 | 
					from .agent import views as agent
 | 
				
			||||||
 | 
					from .client import views as client
 | 
				
			||||||
 | 
					from .site import views as site
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router = routers.DefaultRouter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router.register("agent", agent.AgentViewSet, basename="agent")
 | 
				
			||||||
 | 
					router.register("client", client.ClientViewSet, basename="client")
 | 
				
			||||||
 | 
					router.register("site", site.SiteViewSet, basename="site")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					urlpatterns = router.urls
 | 
				
			||||||
@@ -172,8 +172,14 @@ class CheckRunnerGetSerializer(serializers.ModelSerializer):
 | 
				
			|||||||
        if obj.check_type != CheckType.SCRIPT:
 | 
					        if obj.check_type != CheckType.SCRIPT:
 | 
				
			||||||
            return []
 | 
					            return []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # check's env_vars override the script's env vars
 | 
					        agent = self.context["agent"] if "agent" in self.context.keys() else obj.agent
 | 
				
			||||||
        return obj.env_vars or obj.script.env_vars
 | 
					
 | 
				
			||||||
 | 
					        return Script.parse_script_env_vars(
 | 
				
			||||||
 | 
					            agent=agent,
 | 
				
			||||||
 | 
					            shell=obj.script.shell,
 | 
				
			||||||
 | 
					            env_vars=obj.env_vars
 | 
				
			||||||
 | 
					            or obj.script.env_vars,  # check's env_vars override the script's env vars
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = Check
 | 
					        model = Check
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -172,6 +172,31 @@ class TestCheckViews(TacticalTestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.check_not_authenticated("post", url)
 | 
					        self.check_not_authenticated("post", url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_reset_all_checks_status(self):
 | 
				
			||||||
 | 
					        # setup data
 | 
				
			||||||
 | 
					        agent = baker.make_recipe("agents.agent")
 | 
				
			||||||
 | 
					        check = baker.make_recipe("checks.diskspace_check", agent=agent)
 | 
				
			||||||
 | 
					        baker.make("checks.CheckResult", assigned_check=check, agent=agent)
 | 
				
			||||||
 | 
					        baker.make(
 | 
				
			||||||
 | 
					            "checks.CheckHistory",
 | 
				
			||||||
 | 
					            check_id=check.id,
 | 
				
			||||||
 | 
					            agent_id=agent.agent_id,
 | 
				
			||||||
 | 
					            _quantity=30,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        baker.make(
 | 
				
			||||||
 | 
					            "checks.CheckHistory",
 | 
				
			||||||
 | 
					            check_id=check.id,
 | 
				
			||||||
 | 
					            agent_id=agent.agent_id,
 | 
				
			||||||
 | 
					            _quantity=30,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        url = f"{base_url}/{agent.agent_id}/resetall/"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        resp = self.client.post(url)
 | 
				
			||||||
 | 
					        self.assertEqual(resp.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.check_not_authenticated("post", url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_add_memory_check(self):
 | 
					    def test_add_memory_check(self):
 | 
				
			||||||
        url = f"{base_url}/"
 | 
					        url = f"{base_url}/"
 | 
				
			||||||
        agent = baker.make_recipe("agents.agent")
 | 
					        agent = baker.make_recipe("agents.agent")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ urlpatterns = [
 | 
				
			|||||||
    path("", views.GetAddChecks.as_view()),
 | 
					    path("", views.GetAddChecks.as_view()),
 | 
				
			||||||
    path("<int:pk>/", views.GetUpdateDeleteCheck.as_view()),
 | 
					    path("<int:pk>/", views.GetUpdateDeleteCheck.as_view()),
 | 
				
			||||||
    path("<int:pk>/reset/", views.ResetCheck.as_view()),
 | 
					    path("<int:pk>/reset/", views.ResetCheck.as_view()),
 | 
				
			||||||
 | 
					    path("<agent:agent_id>/resetall/", views.ResetAllChecksStatus.as_view()),
 | 
				
			||||||
    path("<agent:agent_id>/run/", views.run_checks),
 | 
					    path("<agent:agent_id>/run/", views.run_checks),
 | 
				
			||||||
    path("<int:pk>/history/", views.GetCheckHistory.as_view()),
 | 
					    path("<int:pk>/history/", views.GetCheckHistory.as_view()),
 | 
				
			||||||
    path("<str:target>/<int:pk>/csbulkrun/", views.bulk_run_checks),
 | 
					    path("<str:target>/<int:pk>/csbulkrun/", views.bulk_run_checks),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import asyncio
 | 
					import asyncio
 | 
				
			||||||
from datetime import datetime as dt
 | 
					from datetime import datetime as dt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.db.models import Q
 | 
					from django.db.models import Prefetch, Q
 | 
				
			||||||
from django.shortcuts import get_object_or_404
 | 
					from django.shortcuts import get_object_or_404
 | 
				
			||||||
from django.utils import timezone as djangotime
 | 
					from django.utils import timezone as djangotime
 | 
				
			||||||
from rest_framework.decorators import api_view, permission_classes
 | 
					from rest_framework.decorators import api_view, permission_classes
 | 
				
			||||||
@@ -13,7 +13,7 @@ from rest_framework.views import APIView
 | 
				
			|||||||
from agents.models import Agent
 | 
					from agents.models import Agent
 | 
				
			||||||
from alerts.models import Alert
 | 
					from alerts.models import Alert
 | 
				
			||||||
from automation.models import Policy
 | 
					from automation.models import Policy
 | 
				
			||||||
from tacticalrmm.constants import CheckStatus, CheckType
 | 
					from tacticalrmm.constants import AGENT_DEFER, CheckStatus, CheckType
 | 
				
			||||||
from tacticalrmm.exceptions import NatsDown
 | 
					from tacticalrmm.exceptions import NatsDown
 | 
				
			||||||
from tacticalrmm.helpers import notify_error
 | 
					from tacticalrmm.helpers import notify_error
 | 
				
			||||||
from tacticalrmm.nats_utils import abulk_nats_command
 | 
					from tacticalrmm.nats_utils import abulk_nats_command
 | 
				
			||||||
@@ -122,15 +122,54 @@ class ResetCheck(APIView):
 | 
				
			|||||||
        result.save()
 | 
					        result.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # resolve any alerts that are open
 | 
					        # resolve any alerts that are open
 | 
				
			||||||
        alert = Alert.create_or_return_check_alert(
 | 
					        if alert := Alert.create_or_return_check_alert(
 | 
				
			||||||
            result.assigned_check, agent=result.agent, skip_create=True
 | 
					            result.assigned_check, agent=result.agent, skip_create=True
 | 
				
			||||||
        )
 | 
					        ):
 | 
				
			||||||
        if alert:
 | 
					 | 
				
			||||||
            alert.resolve()
 | 
					            alert.resolve()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Response("The check status was reset")
 | 
					        return Response("The check status was reset")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ResetAllChecksStatus(APIView):
 | 
				
			||||||
 | 
					    permission_classes = [IsAuthenticated, ChecksPerms]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def post(self, request, agent_id):
 | 
				
			||||||
 | 
					        agent = get_object_or_404(
 | 
				
			||||||
 | 
					            Agent.objects.defer(*AGENT_DEFER)
 | 
				
			||||||
 | 
					            .select_related(
 | 
				
			||||||
 | 
					                "policy",
 | 
				
			||||||
 | 
					                "policy__alert_template",
 | 
				
			||||||
 | 
					                "alert_template",
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .prefetch_related(
 | 
				
			||||||
 | 
					                Prefetch(
 | 
				
			||||||
 | 
					                    "checkresults",
 | 
				
			||||||
 | 
					                    queryset=CheckResult.objects.select_related("assigned_check"),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                "agentchecks",
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            agent_id=agent_id,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not _has_perm_on_agent(request.user, agent.agent_id):
 | 
				
			||||||
 | 
					            raise PermissionDenied()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for check in agent.get_checks_with_policies():
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                result = check.check_result
 | 
				
			||||||
 | 
					                result.status = CheckStatus.PASSING
 | 
				
			||||||
 | 
					                result.save()
 | 
				
			||||||
 | 
					                if alert := Alert.create_or_return_check_alert(
 | 
				
			||||||
 | 
					                    result.assigned_check, agent=agent, skip_create=True
 | 
				
			||||||
 | 
					                ):
 | 
				
			||||||
 | 
					                    alert.resolve()
 | 
				
			||||||
 | 
					            except:
 | 
				
			||||||
 | 
					                # check hasn't run yet, no check result entry
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Response("All checks status were reset")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class GetCheckHistory(APIView):
 | 
					class GetCheckHistory(APIView):
 | 
				
			||||||
    permission_classes = [IsAuthenticated, ChecksPerms]
 | 
					    permission_classes = [IsAuthenticated, ChecksPerms]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ import re
 | 
				
			|||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
from contextlib import suppress
 | 
					from contextlib import suppress
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
from django.db.models import Count, Exists, OuterRef, Prefetch, prefetch_related_objects
 | 
					from django.db.models import Count, Exists, OuterRef, Prefetch, prefetch_related_objects
 | 
				
			||||||
from django.shortcuts import get_object_or_404
 | 
					from django.shortcuts import get_object_or_404
 | 
				
			||||||
from django.utils import timezone as djangotime
 | 
					from django.utils import timezone as djangotime
 | 
				
			||||||
@@ -288,6 +289,9 @@ class AgentDeployment(APIView):
 | 
				
			|||||||
        return Response(DeploymentSerializer(deps, many=True).data)
 | 
					        return Response(DeploymentSerializer(deps, many=True).data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def post(self, request):
 | 
					    def post(self, request):
 | 
				
			||||||
 | 
					        if getattr(settings, "TRMM_INSECURE", False):
 | 
				
			||||||
 | 
					            return notify_error("Not available in insecure mode")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        from accounts.models import User
 | 
					        from accounts.models import User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        site = get_object_or_404(Site, pk=request.data["site"])
 | 
					        site = get_object_or_404(Site, pk=request.data["site"])
 | 
				
			||||||
@@ -343,6 +347,9 @@ class GenerateAgent(APIView):
 | 
				
			|||||||
    permission_classes = (AllowAny,)
 | 
					    permission_classes = (AllowAny,)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get(self, request, uid):
 | 
					    def get(self, request, uid):
 | 
				
			||||||
 | 
					        if getattr(settings, "TRMM_INSECURE", False):
 | 
				
			||||||
 | 
					            return notify_error("Not available in insecure mode")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        from tacticalrmm.utils import generate_winagent_exe
 | 
					        from tacticalrmm.utils import generate_winagent_exe
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,19 @@ if [ "${HAS_SYSTEMD}" != 'systemd' ]; then
 | 
				
			|||||||
    exit 1
 | 
					    exit 1
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [[ $DISPLAY ]]; then
 | 
				
			||||||
 | 
					    echo "ERROR: Display detected. Installer only supports running headless, i.e from ssh."
 | 
				
			||||||
 | 
					    echo "If you cannot ssh in then please run 'sudo systemctl isolate multi-user.target' to switch to a non-graphical user session and run the installer again."
 | 
				
			||||||
 | 
					    echo "If you are already running headless, then you are probably running with X forwarding which is setting DISPLAY, if so then simply run"
 | 
				
			||||||
 | 
					    echo "unset DISPLAY"
 | 
				
			||||||
 | 
					    echo "to unset the variable and then try running the installer again"
 | 
				
			||||||
 | 
					    exit 1
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEBUG=0
 | 
				
			||||||
 | 
					INSECURE=0
 | 
				
			||||||
 | 
					NOMESH=0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
agentDL='agentDLChange'
 | 
					agentDL='agentDLChange'
 | 
				
			||||||
meshDL='meshDLChange'
 | 
					meshDL='meshDLChange'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -124,6 +137,19 @@ if [ $# -ne 0 ] && [ $1 == 'uninstall' ]; then
 | 
				
			|||||||
    exit 0
 | 
					    exit 0
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					while [[ "$#" -gt 0 ]]; do
 | 
				
			||||||
 | 
					    case $1 in
 | 
				
			||||||
 | 
					    --debug) DEBUG=1 ;;
 | 
				
			||||||
 | 
					    --insecure) INSECURE=1 ;;
 | 
				
			||||||
 | 
					    --nomesh) NOMESH=1 ;;
 | 
				
			||||||
 | 
					    *)
 | 
				
			||||||
 | 
					        echo "ERROR: Unknown parameter: $1"
 | 
				
			||||||
 | 
					        exit 1
 | 
				
			||||||
 | 
					        ;;
 | 
				
			||||||
 | 
					    esac
 | 
				
			||||||
 | 
					    shift
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RemoveOldAgent
 | 
					RemoveOldAgent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo "Downloading tactical agent..."
 | 
					echo "Downloading tactical agent..."
 | 
				
			||||||
@@ -136,7 +162,7 @@ chmod +x ${agentBin}
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
MESH_NODE_ID=""
 | 
					MESH_NODE_ID=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [ $# -ne 0 ] && [ $1 == '--nomesh' ]; then
 | 
					if [[ $NOMESH -eq 1 ]]; then
 | 
				
			||||||
    echo "Skipping mesh install"
 | 
					    echo "Skipping mesh install"
 | 
				
			||||||
else
 | 
					else
 | 
				
			||||||
    if [ -f "${meshSystemBin}" ]; then
 | 
					    if [ -f "${meshSystemBin}" ]; then
 | 
				
			||||||
@@ -154,18 +180,22 @@ if [ ! -d "${agentBinPath}" ]; then
 | 
				
			|||||||
    mkdir -p ${agentBinPath}
 | 
					    mkdir -p ${agentBinPath}
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [ $# -ne 0 ] && [ $1 == '--debug' ]; then
 | 
					 | 
				
			||||||
    INSTALL_CMD="${agentBin} -m install -api ${apiURL} -client-id ${clientID} -site-id ${siteID} -agent-type ${agentType} -auth ${token} -log debug"
 | 
					 | 
				
			||||||
else
 | 
					 | 
				
			||||||
INSTALL_CMD="${agentBin} -m install -api ${apiURL} -client-id ${clientID} -site-id ${siteID} -agent-type ${agentType} -auth ${token}"
 | 
					INSTALL_CMD="${agentBin} -m install -api ${apiURL} -client-id ${clientID} -site-id ${siteID} -agent-type ${agentType} -auth ${token}"
 | 
				
			||||||
fi
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [ "${MESH_NODE_ID}" != '' ]; then
 | 
					if [ "${MESH_NODE_ID}" != '' ]; then
 | 
				
			||||||
    INSTALL_CMD+=" -meshnodeid ${MESH_NODE_ID}"
 | 
					    INSTALL_CMD+=" --meshnodeid ${MESH_NODE_ID}"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [[ $DEBUG -eq 1 ]]; then
 | 
				
			||||||
 | 
					    INSTALL_CMD+=" --log debug"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [[ $INSECURE -eq 1 ]]; then
 | 
				
			||||||
 | 
					    INSTALL_CMD+=" --insecure"
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [ "${proxy}" != '' ]; then
 | 
					if [ "${proxy}" != '' ]; then
 | 
				
			||||||
    INSTALL_CMD+=" -proxy ${proxy}"
 | 
					    INSTALL_CMD+=" --proxy ${proxy}"
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
eval ${INSTALL_CMD}
 | 
					eval ${INSTALL_CMD}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ import os
 | 
				
			|||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.core.management.base import BaseCommand
 | 
					from django.core.management.base import BaseCommand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from tacticalrmm.helpers import get_nats_ports
 | 
					from tacticalrmm.helpers import get_nats_internal_protocol, get_nats_ports
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Command(BaseCommand):
 | 
					class Command(BaseCommand):
 | 
				
			||||||
@@ -21,9 +21,10 @@ class Command(BaseCommand):
 | 
				
			|||||||
            ssl = "disable"
 | 
					            ssl = "disable"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        nats_std_port, _ = get_nats_ports()
 | 
					        nats_std_port, _ = get_nats_ports()
 | 
				
			||||||
 | 
					        proto = get_nats_internal_protocol()
 | 
				
			||||||
        config = {
 | 
					        config = {
 | 
				
			||||||
            "key": settings.SECRET_KEY,
 | 
					            "key": settings.SECRET_KEY,
 | 
				
			||||||
            "natsurl": f"tls://{settings.ALLOWED_HOSTS[0]}:{nats_std_port}",
 | 
					            "natsurl": f"{proto}://{settings.ALLOWED_HOSTS[0]}:{nats_std_port}",
 | 
				
			||||||
            "user": db["USER"],
 | 
					            "user": db["USER"],
 | 
				
			||||||
            "pass": db["PASSWORD"],
 | 
					            "pass": db["PASSWORD"],
 | 
				
			||||||
            "host": db["HOST"],
 | 
					            "host": db["HOST"],
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,4 +11,7 @@ class Command(BaseCommand):
 | 
				
			|||||||
        self.stdout.write(self.style.WARNING("Cleaning the cache"))
 | 
					        self.stdout.write(self.style.WARNING("Cleaning the cache"))
 | 
				
			||||||
        clear_entire_cache()
 | 
					        clear_entire_cache()
 | 
				
			||||||
        self.stdout.write(self.style.SUCCESS("Cache was cleared!"))
 | 
					        self.stdout.write(self.style.SUCCESS("Cache was cleared!"))
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
            call_command("fix_dupe_agent_customfields")
 | 
					            call_command("fix_dupe_agent_customfields")
 | 
				
			||||||
 | 
					        except:
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -502,3 +502,27 @@ class TestCoreUtils(TacticalTestCase):
 | 
				
			|||||||
            r,
 | 
					            r,
 | 
				
			||||||
            "http://tactical-meshcentral:4443/meshagents?id=4&meshid=abc123&installflags=0",
 | 
					            "http://tactical-meshcentral:4443/meshagents?id=4&meshid=abc123&installflags=0",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @override_settings(TRMM_INSECURE=True)
 | 
				
			||||||
 | 
					    def test_get_meshagent_url_insecure(self):
 | 
				
			||||||
 | 
					        r = get_meshagent_url(
 | 
				
			||||||
 | 
					            ident=MeshAgentIdent.DARWIN_UNIVERSAL,
 | 
				
			||||||
 | 
					            plat="darwin",
 | 
				
			||||||
 | 
					            mesh_site="https://mesh.example.com",
 | 
				
			||||||
 | 
					            mesh_device_id="abc123",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            r,
 | 
				
			||||||
 | 
					            "http://mesh.example.com:4430/meshagents?id=abc123&installflags=2&meshinstall=10005",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = get_meshagent_url(
 | 
				
			||||||
 | 
					            ident=MeshAgentIdent.WIN64,
 | 
				
			||||||
 | 
					            plat="windows",
 | 
				
			||||||
 | 
					            mesh_site="https://mesh.example.com",
 | 
				
			||||||
 | 
					            mesh_device_id="abc123",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            r,
 | 
				
			||||||
 | 
					            "http://mesh.example.com:4430/meshagents?id=4&meshid=abc123&installflags=0",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -87,6 +87,10 @@ def get_mesh_ws_url() -> str:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if settings.DOCKER_BUILD:
 | 
					    if settings.DOCKER_BUILD:
 | 
				
			||||||
        uri = f"{settings.MESH_WS_URL}/control.ashx?auth={token}"
 | 
					        uri = f"{settings.MESH_WS_URL}/control.ashx?auth={token}"
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        if getattr(settings, "TRMM_INSECURE", False):
 | 
				
			||||||
 | 
					            site = core.mesh_site.replace("https", "ws")
 | 
				
			||||||
 | 
					            uri = f"{site}:4430/control.ashx?auth={token}"
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            site = core.mesh_site.replace("https", "wss")
 | 
					            site = core.mesh_site.replace("https", "wss")
 | 
				
			||||||
            uri = f"{site}/control.ashx?auth={token}"
 | 
					            uri = f"{site}/control.ashx?auth={token}"
 | 
				
			||||||
@@ -181,6 +185,8 @@ def get_meshagent_url(
 | 
				
			|||||||
) -> str:
 | 
					) -> str:
 | 
				
			||||||
    if settings.DOCKER_BUILD:
 | 
					    if settings.DOCKER_BUILD:
 | 
				
			||||||
        base = settings.MESH_WS_URL.replace("ws://", "http://")
 | 
					        base = settings.MESH_WS_URL.replace("ws://", "http://")
 | 
				
			||||||
 | 
					    elif getattr(settings, "TRMM_INSECURE", False):
 | 
				
			||||||
 | 
					        base = mesh_site.replace("https", "http") + ":4430"
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        base = mesh_site
 | 
					        base = mesh_site
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,27 +1,28 @@
 | 
				
			|||||||
adrf==0.1.1
 | 
					adrf==0.1.2
 | 
				
			||||||
asgiref==3.7.2
 | 
					asgiref==3.7.2
 | 
				
			||||||
celery==5.3.1
 | 
					celery==5.3.1
 | 
				
			||||||
certifi==2023.7.22
 | 
					certifi==2023.7.22
 | 
				
			||||||
cffi==1.15.1
 | 
					cffi==1.15.1
 | 
				
			||||||
channels==4.0.0
 | 
					channels==4.0.0
 | 
				
			||||||
channels_redis==4.1.0
 | 
					channels_redis==4.1.0
 | 
				
			||||||
cryptography==41.0.3
 | 
					cryptography==41.0.4
 | 
				
			||||||
daphne==4.0.0
 | 
					daphne==4.0.0
 | 
				
			||||||
Django==4.2.4
 | 
					Django==4.2.5
 | 
				
			||||||
django-cors-headers==4.2.0
 | 
					django-cors-headers==4.2.0
 | 
				
			||||||
 | 
					django-filter==23.3
 | 
				
			||||||
django-ipware==5.0.0
 | 
					django-ipware==5.0.0
 | 
				
			||||||
django-rest-knox==4.2.0
 | 
					django-rest-knox==4.2.0
 | 
				
			||||||
djangorestframework==3.14.0
 | 
					djangorestframework==3.14.0
 | 
				
			||||||
drf-spectacular==0.26.4
 | 
					drf-spectacular==0.26.5
 | 
				
			||||||
hiredis==2.2.3
 | 
					hiredis==2.2.3
 | 
				
			||||||
meshctrl==0.1.15
 | 
					meshctrl==0.1.15
 | 
				
			||||||
msgpack==1.0.5
 | 
					msgpack==1.0.7
 | 
				
			||||||
nats-py==2.3.1
 | 
					nats-py==2.4.0
 | 
				
			||||||
packaging==23.1
 | 
					packaging==23.1
 | 
				
			||||||
psutil==5.9.5
 | 
					psutil==5.9.5
 | 
				
			||||||
psycopg[binary]==3.1.10
 | 
					psycopg[binary]==3.1.12
 | 
				
			||||||
pycparser==2.21
 | 
					pycparser==2.21
 | 
				
			||||||
pycryptodome==3.18.0
 | 
					pycryptodome==3.19.0
 | 
				
			||||||
pyotp==2.9.0
 | 
					pyotp==2.9.0
 | 
				
			||||||
pyparsing==3.1.1
 | 
					pyparsing==3.1.1
 | 
				
			||||||
pytz==2023.3
 | 
					pytz==2023.3
 | 
				
			||||||
@@ -30,10 +31,10 @@ redis==4.5.5
 | 
				
			|||||||
requests==2.31.0
 | 
					requests==2.31.0
 | 
				
			||||||
six==1.16.0
 | 
					six==1.16.0
 | 
				
			||||||
sqlparse==0.4.4
 | 
					sqlparse==0.4.4
 | 
				
			||||||
twilio==8.5.0
 | 
					twilio==8.9.0
 | 
				
			||||||
urllib3==2.0.4
 | 
					urllib3==2.0.5
 | 
				
			||||||
uWSGI==2.0.22
 | 
					uWSGI==2.0.22
 | 
				
			||||||
validators==0.20.0
 | 
					validators==0.20.0
 | 
				
			||||||
vine==5.0.0
 | 
					vine==5.0.0
 | 
				
			||||||
websockets==11.0.3
 | 
					websockets==11.0.3
 | 
				
			||||||
zipp==3.16.2
 | 
					zipp==3.17.0
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -194,6 +194,7 @@ class Script(BaseAuditModel):
 | 
				
			|||||||
        return ScriptSerializer(script).data
 | 
					        return ScriptSerializer(script).data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    # TODO refactor common functionality of parse functions
 | 
				
			||||||
    def parse_script_args(cls, agent, shell: str, args: List[str] = []) -> list:
 | 
					    def parse_script_args(cls, agent, shell: str, args: List[str] = []) -> list:
 | 
				
			||||||
        if not args:
 | 
					        if not args:
 | 
				
			||||||
            return []
 | 
					            return []
 | 
				
			||||||
@@ -204,8 +205,7 @@ class Script(BaseAuditModel):
 | 
				
			|||||||
        pattern = re.compile(".*\\{\\{(.*)\\}\\}.*")
 | 
					        pattern = re.compile(".*\\{\\{(.*)\\}\\}.*")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for arg in args:
 | 
					        for arg in args:
 | 
				
			||||||
            match = pattern.match(arg)
 | 
					            if match := pattern.match(arg):
 | 
				
			||||||
            if match:
 | 
					 | 
				
			||||||
                # only get the match between the () in regex
 | 
					                # only get the match between the () in regex
 | 
				
			||||||
                string = match.group(1)
 | 
					                string = match.group(1)
 | 
				
			||||||
                value = replace_db_values(
 | 
					                value = replace_db_values(
 | 
				
			||||||
@@ -231,6 +231,42 @@ class Script(BaseAuditModel):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return temp_args
 | 
					        return temp_args
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    # TODO refactor common functionality of parse functions
 | 
				
			||||||
 | 
					    def parse_script_env_vars(cls, agent, shell: str, env_vars: list[str] = []) -> list:
 | 
				
			||||||
 | 
					        if not env_vars:
 | 
				
			||||||
 | 
					            return []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        temp_env_vars = []
 | 
				
			||||||
 | 
					        pattern = re.compile(".*\\{\\{(.*)\\}\\}.*")
 | 
				
			||||||
 | 
					        for env_var in env_vars:
 | 
				
			||||||
 | 
					            # must be in format KEY=VALUE
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                env_key = env_var.split("=")[0]
 | 
				
			||||||
 | 
					                env_val = env_var.split("=")[1]
 | 
				
			||||||
 | 
					            except:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            if match := pattern.match(env_val):
 | 
				
			||||||
 | 
					                string = match.group(1)
 | 
				
			||||||
 | 
					                value = replace_db_values(
 | 
				
			||||||
 | 
					                    string=string,
 | 
				
			||||||
 | 
					                    instance=agent,
 | 
				
			||||||
 | 
					                    shell=shell,
 | 
				
			||||||
 | 
					                    quotes=False,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if value:
 | 
				
			||||||
 | 
					                    try:
 | 
				
			||||||
 | 
					                        new_val = re.sub("\\{\\{.*\\}\\}", value, env_val)
 | 
				
			||||||
 | 
					                    except re.error:
 | 
				
			||||||
 | 
					                        new_val = re.sub("\\{\\{.*\\}\\}", re.escape(value), env_val)
 | 
				
			||||||
 | 
					                    temp_env_vars.append(f"{env_key}={new_val}")
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                # pass parameter unaltered
 | 
				
			||||||
 | 
					                temp_env_vars.append(env_var)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return temp_env_vars
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ScriptSnippet(models.Model):
 | 
					class ScriptSnippet(models.Model):
 | 
				
			||||||
    name = CharField(max_length=40, unique=True)
 | 
					    name = CharField(max_length=40, unique=True)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,7 +77,7 @@ def bulk_script_task(
 | 
				
			|||||||
                "shell": script.shell,
 | 
					                "shell": script.shell,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "run_as_user": run_as_user,
 | 
					            "run_as_user": run_as_user,
 | 
				
			||||||
            "env_vars": env_vars,
 | 
					            "env_vars": script.parse_script_env_vars(agent, script.shell, env_vars),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        tup = (agent.agent_id, data)
 | 
					        tup = (agent.agent_id, data)
 | 
				
			||||||
        items.append(tup)
 | 
					        items.append(tup)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -242,6 +242,25 @@ class TestScriptViews(TacticalTestCase):
 | 
				
			|||||||
            Script.parse_script_args(agent=agent, shell=ScriptShell.PYTHON, args=args),
 | 
					            Script.parse_script_args(agent=agent, shell=ScriptShell.PYTHON, args=args),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_script_env_vars_variable_replacement(self):
 | 
				
			||||||
 | 
					        agent = baker.make_recipe("agents.agent", public_ip="12.12.12.12")
 | 
				
			||||||
 | 
					        env_vars = [
 | 
				
			||||||
 | 
					            "PUBIP={{agent.public_ip}}",
 | 
				
			||||||
 | 
					            "123CLIENT={{client.name}}",
 | 
				
			||||||
 | 
					            "FOOBARSITE={{site.name}}",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                "PUBIP=12.12.12.12",
 | 
				
			||||||
 | 
					                f"123CLIENT={agent.client.name}",
 | 
				
			||||||
 | 
					                f"FOOBARSITE={agent.site.name}",
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            Script.parse_script_env_vars(
 | 
				
			||||||
 | 
					                agent=agent, shell=ScriptShell.POWERSHELL, env_vars=env_vars
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_script_arg_replacement_custom_field(self):
 | 
					    def test_script_arg_replacement_custom_field(self):
 | 
				
			||||||
        agent = baker.make_recipe("agents.agent")
 | 
					        agent = baker.make_recipe("agents.agent")
 | 
				
			||||||
        field = baker.make(
 | 
					        field = baker.make(
 | 
				
			||||||
@@ -272,6 +291,40 @@ class TestScriptViews(TacticalTestCase):
 | 
				
			|||||||
            Script.parse_script_args(agent=agent, shell=ScriptShell.PYTHON, args=args),
 | 
					            Script.parse_script_args(agent=agent, shell=ScriptShell.PYTHON, args=args),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_script_env_vars_replacement_custom_field(self):
 | 
				
			||||||
 | 
					        agent = baker.make_recipe("agents.agent")
 | 
				
			||||||
 | 
					        field = baker.make(
 | 
				
			||||||
 | 
					            "core.CustomField",
 | 
				
			||||||
 | 
					            name="Test Field",
 | 
				
			||||||
 | 
					            model=CustomFieldModel.AGENT,
 | 
				
			||||||
 | 
					            type=CustomFieldType.TEXT,
 | 
				
			||||||
 | 
					            default_value_string="DEFAULT",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        env_vars = ["FOOBAR={{agent.Test Field}}"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # test default value
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            ["FOOBAR=DEFAULT"],
 | 
				
			||||||
 | 
					            Script.parse_script_env_vars(
 | 
				
			||||||
 | 
					                agent=agent, shell=ScriptShell.POWERSHELL, env_vars=env_vars
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # test with set value
 | 
				
			||||||
 | 
					        baker.make(
 | 
				
			||||||
 | 
					            "agents.AgentCustomField",
 | 
				
			||||||
 | 
					            field=field,
 | 
				
			||||||
 | 
					            agent=agent,
 | 
				
			||||||
 | 
					            string_value="CUSTOM VALUE",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            ["FOOBAR=CUSTOM VALUE"],
 | 
				
			||||||
 | 
					            Script.parse_script_env_vars(
 | 
				
			||||||
 | 
					                agent=agent, shell=ScriptShell.POWERSHELL, env_vars=env_vars
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_script_arg_replacement_client_custom_fields(self):
 | 
					    def test_script_arg_replacement_client_custom_fields(self):
 | 
				
			||||||
        agent = baker.make_recipe("agents.agent")
 | 
					        agent = baker.make_recipe("agents.agent")
 | 
				
			||||||
        field = baker.make(
 | 
					        field = baker.make(
 | 
				
			||||||
@@ -302,6 +355,42 @@ class TestScriptViews(TacticalTestCase):
 | 
				
			|||||||
            Script.parse_script_args(agent=agent, shell=ScriptShell.PYTHON, args=args),
 | 
					            Script.parse_script_args(agent=agent, shell=ScriptShell.PYTHON, args=args),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_script_env_vars_replacement_client_custom_fields(self):
 | 
				
			||||||
 | 
					        agent = baker.make_recipe("agents.agent")
 | 
				
			||||||
 | 
					        field = baker.make(
 | 
				
			||||||
 | 
					            "core.CustomField",
 | 
				
			||||||
 | 
					            name="test123",
 | 
				
			||||||
 | 
					            model=CustomFieldModel.CLIENT,
 | 
				
			||||||
 | 
					            type=CustomFieldType.TEXT,
 | 
				
			||||||
 | 
					            default_value_string="https://a1234lkasd.asdinasd234.com/ask2348uASDlk234@!#$@#asd1dsf",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        env_vars = ["FOOBAR={{client.test123}}"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # test default value
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            ["FOOBAR=https://a1234lkasd.asdinasd234.com/ask2348uASDlk234@!#$@#asd1dsf"],
 | 
				
			||||||
 | 
					            Script.parse_script_env_vars(
 | 
				
			||||||
 | 
					                agent=agent, shell=ScriptShell.POWERSHELL, env_vars=env_vars
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # test with set value
 | 
				
			||||||
 | 
					        baker.make(
 | 
				
			||||||
 | 
					            "clients.ClientCustomField",
 | 
				
			||||||
 | 
					            field=field,
 | 
				
			||||||
 | 
					            client=agent.client,
 | 
				
			||||||
 | 
					            string_value="uASdklj23487ASDkjhr345il987UASXK<DFOIul32oi454329837492384512342134!@#!@#ADSFW45X",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                "FOOBAR=uASdklj23487ASDkjhr345il987UASXK<DFOIul32oi454329837492384512342134!@#!@#ADSFW45X"
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            Script.parse_script_env_vars(
 | 
				
			||||||
 | 
					                agent=agent, shell=ScriptShell.POWERSHELL, env_vars=env_vars
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_script_arg_replacement_site_custom_fields(self):
 | 
					    def test_script_arg_replacement_site_custom_fields(self):
 | 
				
			||||||
        agent = baker.make_recipe("agents.agent")
 | 
					        agent = baker.make_recipe("agents.agent")
 | 
				
			||||||
        field = baker.make(
 | 
					        field = baker.make(
 | 
				
			||||||
@@ -350,6 +439,51 @@ class TestScriptViews(TacticalTestCase):
 | 
				
			|||||||
            Script.parse_script_args(agent=agent, shell=ScriptShell.PYTHON, args=args),
 | 
					            Script.parse_script_args(agent=agent, shell=ScriptShell.PYTHON, args=args),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_script_env_vars_replacement_site_custom_fields(self):
 | 
				
			||||||
 | 
					        agent = baker.make_recipe("agents.agent")
 | 
				
			||||||
 | 
					        field = baker.make(
 | 
				
			||||||
 | 
					            "core.CustomField",
 | 
				
			||||||
 | 
					            name="ffas2345asdasasdWEdd",
 | 
				
			||||||
 | 
					            model=CustomFieldModel.SITE,
 | 
				
			||||||
 | 
					            type=CustomFieldType.TEXT,
 | 
				
			||||||
 | 
					            default_value_string="https://site.easkdjas.com/asik2348aSDH234RJKADBCA%123SAD",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        env_vars = ["ASD45ASDKJASHD={{site.ffas2345asdasasdWEdd}}"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # test default value
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            ["ASD45ASDKJASHD=https://site.easkdjas.com/asik2348aSDH234RJKADBCA%123SAD"],
 | 
				
			||||||
 | 
					            Script.parse_script_env_vars(
 | 
				
			||||||
 | 
					                agent=agent, shell=ScriptShell.POWERSHELL, env_vars=env_vars
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # test with set value
 | 
				
			||||||
 | 
					        value = baker.make(
 | 
				
			||||||
 | 
					            "clients.SiteCustomField",
 | 
				
			||||||
 | 
					            field=field,
 | 
				
			||||||
 | 
					            site=agent.site,
 | 
				
			||||||
 | 
					            string_value="g435asdASD2354SDFasdfsdf",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            ["ASD45ASDKJASHD=g435asdASD2354SDFasdfsdf"],
 | 
				
			||||||
 | 
					            Script.parse_script_env_vars(
 | 
				
			||||||
 | 
					                agent=agent, shell=ScriptShell.POWERSHELL, env_vars=env_vars
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # test with set but empty field value
 | 
				
			||||||
 | 
					        value.string_value = ""
 | 
				
			||||||
 | 
					        value.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            ["ASD45ASDKJASHD=https://site.easkdjas.com/asik2348aSDH234RJKADBCA%123SAD"],
 | 
				
			||||||
 | 
					            Script.parse_script_env_vars(
 | 
				
			||||||
 | 
					                agent=agent, shell=ScriptShell.POWERSHELL, env_vars=env_vars
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_script_arg_replacement_array_fields(self):
 | 
					    def test_script_arg_replacement_array_fields(self):
 | 
				
			||||||
        agent = baker.make_recipe("agents.agent")
 | 
					        agent = baker.make_recipe("agents.agent")
 | 
				
			||||||
        field = baker.make(
 | 
					        field = baker.make(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -148,6 +148,9 @@ class TestScript(APIView):
 | 
				
			|||||||
        parsed_args = Script.parse_script_args(
 | 
					        parsed_args = Script.parse_script_args(
 | 
				
			||||||
            agent, request.data["shell"], request.data["args"]
 | 
					            agent, request.data["shell"], request.data["args"]
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        parsed_env_vars = Script.parse_script_env_vars(
 | 
				
			||||||
 | 
					            agent, request.data["shell"], request.data["env_vars"]
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        data = {
 | 
					        data = {
 | 
				
			||||||
            "func": "runscript",
 | 
					            "func": "runscript",
 | 
				
			||||||
@@ -158,7 +161,7 @@ class TestScript(APIView):
 | 
				
			|||||||
                "shell": request.data["shell"],
 | 
					                "shell": request.data["shell"],
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "run_as_user": request.data["run_as_user"],
 | 
					            "run_as_user": request.data["run_as_user"],
 | 
				
			||||||
            "env_vars": request.data["env_vars"],
 | 
					            "env_vars": parsed_env_vars,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        r = asyncio.run(
 | 
					        r = asyncio.run(
 | 
				
			||||||
 
 | 
				
			|||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -42,6 +42,13 @@ def get_nats_ports() -> tuple[int, int]:
 | 
				
			|||||||
    return nats_standard_port, nats_websocket_port
 | 
					    return nats_standard_port, nats_websocket_port
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_nats_internal_protocol() -> str:
 | 
				
			||||||
 | 
					    if getattr(settings, "TRMM_INSECURE", False):
 | 
				
			||||||
 | 
					        return "nats"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return "tls"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def date_is_in_past(*, datetime_obj: "datetime", agent_tz: str) -> bool:
 | 
					def date_is_in_past(*, datetime_obj: "datetime", agent_tz: str) -> bool:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    datetime_obj must be a naive datetime
 | 
					    datetime_obj must be a naive datetime
 | 
				
			||||||
@@ -66,8 +73,9 @@ def rand_range(min: int, max: int) -> float:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def setup_nats_options() -> dict[str, Any]:
 | 
					def setup_nats_options() -> dict[str, Any]:
 | 
				
			||||||
    nats_std_port, _ = get_nats_ports()
 | 
					    nats_std_port, _ = get_nats_ports()
 | 
				
			||||||
 | 
					    proto = get_nats_internal_protocol()
 | 
				
			||||||
    opts = {
 | 
					    opts = {
 | 
				
			||||||
        "servers": f"tls://{settings.ALLOWED_HOSTS[0]}:{nats_std_port}",
 | 
					        "servers": f"{proto}://{settings.ALLOWED_HOSTS[0]}:{nats_std_port}",
 | 
				
			||||||
        "user": "tacticalrmm",
 | 
					        "user": "tacticalrmm",
 | 
				
			||||||
        "name": "trmm-django",
 | 
					        "name": "trmm-django",
 | 
				
			||||||
        "password": settings.SECRET_KEY,
 | 
					        "password": settings.SECRET_KEY,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,21 +20,21 @@ MAC_UNINSTALL = BASE_DIR / "core" / "mac_uninstall.sh"
 | 
				
			|||||||
AUTH_USER_MODEL = "accounts.User"
 | 
					AUTH_USER_MODEL = "accounts.User"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# latest release
 | 
					# latest release
 | 
				
			||||||
TRMM_VERSION = "0.16.2"
 | 
					TRMM_VERSION = "0.16.5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# https://github.com/amidaware/tacticalrmm-web
 | 
					# https://github.com/amidaware/tacticalrmm-web
 | 
				
			||||||
WEB_VERSION = "0.101.28"
 | 
					WEB_VERSION = "0.101.31"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# bump this version everytime vue code is changed
 | 
					# bump this version everytime vue code is changed
 | 
				
			||||||
# to alert user they need to manually refresh their browser
 | 
					# to alert user they need to manually refresh their browser
 | 
				
			||||||
APP_VER = "0.0.183"
 | 
					APP_VER = "0.0.185"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# https://github.com/amidaware/rmmagent
 | 
					# https://github.com/amidaware/rmmagent
 | 
				
			||||||
LATEST_AGENT_VER = "2.4.11"
 | 
					LATEST_AGENT_VER = "2.5.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MESH_VER = "1.1.9"
 | 
					MESH_VER = "1.1.9"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
NATS_SERVER_VER = "2.9.21"
 | 
					NATS_SERVER_VER = "2.10.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# for the update script, bump when need to recreate venv
 | 
					# for the update script, bump when need to recreate venv
 | 
				
			||||||
PIP_VER = "38"
 | 
					PIP_VER = "38"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,6 +40,9 @@ urlpatterns = [
 | 
				
			|||||||
    path("accounts/", include("accounts.urls")),
 | 
					    path("accounts/", include("accounts.urls")),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if getattr(settings, "BETA_API_ENABLED", False):
 | 
				
			||||||
 | 
					    urlpatterns += (path("beta/v1/", include("beta.v1.urls")),)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if getattr(settings, "ADMIN_ENABLED", False):
 | 
					if getattr(settings, "ADMIN_ENABLED", False):
 | 
				
			||||||
    from django.contrib import admin
 | 
					    from django.contrib import admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,12 @@ from tacticalrmm.constants import (
 | 
				
			|||||||
    DebugLogType,
 | 
					    DebugLogType,
 | 
				
			||||||
    ScriptShell,
 | 
					    ScriptShell,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from tacticalrmm.helpers import get_certs, get_nats_ports, notify_error
 | 
					from tacticalrmm.helpers import (
 | 
				
			||||||
 | 
					    get_certs,
 | 
				
			||||||
 | 
					    get_nats_internal_protocol,
 | 
				
			||||||
 | 
					    get_nats_ports,
 | 
				
			||||||
 | 
					    notify_error,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def generate_winagent_exe(
 | 
					def generate_winagent_exe(
 | 
				
			||||||
@@ -204,10 +209,6 @@ def reload_nats() -> None:
 | 
				
			|||||||
    nats_std_port, nats_ws_port = get_nats_ports()
 | 
					    nats_std_port, nats_ws_port = get_nats_ports()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    config = {
 | 
					    config = {
 | 
				
			||||||
        "tls": {
 | 
					 | 
				
			||||||
            "cert_file": cert_file,
 | 
					 | 
				
			||||||
            "key_file": key_file,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "authorization": {"users": users},
 | 
					        "authorization": {"users": users},
 | 
				
			||||||
        "max_payload": 67108864,
 | 
					        "max_payload": 67108864,
 | 
				
			||||||
        "port": nats_std_port,  # internal only
 | 
					        "port": nats_std_port,  # internal only
 | 
				
			||||||
@@ -217,6 +218,12 @@ def reload_nats() -> None:
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if get_nats_internal_protocol() == "tls":
 | 
				
			||||||
 | 
					        config["tls"] = {
 | 
				
			||||||
 | 
					            "cert_file": cert_file,
 | 
				
			||||||
 | 
					            "key_file": key_file,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if "NATS_HTTP_PORT" in os.environ:
 | 
					    if "NATS_HTTP_PORT" in os.environ:
 | 
				
			||||||
        config["http_port"] = int(os.getenv("NATS_HTTP_PORT"))  # type: ignore
 | 
					        config["http_port"] = int(os.getenv("NATS_HTTP_PORT"))  # type: ignore
 | 
				
			||||||
    elif hasattr(settings, "NATS_HTTP_PORT"):
 | 
					    elif hasattr(settings, "NATS_HTTP_PORT"):
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								backup.sh
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								backup.sh
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
#!/usr/bin/env bash
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SCRIPT_VERSION="28"
 | 
					SCRIPT_VERSION="30"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GREEN='\033[0;32m'
 | 
					GREEN='\033[0;32m'
 | 
				
			||||||
YELLOW='\033[1;33m'
 | 
					YELLOW='\033[1;33m'
 | 
				
			||||||
@@ -72,7 +72,7 @@ mkdir ${tmp_dir}/confd
 | 
				
			|||||||
POSTGRES_USER=$(/rmm/api/env/bin/python /rmm/api/tacticalrmm/manage.py get_config dbuser)
 | 
					POSTGRES_USER=$(/rmm/api/env/bin/python /rmm/api/tacticalrmm/manage.py get_config dbuser)
 | 
				
			||||||
POSTGRES_PW=$(/rmm/api/env/bin/python /rmm/api/tacticalrmm/manage.py get_config dbpw)
 | 
					POSTGRES_PW=$(/rmm/api/env/bin/python /rmm/api/tacticalrmm/manage.py get_config dbpw)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pg_dump --dbname=postgresql://"${POSTGRES_USER}":"${POSTGRES_PW}"@127.0.0.1:5432/tacticalrmm | gzip -9 >${tmp_dir}/postgres/db-${dt_now}.psql.gz
 | 
					pg_dump --dbname=postgresql://"${POSTGRES_USER}":"${POSTGRES_PW}"@localhost:5432/tacticalrmm | gzip -9 >${tmp_dir}/postgres/db-${dt_now}.psql.gz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
node /meshcentral/node_modules/meshcentral --dbexport # for import to postgres
 | 
					node /meshcentral/node_modules/meshcentral --dbexport # for import to postgres
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -82,7 +82,7 @@ if grep -q postgres "/meshcentral/meshcentral-data/config.json"; then
 | 
				
			|||||||
    fi
 | 
					    fi
 | 
				
			||||||
    MESH_POSTGRES_USER=$(jq '.settings.postgres.user' /meshcentral/meshcentral-data/config.json -r)
 | 
					    MESH_POSTGRES_USER=$(jq '.settings.postgres.user' /meshcentral/meshcentral-data/config.json -r)
 | 
				
			||||||
    MESH_POSTGRES_PW=$(jq '.settings.postgres.password' /meshcentral/meshcentral-data/config.json -r)
 | 
					    MESH_POSTGRES_PW=$(jq '.settings.postgres.password' /meshcentral/meshcentral-data/config.json -r)
 | 
				
			||||||
    pg_dump --dbname=postgresql://"${MESH_POSTGRES_USER}":"${MESH_POSTGRES_PW}"@127.0.0.1:5432/meshcentral | gzip -9 >${tmp_dir}/postgres/mesh-db-${dt_now}.psql.gz
 | 
					    pg_dump --dbname=postgresql://"${MESH_POSTGRES_USER}":"${MESH_POSTGRES_PW}"@localhost:5432/meshcentral | gzip -9 >${tmp_dir}/postgres/mesh-db-${dt_now}.psql.gz
 | 
				
			||||||
else
 | 
					else
 | 
				
			||||||
    mongodump --gzip --out=${tmp_dir}/meshcentral/mongo
 | 
					    mongodump --gzip --out=${tmp_dir}/meshcentral/mongo
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
@@ -101,6 +101,11 @@ if grep -q CERT_FILE "$local_settings"; then
 | 
				
			|||||||
    KEY_FILE=$(grep "^KEY_FILE" "$local_settings" | awk -F'[= "]' '{print $5}')
 | 
					    KEY_FILE=$(grep "^KEY_FILE" "$local_settings" | awk -F'[= "]' '{print $5}')
 | 
				
			||||||
    cp -p $CERT_FILE ${tmp_dir}/certs/custom/cert
 | 
					    cp -p $CERT_FILE ${tmp_dir}/certs/custom/cert
 | 
				
			||||||
    cp -p $KEY_FILE ${tmp_dir}/certs/custom/key
 | 
					    cp -p $KEY_FILE ${tmp_dir}/certs/custom/key
 | 
				
			||||||
 | 
					elif grep -q TRMM_INSECURE "$local_settings"; then
 | 
				
			||||||
 | 
					    mkdir -p ${tmp_dir}/certs/selfsigned
 | 
				
			||||||
 | 
					    certdir='/etc/ssl/tactical'
 | 
				
			||||||
 | 
					    cp -p ${certdir}/key.pem ${tmp_dir}/certs/selfsigned/
 | 
				
			||||||
 | 
					    cp -p ${certdir}/cert.pem ${tmp_dir}/certs/selfsigned/
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
for i in rmm frontend meshcentral; do
 | 
					for i in rmm frontend meshcentral; do
 | 
				
			||||||
@@ -138,6 +143,7 @@ if [[ $* == *--auto* ]]; then
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
else
 | 
					else
 | 
				
			||||||
    tar -cf /rmmbackups/rmm-backup-${dt_now}.tar -C ${tmp_dir} .
 | 
					    tar -cf /rmmbackups/rmm-backup-${dt_now}.tar -C ${tmp_dir} .
 | 
				
			||||||
 | 
					    rm -rf ${tmp_dir}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    echo -ne "${GREEN}Backup saved to /rmmbackups/rmm-backup-${dt_now}.tar${NC}\n"
 | 
					    echo -ne "${GREEN}Backup saved to /rmmbackups/rmm-backup-${dt_now}.tar${NC}\n"
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,22 @@ SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
COPY api/tacticalrmm/tacticalrmm/settings.py /tmp/settings.py
 | 
					COPY api/tacticalrmm/tacticalrmm/settings.py /tmp/settings.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN npm install meshcentral@$(grep -o 'MESH_VER.*' /tmp/settings.py | cut -d'"' -f 2)
 | 
					RUN MESH_VER=$(grep -o 'MESH_VER.*' /tmp/settings.py | cut -d'"' -f 2) && \
 | 
				
			||||||
 | 
					    cat > package.json <<EOF
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "archiver": "5.3.1",
 | 
				
			||||||
 | 
					    "meshcentral": "$MESH_VER",
 | 
				
			||||||
 | 
					    "mongodb": "4.13.0",
 | 
				
			||||||
 | 
					    "otplib": "10.2.3",
 | 
				
			||||||
 | 
					    "pg": "8.7.1",
 | 
				
			||||||
 | 
					    "pgtools": "0.3.2",
 | 
				
			||||||
 | 
					    "saslprep": "1.0.3"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN npm install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN chown -R node:node /home/node
 | 
					RUN chown -R node:node /home/node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
FROM nats:2.9.20-alpine
 | 
					FROM nats:2.10.1-alpine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ENV TACTICAL_DIR /opt/tactical
 | 
					ENV TACTICAL_DIR /opt/tactical
 | 
				
			||||||
ENV TACTICAL_READY_FILE ${TACTICAL_DIR}/tmp/tactical.ready
 | 
					ENV TACTICAL_READY_FILE ${TACTICAL_DIR}/tmp/tactical.ready
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										78
									
								
								install.sh
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								install.sh
									
									
									
									
									
								
							@@ -1,9 +1,9 @@
 | 
				
			|||||||
#!/usr/bin/env bash
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SCRIPT_VERSION="75"
 | 
					SCRIPT_VERSION="78"
 | 
				
			||||||
SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/install.sh'
 | 
					SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/install.sh'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sudo apt install -y curl wget dirmngr gnupg lsb-release
 | 
					sudo apt install -y curl wget dirmngr gnupg lsb-release ca-certificates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GREEN='\033[0;32m'
 | 
					GREEN='\033[0;32m'
 | 
				
			||||||
YELLOW='\033[1;33m'
 | 
					YELLOW='\033[1;33m'
 | 
				
			||||||
@@ -14,6 +14,7 @@ NC='\033[0m'
 | 
				
			|||||||
SCRIPTS_DIR='/opt/trmm-community-scripts'
 | 
					SCRIPTS_DIR='/opt/trmm-community-scripts'
 | 
				
			||||||
PYTHON_VER='3.11.4'
 | 
					PYTHON_VER='3.11.4'
 | 
				
			||||||
SETTINGS_FILE='/rmm/api/tacticalrmm/tacticalrmm/settings.py'
 | 
					SETTINGS_FILE='/rmm/api/tacticalrmm/tacticalrmm/settings.py'
 | 
				
			||||||
 | 
					local_settings='/rmm/api/tacticalrmm/tacticalrmm/local_settings.py'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TMP_FILE=$(mktemp -p "" "rmminstall_XXXXXXXXXX")
 | 
					TMP_FILE=$(mktemp -p "" "rmminstall_XXXXXXXXXX")
 | 
				
			||||||
curl -s -L "${SCRIPT_URL}" >${TMP_FILE}
 | 
					curl -s -L "${SCRIPT_URL}" >${TMP_FILE}
 | 
				
			||||||
@@ -86,7 +87,7 @@ if [ "$arch" = "x86_64" ]; then
 | 
				
			|||||||
else
 | 
					else
 | 
				
			||||||
  pgarch='arm64'
 | 
					  pgarch='arm64'
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
postgresql_repo="deb [arch=${pgarch}] https://apt.postgresql.org/pub/repos/apt/ $codename-pgdg main"
 | 
					postgresql_repo="deb [arch=${pgarch} signed-by=/etc/apt/keyrings/postgresql-archive-keyring.gpg] https://apt.postgresql.org/pub/repos/apt/ $codename-pgdg main"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# prevents logging issues with some VPS providers like Vultr if this is a freshly provisioned instance that hasn't been rebooted yet
 | 
					# prevents logging issues with some VPS providers like Vultr if this is a freshly provisioned instance that hasn't been rebooted yet
 | 
				
			||||||
sudo systemctl restart systemd-journald.service
 | 
					sudo systemctl restart systemd-journald.service
 | 
				
			||||||
@@ -161,30 +162,50 @@ if echo "$IPV4" | grep -qE '^(10\.|172\.1[6789]\.|172\.2[0-9]\.|172\.3[01]\.|192
 | 
				
			|||||||
  BEHIND_NAT=true
 | 
					  BEHIND_NAT=true
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					insecure=false
 | 
				
			||||||
 | 
					if [[ $* == *--insecure* ]]; then
 | 
				
			||||||
 | 
					  insecure=true
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sudo apt install -y software-properties-common
 | 
					sudo apt install -y software-properties-common
 | 
				
			||||||
sudo apt update
 | 
					sudo apt update
 | 
				
			||||||
sudo apt install -y certbot openssl
 | 
					sudo apt install -y openssl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [[ "$insecure" = true ]]; then
 | 
				
			||||||
 | 
					  print_green 'Generating self-signed cert'
 | 
				
			||||||
 | 
					  certdir='/etc/ssl/tactical'
 | 
				
			||||||
 | 
					  sudo mkdir -p $certdir
 | 
				
			||||||
 | 
					  sudo chown ${USER}:${USER} $certdir
 | 
				
			||||||
 | 
					  sudo chmod 770 $certdir
 | 
				
			||||||
 | 
					  CERT_PRIV_KEY=${certdir}/key.pem
 | 
				
			||||||
 | 
					  CERT_PUB_KEY=${certdir}/cert.pem
 | 
				
			||||||
 | 
					  openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 \
 | 
				
			||||||
 | 
					    -nodes -keyout ${CERT_PRIV_KEY} -out ${CERT_PUB_KEY} -subj "/CN=${rootdomain}" \
 | 
				
			||||||
 | 
					    -addext "subjectAltName=DNS:${rootdomain},DNS:*.${rootdomain}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					  sudo apt install -y certbot
 | 
				
			||||||
  print_green 'Getting wildcard cert'
 | 
					  print_green 'Getting wildcard cert'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sudo certbot certonly --manual -d *.${rootdomain} --agree-tos --no-bootstrap --preferred-challenges dns -m ${letsemail} --no-eff-email
 | 
					  sudo certbot certonly --manual -d *.${rootdomain} --agree-tos --no-bootstrap --preferred-challenges dns -m ${letsemail} --no-eff-email
 | 
				
			||||||
  while [[ $? -ne 0 ]]; do
 | 
					  while [[ $? -ne 0 ]]; do
 | 
				
			||||||
    sudo certbot certonly --manual -d *.${rootdomain} --agree-tos --no-bootstrap --preferred-challenges dns -m ${letsemail} --no-eff-email
 | 
					    sudo certbot certonly --manual -d *.${rootdomain} --agree-tos --no-bootstrap --preferred-challenges dns -m ${letsemail} --no-eff-email
 | 
				
			||||||
  done
 | 
					  done
 | 
				
			||||||
 | 
					 | 
				
			||||||
  CERT_PRIV_KEY=/etc/letsencrypt/live/${rootdomain}/privkey.pem
 | 
					  CERT_PRIV_KEY=/etc/letsencrypt/live/${rootdomain}/privkey.pem
 | 
				
			||||||
  CERT_PUB_KEY=/etc/letsencrypt/live/${rootdomain}/fullchain.pem
 | 
					  CERT_PUB_KEY=/etc/letsencrypt/live/${rootdomain}/fullchain.pem
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sudo chown ${USER}:${USER} -R /etc/letsencrypt
 | 
					sudo chown ${USER}:${USER} -R /etc/letsencrypt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
print_green 'Installing Nginx'
 | 
					print_green 'Installing Nginx'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wget -qO - https://nginx.org/packages/keys/nginx_signing.key | sudo apt-key add -
 | 
					sudo mkdir -p /etc/apt/keyrings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wget -qO - https://nginx.org/packages/keys/nginx_signing.key | sudo gpg --dearmor -o /etc/apt/keyrings/nginx-archive-keyring.gpg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
nginxrepo="$(
 | 
					nginxrepo="$(
 | 
				
			||||||
  cat <<EOF
 | 
					  cat <<EOF
 | 
				
			||||||
deb https://nginx.org/packages/$osname/ $codename nginx
 | 
					deb [signed-by=/etc/apt/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/$osname $codename nginx
 | 
				
			||||||
deb-src https://nginx.org/packages/$osname/ $codename nginx
 | 
					 | 
				
			||||||
EOF
 | 
					EOF
 | 
				
			||||||
)"
 | 
					)"
 | 
				
			||||||
echo "${nginxrepo}" | sudo tee /etc/apt/sources.list.d/nginx.list >/dev/null
 | 
					echo "${nginxrepo}" | sudo tee /etc/apt/sources.list.d/nginx.list >/dev/null
 | 
				
			||||||
@@ -232,7 +253,9 @@ done
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
print_green 'Installing NodeJS'
 | 
					print_green 'Installing NodeJS'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash -
 | 
					curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
 | 
				
			||||||
 | 
					NODE_MAJOR=18
 | 
				
			||||||
 | 
					echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
 | 
				
			||||||
sudo apt update
 | 
					sudo apt update
 | 
				
			||||||
sudo apt install -y gcc g++ make
 | 
					sudo apt install -y gcc g++ make
 | 
				
			||||||
sudo apt install -y nodejs
 | 
					sudo apt install -y nodejs
 | 
				
			||||||
@@ -253,13 +276,13 @@ cd ~
 | 
				
			|||||||
sudo rm -rf Python-${PYTHON_VER} Python-${PYTHON_VER}.tgz
 | 
					sudo rm -rf Python-${PYTHON_VER} Python-${PYTHON_VER}.tgz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
print_green 'Installing redis and git'
 | 
					print_green 'Installing redis and git'
 | 
				
			||||||
sudo apt install -y ca-certificates redis git
 | 
					sudo apt install -y redis git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
print_green 'Installing postgresql'
 | 
					print_green 'Installing postgresql'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo "$postgresql_repo" | sudo tee /etc/apt/sources.list.d/pgdg.list
 | 
					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 -
 | 
					wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo gpg --dearmor -o /etc/apt/keyrings/postgresql-archive-keyring.gpg
 | 
				
			||||||
sudo apt update
 | 
					sudo apt update
 | 
				
			||||||
sudo apt install -y postgresql-15
 | 
					sudo apt install -y postgresql-15
 | 
				
			||||||
sleep 2
 | 
					sleep 2
 | 
				
			||||||
@@ -336,9 +359,23 @@ MESH_VER=$(grep "^MESH_VER" "$SETTINGS_FILE" | awk -F'[= "]' '{print $5}')
 | 
				
			|||||||
sudo mkdir -p /meshcentral/meshcentral-data
 | 
					sudo mkdir -p /meshcentral/meshcentral-data
 | 
				
			||||||
sudo chown ${USER}:${USER} -R /meshcentral
 | 
					sudo chown ${USER}:${USER} -R /meshcentral
 | 
				
			||||||
cd /meshcentral
 | 
					cd /meshcentral
 | 
				
			||||||
npm install meshcentral@${MESH_VER}
 | 
					 | 
				
			||||||
sudo chown ${USER}:${USER} -R /meshcentral
 | 
					sudo chown ${USER}:${USER} -R /meshcentral
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mesh_pkg="$(
 | 
				
			||||||
 | 
					  cat <<EOF
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "archiver": "5.3.1",
 | 
				
			||||||
 | 
					    "meshcentral": "${MESH_VER}",
 | 
				
			||||||
 | 
					    "otplib": "10.2.3",
 | 
				
			||||||
 | 
					    "pg": "8.7.1",
 | 
				
			||||||
 | 
					    "pgtools": "0.3.2"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					)"
 | 
				
			||||||
 | 
					echo "${mesh_pkg}" >/meshcentral/package.json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
meshcfg="$(
 | 
					meshcfg="$(
 | 
				
			||||||
  cat <<EOF
 | 
					  cat <<EOF
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -382,6 +419,8 @@ EOF
 | 
				
			|||||||
)"
 | 
					)"
 | 
				
			||||||
echo "${meshcfg}" >/meshcentral/meshcentral-data/config.json
 | 
					echo "${meshcfg}" >/meshcentral/meshcentral-data/config.json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					npm install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
localvars="$(
 | 
					localvars="$(
 | 
				
			||||||
  cat <<EOF
 | 
					  cat <<EOF
 | 
				
			||||||
SECRET_KEY = "${DJANGO_SEKRET}"
 | 
					SECRET_KEY = "${DJANGO_SEKRET}"
 | 
				
			||||||
@@ -413,7 +452,11 @@ REDIS_HOST    = "localhost"
 | 
				
			|||||||
ADMIN_ENABLED = True
 | 
					ADMIN_ENABLED = True
 | 
				
			||||||
EOF
 | 
					EOF
 | 
				
			||||||
)"
 | 
					)"
 | 
				
			||||||
echo "${localvars}" >/rmm/api/tacticalrmm/tacticalrmm/local_settings.py
 | 
					echo "${localvars}" >$local_settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [[ "$insecure" = true ]]; then
 | 
				
			||||||
 | 
					  echo "TRMM_INSECURE = True" | tee --append $local_settings >/dev/null
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [ "$arch" = "x86_64" ]; then
 | 
					if [ "$arch" = "x86_64" ]; then
 | 
				
			||||||
  natsapi='nats-api'
 | 
					  natsapi='nats-api'
 | 
				
			||||||
@@ -446,7 +489,7 @@ python manage.py load_community_scripts
 | 
				
			|||||||
WEB_VERSION=$(python manage.py get_config webversion)
 | 
					WEB_VERSION=$(python manage.py get_config webversion)
 | 
				
			||||||
printf >&2 "${YELLOW}%0.s*${NC}" {1..80}
 | 
					printf >&2 "${YELLOW}%0.s*${NC}" {1..80}
 | 
				
			||||||
printf >&2 "\n"
 | 
					printf >&2 "\n"
 | 
				
			||||||
printf >&2 "${YELLOW}Please create your login for the RMM website and django admin${NC}\n"
 | 
					printf >&2 "${YELLOW}Please create your login for the RMM website${NC}\n"
 | 
				
			||||||
printf >&2 "${YELLOW}%0.s*${NC}" {1..80}
 | 
					printf >&2 "${YELLOW}%0.s*${NC}" {1..80}
 | 
				
			||||||
printf >&2 "\n"
 | 
					printf >&2 "\n"
 | 
				
			||||||
echo -ne "Username: "
 | 
					echo -ne "Username: "
 | 
				
			||||||
@@ -856,7 +899,7 @@ done
 | 
				
			|||||||
sleep 5
 | 
					sleep 5
 | 
				
			||||||
sudo systemctl enable meshcentral
 | 
					sudo systemctl enable meshcentral
 | 
				
			||||||
 | 
					
 | 
				
			||||||
print_green 'Starting meshcentral and waiting for it to install plugins'
 | 
					print_green 'Starting meshcentral and waiting for it to be ready'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sudo systemctl restart meshcentral
 | 
					sudo systemctl restart meshcentral
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -880,7 +923,7 @@ meshtoken="$(
 | 
				
			|||||||
MESH_TOKEN_KEY = "${MESHTOKENKEY}"
 | 
					MESH_TOKEN_KEY = "${MESHTOKENKEY}"
 | 
				
			||||||
EOF
 | 
					EOF
 | 
				
			||||||
)"
 | 
					)"
 | 
				
			||||||
echo "${meshtoken}" | tee --append /rmm/api/tacticalrmm/tacticalrmm/local_settings.py >/dev/null
 | 
					echo "${meshtoken}" | tee --append $local_settings >/dev/null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
print_green 'Creating meshcentral account and group'
 | 
					print_green 'Creating meshcentral account and group'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -917,7 +960,7 @@ sudo systemctl enable nats-api.service
 | 
				
			|||||||
sudo systemctl start nats-api.service
 | 
					sudo systemctl start nats-api.service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## disable django admin
 | 
					## disable django admin
 | 
				
			||||||
sed -i 's/ADMIN_ENABLED = True/ADMIN_ENABLED = False/g' /rmm/api/tacticalrmm/tacticalrmm/local_settings.py
 | 
					sed -i 's/ADMIN_ENABLED = True/ADMIN_ENABLED = False/g' $local_settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
print_green 'Restarting services'
 | 
					print_green 'Restarting services'
 | 
				
			||||||
for i in rmm.service daphne.service celery.service celerybeat.service; do
 | 
					for i in rmm.service daphne.service celery.service celerybeat.service; do
 | 
				
			||||||
@@ -929,7 +972,6 @@ printf >&2 "${YELLOW}%0.s*${NC}" {1..80}
 | 
				
			|||||||
printf >&2 "\n\n"
 | 
					printf >&2 "\n\n"
 | 
				
			||||||
printf >&2 "${YELLOW}Installation complete!${NC}\n\n"
 | 
					printf >&2 "${YELLOW}Installation complete!${NC}\n\n"
 | 
				
			||||||
printf >&2 "${YELLOW}Access your rmm at: ${GREEN}https://${frontenddomain}${NC}\n\n"
 | 
					printf >&2 "${YELLOW}Access your rmm at: ${GREEN}https://${frontenddomain}${NC}\n\n"
 | 
				
			||||||
printf >&2 "${YELLOW}Django admin url (disabled by default): ${GREEN}https://${rmmdomain}/${ADMINURL}/${NC}\n\n"
 | 
					 | 
				
			||||||
printf >&2 "${YELLOW}MeshCentral username: ${GREEN}${meshusername}${NC}\n"
 | 
					printf >&2 "${YELLOW}MeshCentral username: ${GREEN}${meshusername}${NC}\n"
 | 
				
			||||||
printf >&2 "${YELLOW}MeshCentral password: ${GREEN}${MESHPASSWD}${NC}\n\n"
 | 
					printf >&2 "${YELLOW}MeshCentral password: ${GREEN}${MESHPASSWD}${NC}\n\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										44
									
								
								restore.sh
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								restore.sh
									
									
									
									
									
								
							@@ -1,10 +1,10 @@
 | 
				
			|||||||
#!/usr/bin/env bash
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SCRIPT_VERSION="50"
 | 
					SCRIPT_VERSION="53"
 | 
				
			||||||
SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/restore.sh'
 | 
					SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/restore.sh'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sudo apt update
 | 
					sudo apt update
 | 
				
			||||||
sudo apt install -y curl wget dirmngr gnupg lsb-release
 | 
					sudo apt install -y curl wget dirmngr gnupg lsb-release ca-certificates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GREEN='\033[0;32m'
 | 
					GREEN='\033[0;32m'
 | 
				
			||||||
YELLOW='\033[1;33m'
 | 
					YELLOW='\033[1;33m'
 | 
				
			||||||
@@ -86,7 +86,7 @@ if [ "$arch" = "x86_64" ]; then
 | 
				
			|||||||
else
 | 
					else
 | 
				
			||||||
  pgarch='arm64'
 | 
					  pgarch='arm64'
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
postgresql_repo="deb [arch=${pgarch}] https://apt.postgresql.org/pub/repos/apt/ $codename-pgdg main"
 | 
					postgresql_repo="deb [arch=${pgarch} signed-by=/etc/apt/keyrings/postgresql-archive-keyring.gpg] https://apt.postgresql.org/pub/repos/apt/ $codename-pgdg main"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [ ! -f "${1}" ]; then
 | 
					if [ ! -f "${1}" ]; then
 | 
				
			||||||
  echo -ne "\n${RED}usage: ./restore.sh rmm-backup-xxxx.tar${NC}\n"
 | 
					  echo -ne "\n${RED}usage: ./restore.sh rmm-backup-xxxx.tar${NC}\n"
 | 
				
			||||||
@@ -122,7 +122,10 @@ sudo apt update
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
print_green 'Installing NodeJS'
 | 
					print_green 'Installing NodeJS'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash -
 | 
					sudo mkdir -p /etc/apt/keyrings
 | 
				
			||||||
 | 
					curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
 | 
				
			||||||
 | 
					NODE_MAJOR=18
 | 
				
			||||||
 | 
					echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
 | 
				
			||||||
sudo apt update
 | 
					sudo apt update
 | 
				
			||||||
sudo apt install -y gcc g++ make
 | 
					sudo apt install -y gcc g++ make
 | 
				
			||||||
sudo apt install -y nodejs
 | 
					sudo apt install -y nodejs
 | 
				
			||||||
@@ -130,12 +133,11 @@ sudo npm install -g npm
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
print_green 'Restoring Nginx'
 | 
					print_green 'Restoring Nginx'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wget -qO - https://nginx.org/packages/keys/nginx_signing.key | sudo apt-key add -
 | 
					wget -qO - https://nginx.org/packages/keys/nginx_signing.key | sudo gpg --dearmor -o /etc/apt/keyrings/nginx-archive-keyring.gpg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
nginxrepo="$(
 | 
					nginxrepo="$(
 | 
				
			||||||
  cat <<EOF
 | 
					  cat <<EOF
 | 
				
			||||||
deb https://nginx.org/packages/$osname/ $codename nginx
 | 
					deb [signed-by=/etc/apt/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/$osname $codename nginx
 | 
				
			||||||
deb-src https://nginx.org/packages/$osname/ $codename nginx
 | 
					 | 
				
			||||||
EOF
 | 
					EOF
 | 
				
			||||||
)"
 | 
					)"
 | 
				
			||||||
echo "${nginxrepo}" | sudo tee /etc/apt/sources.list.d/nginx.list >/dev/null
 | 
					echo "${nginxrepo}" | sudo tee /etc/apt/sources.list.d/nginx.list >/dev/null
 | 
				
			||||||
@@ -209,7 +211,13 @@ if [ -d "${tmp_dir}/certs/custom" ]; then
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  cp -p ${tmp_dir}/certs/custom/cert $CERT_FILE
 | 
					  cp -p ${tmp_dir}/certs/custom/cert $CERT_FILE
 | 
				
			||||||
  cp -p ${tmp_dir}/certs/custom/key $KEY_FILE
 | 
					  cp -p ${tmp_dir}/certs/custom/key $KEY_FILE
 | 
				
			||||||
 | 
					elif [ -d "${tmp_dir}/certs/selfsigned" ]; then
 | 
				
			||||||
 | 
					  certdir='/etc/ssl/tactical'
 | 
				
			||||||
 | 
					  sudo mkdir -p $certdir
 | 
				
			||||||
 | 
					  sudo chown ${USER}:${USER} $certdir
 | 
				
			||||||
 | 
					  sudo chmod 770 $certdir
 | 
				
			||||||
 | 
					  cp -p ${tmp_dir}/certs/selfsigned/key.pem $certdir
 | 
				
			||||||
 | 
					  cp -p ${tmp_dir}/certs/selfsigned/cert.pem $certdir
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
print_green 'Restoring celery configs'
 | 
					print_green 'Restoring celery configs'
 | 
				
			||||||
@@ -238,12 +246,12 @@ cd ~
 | 
				
			|||||||
sudo rm -rf Python-${PYTHON_VER} Python-${PYTHON_VER}.tgz
 | 
					sudo rm -rf Python-${PYTHON_VER} Python-${PYTHON_VER}.tgz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
print_green 'Installing redis and git'
 | 
					print_green 'Installing redis and git'
 | 
				
			||||||
sudo apt install -y ca-certificates redis git
 | 
					sudo apt install -y redis git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
print_green 'Installing postgresql'
 | 
					print_green 'Installing postgresql'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo "$postgresql_repo" | sudo tee /etc/apt/sources.list.d/pgdg.list
 | 
					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 -
 | 
					wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo gpg --dearmor -o /etc/apt/keyrings/postgresql-archive-keyring.gpg
 | 
				
			||||||
sudo apt update
 | 
					sudo apt update
 | 
				
			||||||
sudo apt install -y postgresql-15
 | 
					sudo apt install -y postgresql-15
 | 
				
			||||||
sleep 2
 | 
					sleep 2
 | 
				
			||||||
@@ -349,7 +357,21 @@ else
 | 
				
			|||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cd /meshcentral
 | 
					cd /meshcentral
 | 
				
			||||||
npm install meshcentral@${MESH_VER}
 | 
					mesh_pkg="$(
 | 
				
			||||||
 | 
					  cat <<EOF
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "archiver": "5.3.1",
 | 
				
			||||||
 | 
					    "meshcentral": "${MESH_VER}",
 | 
				
			||||||
 | 
					    "otplib": "10.2.3",
 | 
				
			||||||
 | 
					    "pg": "8.7.1",
 | 
				
			||||||
 | 
					    "pgtools": "0.3.2"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					)"
 | 
				
			||||||
 | 
					echo "${mesh_pkg}" >/meshcentral/package.json
 | 
				
			||||||
 | 
					npm install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [ "$FROM_MONGO" = true ]; then
 | 
					if [ "$FROM_MONGO" = true ]; then
 | 
				
			||||||
  node node_modules/meshcentral --dbimport >/dev/null
 | 
					  node node_modules/meshcentral --dbimport >/dev/null
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										29
									
								
								update.sh
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								update.sh
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
#!/usr/bin/env bash
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SCRIPT_VERSION="146"
 | 
					SCRIPT_VERSION="148"
 | 
				
			||||||
SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/update.sh'
 | 
					SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/update.sh'
 | 
				
			||||||
LATEST_SETTINGS_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/api/tacticalrmm/tacticalrmm/settings.py'
 | 
					LATEST_SETTINGS_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/api/tacticalrmm/tacticalrmm/settings.py'
 | 
				
			||||||
YELLOW='\033[1;33m'
 | 
					YELLOW='\033[1;33m'
 | 
				
			||||||
@@ -67,6 +67,10 @@ cls() {
 | 
				
			|||||||
  printf "\033c"
 | 
					  printf "\033c"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [ ! -d /etc/apt/keyrings ]; then
 | 
				
			||||||
 | 
					  sudo mkdir -p /etc/apt/keyrings
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CHECK_NATS_LIMITNOFILE=$(grep LimitNOFILE /etc/systemd/system/nats.service)
 | 
					CHECK_NATS_LIMITNOFILE=$(grep LimitNOFILE /etc/systemd/system/nats.service)
 | 
				
			||||||
if ! [[ $CHECK_NATS_LIMITNOFILE ]]; then
 | 
					if ! [[ $CHECK_NATS_LIMITNOFILE ]]; then
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -167,12 +171,11 @@ if [ ! -f /etc/apt/sources.list.d/nginx.list ]; then
 | 
				
			|||||||
  codename=$(lsb_release -sc)
 | 
					  codename=$(lsb_release -sc)
 | 
				
			||||||
  nginxrepo="$(
 | 
					  nginxrepo="$(
 | 
				
			||||||
    cat <<EOF
 | 
					    cat <<EOF
 | 
				
			||||||
deb https://nginx.org/packages/$osname/ $codename nginx
 | 
					deb [signed-by=/etc/apt/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/$osname $codename nginx
 | 
				
			||||||
deb-src https://nginx.org/packages/$osname/ $codename nginx
 | 
					 | 
				
			||||||
EOF
 | 
					EOF
 | 
				
			||||||
  )"
 | 
					  )"
 | 
				
			||||||
  echo "${nginxrepo}" | sudo tee /etc/apt/sources.list.d/nginx.list >/dev/null
 | 
					  echo "${nginxrepo}" | sudo tee /etc/apt/sources.list.d/nginx.list >/dev/null
 | 
				
			||||||
  wget -qO - https://nginx.org/packages/keys/nginx_signing.key | sudo apt-key add -
 | 
					  wget -qO - https://nginx.org/packages/keys/nginx_signing.key | sudo gpg --dearmor -o /etc/apt/keyrings/nginx-archive-keyring.gpg
 | 
				
			||||||
  sudo apt update
 | 
					  sudo apt update
 | 
				
			||||||
  sudo apt install -y nginx
 | 
					  sudo apt install -y nginx
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
@@ -407,8 +410,22 @@ if [[ "${CURRENT_MESH_VER}" != "${LATEST_MESH_VER}" ]] || [[ "$force" = true ]];
 | 
				
			|||||||
  sudo systemctl stop meshcentral
 | 
					  sudo systemctl stop meshcentral
 | 
				
			||||||
  sudo chown ${USER}:${USER} -R /meshcentral
 | 
					  sudo chown ${USER}:${USER} -R /meshcentral
 | 
				
			||||||
  cd /meshcentral
 | 
					  cd /meshcentral
 | 
				
			||||||
  rm -rf node_modules/
 | 
					  rm -rf node_modules/ package.json package-lock.json
 | 
				
			||||||
  npm install meshcentral@${LATEST_MESH_VER}
 | 
					  mesh_pkg="$(
 | 
				
			||||||
 | 
					    cat <<EOF
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "archiver": "5.3.1",
 | 
				
			||||||
 | 
					    "meshcentral": "${LATEST_MESH_VER}",
 | 
				
			||||||
 | 
					    "otplib": "10.2.3",
 | 
				
			||||||
 | 
					    "pg": "8.7.1",
 | 
				
			||||||
 | 
					    "pgtools": "0.3.2"
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					  )"
 | 
				
			||||||
 | 
					  echo "${mesh_pkg}" >/meshcentral/package.json
 | 
				
			||||||
 | 
					  npm install
 | 
				
			||||||
  sudo systemctl start meshcentral
 | 
					  sudo systemctl start meshcentral
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user