Compare commits

...

33 Commits

Author SHA1 Message Date
wh1te909
f82b589d03 Release 0.12.3 2022-04-11 23:16:28 +00:00
wh1te909
cddac4d0fb bump version 2022-04-11 22:21:57 +00:00
wh1te909
ff41bbd0e5 adjust celery config 2022-04-09 17:09:54 +00:00
wh1te909
4bdb6ae84e fix graphics 2022-04-09 17:09:09 +00:00
wh1te909
58fe14bd31 add coverage badge 2022-04-09 02:10:51 +00:00
wh1te909
97f362ed1e fix for multiprocessing 2022-04-09 01:26:04 +00:00
wh1te909
b63e87ecb6 add parallel 2022-04-09 01:01:32 +00:00
wh1te909
ac3550dfd7 add lcov 2022-04-09 00:48:00 +00:00
wh1te909
8278a4cfd9 remove run 2022-04-09 00:45:02 +00:00
wh1te909
f161a2bbc8 more coveralls 2022-04-09 00:43:47 +00:00
wh1te909
6a94489df0 testing coveralls 2022-04-09 00:26:22 +00:00
wh1te909
c3a0b9192f update reqs 2022-04-08 19:34:38 +00:00
wh1te909
69ff70a9ce typo [skip ci] 2022-04-08 18:49:15 +00:00
wh1te909
5284eb0af8 validate mesh username 2022-04-08 18:47:57 +00:00
wh1te909
58384ae136 update supported version 2022-04-08 18:45:53 +00:00
wh1te909
054cc78e65 add meshctrl 2022-04-08 18:30:17 +00:00
wh1te909
8c283281d6 remove lower() from mesh username 2022-04-08 16:06:44 +00:00
wh1te909
241fe41756 fix env 2022-04-05 22:44:41 +00:00
wh1te909
e50e0626fa also check env 2022-04-05 21:31:13 +00:00
wh1te909
c9135f1573 add option to specify sslmode for nats-api pg connection closes #1049 2022-04-05 21:14:22 +00:00
wh1te909
ec2663a152 Release 0.12.2 2022-04-05 03:44:17 +00:00
wh1te909
7567042c8a bump versions 2022-04-05 03:41:18 +00:00
wh1te909
c99ceb155f update CI badge and supported agent versions 2022-04-04 22:25:13 +00:00
wh1te909
f44c92f0d3 switch to gh actions for tests 2022-04-04 21:54:46 +00:00
wh1te909
492701ec62 change dir 2022-04-04 07:08:57 +00:00
wh1te909
a6d0acaa4d black 2022-04-03 23:14:44 +00:00
wh1te909
f84b4e7274 remove dev deps from pipelines 2022-04-03 23:09:08 +00:00
wh1te909
b7ef5b82d8 add silk to dev 2022-04-03 22:48:27 +00:00
wh1te909
a854d2c38c add authorization to NATS 2022-04-03 22:47:43 +00:00
wh1te909
5140499bbd update uwsgi conf 2022-04-01 06:12:19 +00:00
wh1te909
7183e9ee85 attempt 2 2022-03-31 06:07:23 +00:00
wh1te909
11885e0aca attemp 1 to fix pipelines 2022-03-31 05:54:30 +00:00
wh1te909
2bda4e822c move pipelines 2022-03-31 05:34:50 +00:00
30 changed files with 429 additions and 1028 deletions

66
.github/workflows/ci-tests.yml vendored Normal file
View File

@@ -0,0 +1,66 @@
name: Tests CI
on:
push:
branches:
- "*"
pull_request:
branches:
- "*"
jobs:
test:
runs-on: self-hosted
steps:
- uses: actions/checkout@v2
- name: Setup virtual env and install requirements
run: |
sudo -u postgres psql -c 'DROP DATABASE IF EXISTS pipeline'
sudo -u postgres psql -c 'DROP DATABASE IF EXISTS test_pipeline'
sudo -u postgres psql -c 'CREATE DATABASE pipeline'
sudo -u postgres psql -c "SET client_encoding = 'UTF8'" pipeline
pwd
rm -rf /actions-runner/_work/trmm-actions/trmm-actions/api/env
cd api
python3.10 -m venv env
source env/bin/activate
cd tacticalrmm
python --version
SETTINGS_FILE="tacticalrmm/settings.py"
SETUPTOOLS_VER=$(grep "^SETUPTOOLS_VER" "$SETTINGS_FILE" | awk -F'[= "]' '{print $5}')
WHEEL_VER=$(grep "^WHEEL_VER" "$SETTINGS_FILE" | awk -F'[= "]' '{print $5}')
pip install --upgrade pip
pip install setuptools==${SETUPTOOLS_VER} wheel==${WHEEL_VER}
pip install -r requirements.txt -r requirements-test.txt
- name: Run django tests
env:
GHACTIONS: "yes"
run: |
cd api/tacticalrmm
source ../env/bin/activate
rm -f .coverage coverage.lcov
coverage run --concurrency=multiprocessing manage.py test -v 2 --parallel
coverage combine
coverage lcov
if [ $? -ne 0 ]; then
exit 1
fi
- name: Codestyle black
run: |
cd api
source env/bin/activate
black --exclude migrations/ --check tacticalrmm
if [ $? -ne 0 ]; then
exit 1
fi
- name: Coveralls
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: ./api/tacticalrmm/coverage.lcov
base-path: ./api/tacticalrmm

1
.gitignore vendored
View File

@@ -51,3 +51,4 @@ reset_db.sh
run_go_cmd.py run_go_cmd.py
nats-api.conf nats-api.conf
ignore/ ignore/
coverage.lcov

View File

@@ -1,7 +1,7 @@
# Tactical RMM # Tactical RMM
[![Build Status](https://dev.azure.com/dcparsi/Tactical%20RMM/_apis/build/status/wh1te909.tacticalrmm?branchName=develop)](https://dev.azure.com/dcparsi/Tactical%20RMM/_build/latest?definitionId=4&branchName=develop) ![CI Tests](https://github.com/amidaware/tacticalrmm/actions/workflows/ci-tests.yml/badge.svg?branch=develop)
[![Coverage Status](https://coveralls.io/repos/github/wh1te909/tacticalrmm/badge.png?branch=develop&kill_cache=1)](https://coveralls.io/github/wh1te909/tacticalrmm?branch=develop) [![Coverage Status](https://coveralls.io/repos/github/amidaware/tacticalrmm/badge.svg?branch=develop)](https://coveralls.io/github/amidaware/tacticalrmm?branch=develop)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black)
Tactical RMM is a remote monitoring & management tool, built with Django and Vue.\ Tactical RMM is a remote monitoring & management tool, built with Django and Vue.\
@@ -28,9 +28,12 @@ Demo database resets every hour. A lot of features are disabled for obvious reas
- Remote software installation via chocolatey - Remote software installation via chocolatey
- Software and hardware inventory - Software and hardware inventory
## Windows versions supported ## Windows agent versions supported
- Windows 7, 8.1, 10, Server 2008R2, 2012R2, 2016, 2019 - Windows 7, 8.1, 10, 11, Server 2008R2, 2012R2, 2016, 2019, 2022
## Linux agent versions supported
- Any distro with systemd
## Installation / Backup / Restore / Usage ## Installation / Backup / Restore / Usage

View File

@@ -4,8 +4,8 @@
| Version | Supported | | Version | Supported |
| ------- | ------------------ | | ------- | ------------------ |
| 0.12.0 | :white_check_mark: | | 0.12.2 | :white_check_mark: |
| < 0.12.0 | :x: | | < 0.12.2 | :x: |
## Reporting a Vulnerability ## Reporting a Vulnerability

View File

@@ -1,7 +1,5 @@
import asyncio import asyncio
import base64
import re import re
import time
from collections import Counter from collections import Counter
from distutils.version import LooseVersion from distutils.version import LooseVersion
from typing import Any from typing import Any
@@ -11,10 +9,6 @@ import nats
import validators import validators
from asgiref.sync import sync_to_async from asgiref.sync import sync_to_async
from core.models import TZ_CHOICES, CoreSettings from core.models import TZ_CHOICES, CoreSettings
from Crypto.Cipher import AES
from Crypto.Hash import SHA3_384
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad
from django.conf import settings from django.conf import settings
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
from django.db import models from django.db import models
@@ -219,7 +213,8 @@ class Agent(BaseAuditModel):
try: try:
if not self.wmi_detail["gpus"]: if not self.wmi_detail["gpus"]:
return "No graphics cards" return "No graphics cards"
return self.wmi_detail["gpus"]
return ", ".join(self.wmi_detail["gpus"])
except: except:
return "Error getting graphics cards" return "Error getting graphics cards"
@@ -613,30 +608,6 @@ class Agent(BaseAuditModel):
# Generate tasks based on policies # Generate tasks based on policies
Policy.generate_policy_tasks(self) Policy.generate_policy_tasks(self)
# https://github.com/Ylianst/MeshCentral/issues/59#issuecomment-521965347
def get_login_token(self, key, user, action=3):
try:
key = bytes.fromhex(key)
key1 = key[0:48]
key2 = key[48:]
msg = '{{"a":{}, "u":"{}","time":{}}}'.format(
action, user.lower(), int(time.time())
)
iv = get_random_bytes(16)
# sha
h = SHA3_384.new()
h.update(key1)
hashed_msg = h.digest() + msg.encode()
# aes
cipher = AES.new(key2, AES.MODE_CBC, iv)
msg = cipher.encrypt(pad(hashed_msg, 16))
return base64.b64encode(iv + msg, altchars=b"@$").decode("utf-8")
except Exception:
return "err"
def _do_nats_debug(self, agent, message): def _do_nats_debug(self, agent, message):
DebugLog.error(agent=agent, log_type="agent_issues", message=message) DebugLog.error(agent=agent, log_type="agent_issues", message=message)

View File

@@ -519,7 +519,7 @@ class TestAgentViews(TacticalTestCase):
self.check_not_authenticated("post", url) self.check_not_authenticated("post", url)
@patch("agents.models.Agent.get_login_token") @patch("meshctrl.utils.get_auth_token")
def test_meshcentral_tabs(self, mock_token): def test_meshcentral_tabs(self, mock_token):
url = f"{base_url}/{self.agent.agent_id}/meshcentral/" url = f"{base_url}/{self.agent.agent_id}/meshcentral/"
mock_token.return_value = "askjh1k238uasdhk487234jadhsajksdhasd" mock_token.return_value = "askjh1k238uasdhk487234jadhsajksdhasd"
@@ -547,10 +547,6 @@ class TestAgentViews(TacticalTestCase):
self.assertEqual(r.status_code, 200) self.assertEqual(r.status_code, 200)
mock_token.return_value = "err"
r = self.client.get(url)
self.assertEqual(r.status_code, 400)
self.check_not_authenticated("get", url) self.check_not_authenticated("get", url)
@patch("agents.models.Agent.nats_cmd") @patch("agents.models.Agent.nats_cmd")

View File

@@ -4,6 +4,7 @@ import os
import random import random
import string import string
import time import time
from meshctrl.utils import get_auth_token
from core.models import CodeSignToken, CoreSettings from core.models import CodeSignToken, CoreSettings
from core.utils import get_mesh_ws_url, remove_mesh_agent, send_command_with_mesh from core.utils import get_mesh_ws_url, remove_mesh_agent, send_command_with_mesh
@@ -208,13 +209,7 @@ class AgentMeshCentral(APIView):
agent = get_object_or_404(Agent, agent_id=agent_id) agent = get_object_or_404(Agent, agent_id=agent_id)
core = CoreSettings.objects.first() core = CoreSettings.objects.first()
token = agent.get_login_token( token = get_auth_token(user=core.mesh_username, key=core.mesh_token)
key=core.mesh_token,
user=f"user//{core.mesh_username.lower()}", # type:ignore
)
if token == "err":
return notify_error("Invalid mesh token")
control = f"{core.mesh_site}/?login={token}&gotonode={agent.mesh_node_id}&viewmode=11&hide=31" # type:ignore control = f"{core.mesh_site}/?login={token}&gotonode={agent.mesh_node_id}&viewmode=11&hide=31" # type:ignore
terminal = f"{core.mesh_site}/?login={token}&gotonode={agent.mesh_node_id}&viewmode=12&hide=31" # type:ignore terminal = f"{core.mesh_site}/?login={token}&gotonode={agent.mesh_node_id}&viewmode=12&hide=31" # type:ignore

View File

@@ -1,9 +1,10 @@
import asyncio import asyncio
from meshctrl.utils import get_auth_token
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from core.models import CoreSettings from core.models import CoreSettings
from core.utils import get_auth_token, get_mesh_device_id, get_mesh_ws_url from core.utils import get_mesh_device_id, get_mesh_ws_url
class Command(BaseCommand): class Command(BaseCommand):

View File

@@ -12,6 +12,13 @@ class Command(BaseCommand):
self.stdout.write("Creating configuration for nats-api...") self.stdout.write("Creating configuration for nats-api...")
db = settings.DATABASES["default"] db = settings.DATABASES["default"]
if hasattr(settings, "DB_SSL"):
ssl = settings.DB_SSL
elif "DB_SSL" in os.environ:
ssl = os.getenv("DB_SSL")
else:
ssl = "disable"
config = { config = {
"key": settings.SECRET_KEY, "key": settings.SECRET_KEY,
"natsurl": f"tls://{settings.ALLOWED_HOSTS[0]}:4222", "natsurl": f"tls://{settings.ALLOWED_HOSTS[0]}:4222",
@@ -20,6 +27,7 @@ class Command(BaseCommand):
"host": db["HOST"], "host": db["HOST"],
"port": int(db["PORT"]), "port": int(db["PORT"]),
"dbname": db["NAME"], "dbname": db["NAME"],
"sslmode": ssl,
} }
conf = os.path.join(settings.BASE_DIR, "nats-api.conf") conf = os.path.join(settings.BASE_DIR, "nats-api.conf")
with open(conf, "w") as f: with open(conf, "w") as f:

View File

@@ -45,9 +45,9 @@ class Command(BaseCommand):
# Check for Mesh Username # Check for Mesh Username
if ( if (
not mesh_settings.mesh_username not mesh_settings.mesh_username
or settings.MESH_USERNAME != mesh_settings.mesh_username or settings.MESH_USERNAME.lower() != mesh_settings.mesh_username
): ):
mesh_settings.mesh_username = settings.MESH_USERNAME mesh_settings.mesh_username = settings.MESH_USERNAME.lower()
# Check for Mesh Site # Check for Mesh Site
if ( if (

View File

@@ -97,7 +97,7 @@ class CoreSettings(BaseAuditModel):
if not self.pk: if not self.pk:
try: try:
self.mesh_site = settings.MESH_SITE self.mesh_site = settings.MESH_SITE
self.mesh_username = settings.MESH_USERNAME self.mesh_username = settings.MESH_USERNAME.lower()
self.mesh_token = settings.MESH_TOKEN_KEY self.mesh_token = settings.MESH_TOKEN_KEY
except: except:
pass pass

View File

@@ -1,30 +1,14 @@
import json import json
import tempfile import tempfile
import time
from base64 import b64encode from base64 import b64encode
from meshctrl.utils import get_auth_token
import requests import requests
import websockets import websockets
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from django.conf import settings from django.conf import settings
from django.http import FileResponse from django.http import FileResponse
def get_auth_token(user, key):
key = bytes.fromhex(key)
key1 = key[0:32]
msg = '{{"userid":"{}", "domainid":"{}", "time":{}}}'.format(
f"user//{user}", "", int(time.time())
)
iv = get_random_bytes(12)
a = AES.new(key1, AES.MODE_GCM, iv)
msg, tag = a.encrypt_and_digest(bytes(msg, "utf-8")) # type: ignore
return b64encode(iv + tag + msg, altchars=b"@$").decode("utf-8")
def get_mesh_ws_url() -> str: def get_mesh_ws_url() -> str:
from core.models import CoreSettings from core.models import CoreSettings

View File

@@ -3,3 +3,4 @@ Werkzeug
django-extensions django-extensions
isort isort
types-pytz types-pytz
django-silk

View File

@@ -1,3 +1,5 @@
coverage==6.3.2 coverage
coveralls==3.3.1 coveralls
model_bakery model_bakery
black
tblib

View File

@@ -1,5 +1,5 @@
asgiref==3.5.0 asgiref==3.5.0
celery==5.2.3 celery==5.2.6
certifi==2021.10.8 certifi==2021.10.8
cffi==1.15.0 cffi==1.15.0
channels==3.0.4 channels==3.0.4
@@ -14,8 +14,7 @@ django-rest-knox==4.2.0
djangorestframework==3.13.1 djangorestframework==3.13.1
future==0.18.2 future==0.18.2
msgpack==1.0.3 msgpack==1.0.3
nats-py==2.0.0 nats-py==2.1.0
packaging==21.3
psycopg2-binary==2.9.3 psycopg2-binary==2.9.3
pycparser==2.21 pycparser==2.21
pycryptodome==3.14.1 pycryptodome==3.14.1
@@ -23,15 +22,16 @@ pyotp==2.6.0
pyparsing==3.0.7 pyparsing==3.0.7
pytz==2022.1 pytz==2022.1
qrcode==7.3.1 qrcode==7.3.1
redis==4.1.4 redis==4.2.2
requests==2.27.1 requests==2.27.1
six==1.16.0 six==1.16.0
sqlparse==0.4.2 sqlparse==0.4.2
twilio==7.8.0 twilio==7.8.1
urllib3==1.26.9 urllib3==1.26.9
uWSGI==2.0.20 uWSGI==2.0.20
validators==0.18.2 validators==0.18.2
vine==5.0.0 vine==5.0.0
websockets==10.2 websockets==10.2
zipp==3.7.0 zipp==3.8.0
drf_spectacular==0.21.2 drf_spectacular==0.21.2
meshctrl==0.1.13

View File

@@ -17,22 +17,22 @@ LINUX_AGENT_SCRIPT = BASE_DIR / "core" / "agent_linux.sh"
AUTH_USER_MODEL = "accounts.User" AUTH_USER_MODEL = "accounts.User"
# latest release # latest release
TRMM_VERSION = "0.12.1" TRMM_VERSION = "0.12.3"
# 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.159" APP_VER = "0.0.160"
# https://github.com/amidaware/rmmagent # https://github.com/amidaware/rmmagent
LATEST_AGENT_VER = "2.0.1" LATEST_AGENT_VER = "2.0.2"
MESH_VER = "0.9.98" MESH_VER = "1.0.2"
NATS_SERVER_VER = "2.7.4" NATS_SERVER_VER = "2.7.4"
# for the update script, bump when need to recreate venv or npm install # for the update script, bump when need to recreate venv or npm install
PIP_VER = "27" PIP_VER = "28"
NPM_VER = "30" NPM_VER = "31"
SETUPTOOLS_VER = "59.6.0" SETUPTOOLS_VER = "59.6.0"
WHEEL_VER = "0.37.1" WHEEL_VER = "0.37.1"
@@ -52,6 +52,29 @@ REST_KNOX = {
"MIN_REFRESH_INTERVAL": 600, "MIN_REFRESH_INTERVAL": 600,
} }
if "GHACTIONS" in os.environ:
print("-----------------------PIPELINE----------------------------")
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "pipeline",
"USER": "pipeline",
"PASSWORD": "pipeline123456",
"HOST": "127.0.0.1",
"PORT": "",
}
}
SECRET_KEY = "abcdefghijklmnoptravis123456789"
DEBUG = False
ALLOWED_HOSTS = ["api.example.com"]
ADMIN_URL = "abc123456/"
CORS_ORIGIN_WHITELIST = ["https://rmm.example.com"]
MESH_USERNAME = "pipeline"
MESH_SITE = "https://example.com"
MESH_TOKEN_KEY = "bd65e957a1e70c622d32523f61508400d6cd0937001a7ac12042227eba0b9ed625233851a316d4f489f02994145f74537a331415d00047dbbf13d940f556806dffe7a8ce1de216dc49edbad0c1a7399c"
REDIS_HOST = "localhost"
ADMIN_ENABLED = False
try: try:
from .local_settings import * from .local_settings import *
except ImportError: except ImportError:
@@ -74,11 +97,34 @@ SPECTACULAR_SETTINGS = {
"AUTHENTICATION_WHITELIST": ["tacticalrmm.auth.APIAuthentication"], "AUTHENTICATION_WHITELIST": ["tacticalrmm.auth.APIAuthentication"],
} }
if not "AZPIPELINE" in os.environ:
if not DEBUG: # type: ignore if not DEBUG: # type: ignore
REST_FRAMEWORK.update( REST_FRAMEWORK.update(
{"DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",)} {"DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",)}
) )
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"corsheaders.middleware.CorsMiddleware", ##
"tacticalrmm.middleware.LogIPMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"tacticalrmm.middleware.AuditMiddleware",
"tacticalrmm.middleware.LinuxMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
if ADMIN_ENABLED: # type: ignore
MIDDLEWARE += ("django.contrib.messages.middleware.MessageMiddleware",)
try:
if DEMO: # type: ignore
MIDDLEWARE += ("tacticalrmm.middleware.DemoMiddleware",)
except:
pass
INSTALLED_APPS = [ INSTALLED_APPS = [
"django.contrib.auth", "django.contrib.auth",
@@ -107,21 +153,23 @@ INSTALLED_APPS = [
"drf_spectacular", "drf_spectacular",
] ]
if not "AZPIPELINE" in os.environ:
if DEBUG: # type: ignore
INSTALLED_APPS += ("django_extensions",)
CHANNEL_LAYERS = { if DEBUG: # type: ignore
"default": { INSTALLED_APPS += (
"BACKEND": "channels_redis.core.RedisChannelLayer", "django_extensions",
"CONFIG": { "silk",
"hosts": [(REDIS_HOST, 6379)], # type: ignore )
},
MIDDLEWARE.insert(0, "silk.middleware.SilkyMiddleware")
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [(REDIS_HOST, 6379)], # type: ignore
}, },
} },
}
if "AZPIPELINE" in os.environ:
ADMIN_ENABLED = False
if ADMIN_ENABLED: # type: ignore if ADMIN_ENABLED: # type: ignore
INSTALLED_APPS += ( INSTALLED_APPS += (
@@ -130,28 +178,6 @@ if ADMIN_ENABLED: # type: ignore
) )
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"corsheaders.middleware.CorsMiddleware", ##
"tacticalrmm.middleware.LogIPMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"tacticalrmm.middleware.AuditMiddleware",
"tacticalrmm.middleware.LinuxMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
if ADMIN_ENABLED: # type: ignore
MIDDLEWARE += ("django.contrib.messages.middleware.MessageMiddleware",)
try:
if DEMO: # type: ignore
MIDDLEWARE += ("tacticalrmm.middleware.DemoMiddleware",)
except:
pass
ROOT_URLCONF = "tacticalrmm.urls" ROOT_URLCONF = "tacticalrmm.urls"
@@ -226,38 +252,3 @@ LOGGING = {
"django.request": {"handlers": ["file"], "level": "ERROR", "propagate": True} "django.request": {"handlers": ["file"], "level": "ERROR", "propagate": True}
}, },
} }
if "AZPIPELINE" in os.environ:
print("PIPELINE")
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "pipeline",
"USER": "pipeline",
"PASSWORD": "pipeline123456",
"HOST": "127.0.0.1",
"PORT": "",
}
}
REST_FRAMEWORK = {
"DATETIME_FORMAT": "%b-%d-%Y - %H:%M",
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
"DEFAULT_AUTHENTICATION_CLASSES": (
"knox.auth.TokenAuthentication",
"tacticalrmm.auth.APIAuthentication",
),
"DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",),
}
ALLOWED_HOSTS = ["api.example.com"]
DEBUG = True
SECRET_KEY = "abcdefghijklmnoptravis123456789"
ADMIN_URL = "abc123456/"
SCRIPTS_DIR = os.path.join(Path(BASE_DIR).parents[1], "scripts")
MESH_USERNAME = "pipeline"
MESH_SITE = "https://example.com"
MESH_TOKEN_KEY = "bd65e957a1e70c622d32523f61508400d6cd0937001a7ac12042227eba0b9ed625233851a316d4f489f02994145f74537a331415d00047dbbf13d940f556806dffe7a8ce1de216dc49edbad0c1a7399c"
REDIS_HOST = "localhost"

View File

@@ -43,6 +43,9 @@ if getattr(settings, "ADMIN_ENABLED", False):
urlpatterns += (path(settings.ADMIN_URL, admin.site.urls),) urlpatterns += (path(settings.ADMIN_URL, admin.site.urls),)
if getattr(settings, "DEBUG", False):
urlpatterns += [path("silk/", include("silk.urls", namespace="silk"))]
if getattr(settings, "SWAGGER_ENABLED", False): if getattr(settings, "SWAGGER_ENABLED", False):
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView

View File

@@ -161,14 +161,28 @@ def convert_to_iso_duration(string: str) -> str:
def reload_nats(): def reload_nats():
users = [{"user": "tacticalrmm", "password": settings.SECRET_KEY}] users = [
{
"user": "tacticalrmm",
"password": settings.SECRET_KEY,
"permissions": {"publish": ">", "subscribe": ">"},
}
]
agents = Agent.objects.prefetch_related("user").only( agents = Agent.objects.prefetch_related("user").only(
"pk", "agent_id" "pk", "agent_id"
) # type:ignore ) # type:ignore
for agent in agents: for agent in agents:
try: try:
users.append( users.append(
{"user": agent.agent_id, "password": agent.user.auth_token.key} {
"user": agent.agent_id,
"password": agent.user.auth_token.key,
"permissions": {
"publish": {"allow": agent.agent_id},
"subscribe": {"allow": agent.agent_id},
"allow_responses": True,
},
}
) )
except: except:
DebugLog.critical( DebugLog.critical(

View File

@@ -1,65 +0,0 @@
trigger:
- master
- develop
jobs:
- job: setup_env
displayName: "Setup"
strategy:
matrix:
Debian10:
AGENT_NAME: "az-pipeline-fran"
pool:
name: linux-vms
demands:
- agent.name -equals $(AGENT_NAME)
steps:
- script: |
sudo -u postgres psql -c 'DROP DATABASE IF EXISTS pipeline'
sudo -u postgres psql -c 'DROP DATABASE IF EXISTS test_pipeline'
sudo -u postgres psql -c 'CREATE DATABASE pipeline'
sudo -u postgres psql -c "SET client_encoding = 'UTF8'" pipeline
SETTINGS_FILE="/myagent/_work/1/s/api/tacticalrmm/tacticalrmm/settings.py"
rm -rf /myagent/_work/1/s/api/env
cd /myagent/_work/1/s/api
python3.10 -m venv env
source env/bin/activate
cd /myagent/_work/1/s/api/tacticalrmm
pip install --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org --upgrade pip
SETUPTOOLS_VER=$(grep "^SETUPTOOLS_VER" "$SETTINGS_FILE" | awk -F'[= "]' '{print $5}')
WHEEL_VER=$(grep "^WHEEL_VER" "$SETTINGS_FILE" | awk -F'[= "]' '{print $5}')
pip install --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org setuptools==${SETUPTOOLS_VER} wheel==${WHEEL_VER}
pip install --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org -r requirements.txt -r requirements-test.txt -r requirements-dev.txt
displayName: "Install Python Dependencies"
- script: |
cd /myagent/_work/1/s/api
source env/bin/activate
cd /myagent/_work/1/s/api/tacticalrmm
coverage run manage.py test -v 2
if [ $? -ne 0 ]; then
exit 1
fi
displayName: "Run django tests"
- script: |
cd /myagent/_work/1/s/api
source env/bin/activate
black --exclude migrations/ --check tacticalrmm
if [ $? -ne 0 ]; then
exit 1
fi
displayName: "Codestyle black"
- script: |
cd /myagent/_work/1/s/api
source env/bin/activate
cd /myagent/_work/1/s/api/tacticalrmm
export CIRCLE_BRANCH=$BUILD_SOURCEBRANCH
coveralls
displayName: "coveralls"
env:
CIRCLECI: 1
CIRCLE_BUILD_NUM: $(Build.BuildNumber)

View File

@@ -129,11 +129,13 @@ processes = ${uwsgiprocs}
threads = ${uwsgiprocs} threads = ${uwsgiprocs}
enable-threads = true enable-threads = true
socket = 0.0.0.0:8080 socket = 0.0.0.0:8080
harakiri = 300
chmod-socket = 660 chmod-socket = 660
buffer-size = 65535 buffer-size = 65535
vacuum = true vacuum = true
die-on-term = true die-on-term = true
max-requests = 2000 max-requests = 500
disable-logging = true
EOF EOF
)" )"

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
SCRIPT_VERSION="59" SCRIPT_VERSION="61"
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
@@ -406,6 +406,7 @@ buffer-size = 65535
vacuum = true vacuum = true
die-on-term = true die-on-term = true
max-requests = 500 max-requests = 500
disable-logging = true
EOF EOF
)" )"
echo "${uwsgini}" > /rmm/api/tacticalrmm/app.ini echo "${uwsgini}" > /rmm/api/tacticalrmm/app.ini
@@ -657,7 +658,7 @@ CELERY_APP="tacticalrmm"
CELERYD_MULTI="multi" CELERYD_MULTI="multi"
CELERYD_OPTS="--time-limit=86400 --autoscale=50,3" CELERYD_OPTS="--time-limit=86400 --autoscale=20,2"
CELERYD_PID_FILE="/rmm/api/tacticalrmm/%n.pid" CELERYD_PID_FILE="/rmm/api/tacticalrmm/%n.pid"
CELERYD_LOG_FILE="/var/log/celery/%n%I.log" CELERYD_LOG_FILE="/var/log/celery/%n%I.log"

View File

@@ -11,7 +11,7 @@ import (
) )
var ( var (
version = "3.0.1" version = "3.0.2"
log = logrus.New() log = logrus.New()
) )

Binary file not shown.

View File

@@ -13,4 +13,5 @@ type DjangoConfig struct {
Host string `json:"host"` Host string `json:"host"`
Port int `json:"port"` Port int `json:"port"`
DBName string `json:"dbname"` DBName string `json:"dbname"`
SSLMode string `json:"sslmode"`
} }

View File

@@ -41,8 +41,8 @@ func GetConfig(cfg string) (db *sqlx.DB, r DjangoConfig, err error) {
} }
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+ psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
"password=%s dbname=%s sslmode=disable", "password=%s dbname=%s sslmode=%s",
r.Host, r.Port, r.User, r.Pass, r.DBName) r.Host, r.Port, r.User, r.Pass, r.DBName, r.SSLMode)
db, err = sqlx.Connect("postgres", psqlInfo) db, err = sqlx.Connect("postgres", psqlInfo)
if err != nil { if err != nil {

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
SCRIPT_VERSION="34" SCRIPT_VERSION="35"
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
@@ -276,6 +276,7 @@ buffer-size = 65535
vacuum = true vacuum = true
die-on-term = true die-on-term = true
max-requests = 500 max-requests = 500
disable-logging = true
EOF EOF
)" )"
echo "${uwsgini}" > /rmm/api/tacticalrmm/app.ini echo "${uwsgini}" > /rmm/api/tacticalrmm/app.ini

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
SCRIPT_VERSION="131" SCRIPT_VERSION="133"
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'
@@ -144,6 +144,7 @@ buffer-size = 65535
vacuum = true vacuum = true
die-on-term = true die-on-term = true
max-requests = 500 max-requests = 500
disable-logging = true
EOF EOF
)" )"
echo "${uwsgini}" > /rmm/api/tacticalrmm/app.ini echo "${uwsgini}" > /rmm/api/tacticalrmm/app.ini
@@ -257,6 +258,11 @@ sudo chown ${USER}:${USER} -R /etc/conf.d/
sudo chown ${USER}:${USER} -R /etc/letsencrypt sudo chown ${USER}:${USER} -R /etc/letsencrypt
sudo chmod 775 -R /etc/letsencrypt sudo chmod 775 -R /etc/letsencrypt
CHECK_CELERY_CONFIG=$(grep "autoscale=20,2" /etc/conf.d/celery.conf)
if ! [[ $CHECK_CELERY_CONFIG ]]; then
sed -i 's/CELERYD_OPTS=.*/CELERYD_OPTS="--time-limit=86400 --autoscale=20,2"/g' /etc/conf.d/celery.conf
fi
CHECK_ADMIN_ENABLED=$(grep ADMIN_ENABLED /rmm/api/tacticalrmm/tacticalrmm/local_settings.py) CHECK_ADMIN_ENABLED=$(grep ADMIN_ENABLED /rmm/api/tacticalrmm/tacticalrmm/local_settings.py)
if ! [[ $CHECK_ADMIN_ENABLED ]]; then if ! [[ $CHECK_ADMIN_ENABLED ]]; then
adminenabled="$(cat << EOF adminenabled="$(cat << EOF

987
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,12 +10,12 @@
"test:e2e:ci": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress run\"" "test:e2e:ci": "cross-env E2E_TEST=true start-test \"quasar dev\" http-get://localhost:8080 \"cypress run\""
}, },
"dependencies": { "dependencies": {
"@quasar/extras": "^1.13.3", "@quasar/extras": "^1.13.5",
"apexcharts": "^3.33.2", "apexcharts": "^3.33.2",
"axios": "^0.26.1", "axios": "^0.26.1",
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"qrcode.vue": "^3.3.3", "qrcode.vue": "^3.3.3",
"quasar": "^2.6.1", "quasar": "^2.6.6",
"vue": "^3.2.31", "vue": "^3.2.31",
"vue3-ace-editor": "^2.2.2", "vue3-ace-editor": "^2.2.2",
"vue3-apexcharts": "^1.4.1", "vue3-apexcharts": "^1.4.1",
@@ -23,7 +23,7 @@
"vuex": "^4.0.2" "vuex": "^4.0.2"
}, },
"devDependencies": { "devDependencies": {
"@quasar/app-webpack": "^3.4.5", "@quasar/app-webpack": "^3.5.0",
"@quasar/cli": "^1.3.2" "@quasar/cli": "^1.3.2"
}, },
"browserslist": [ "browserslist": [

View File

@@ -304,7 +304,15 @@
<q-card-section class="row"> <q-card-section class="row">
<div class="col-4">Username:</div> <div class="col-4">Username:</div>
<div class="col-2"></div> <div class="col-2"></div>
<q-input dense outlined v-model="settings.mesh_username" class="col-6" /> <q-input
dense
outlined
v-model="settings.mesh_username"
class="col-6"
:rules="[
val => (val == val.toLowerCase() && val != val.toUpperCase()) || 'Username must be all lowercase',
]"
/>
</q-card-section> </q-card-section>
<q-card-section class="row"> <q-card-section class="row">
<div class="col-4">Mesh Site:</div> <div class="col-4">Mesh Site:</div>