Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8554cb5d6c | ||
|
|
f901614056 | ||
|
|
b555d217ab | ||
|
|
775c600234 | ||
|
|
128f2570b8 | ||
|
|
3cd53e79b4 | ||
|
|
ebba84ffda | ||
|
|
1e1a42fe98 | ||
|
|
8a744a440d | ||
|
|
f4fc3c7d55 | ||
|
|
0594d121de | ||
|
|
12c85d6234 | ||
|
|
5e37728f66 | ||
|
|
e8e19fede7 | ||
|
|
e565dbfa66 | ||
|
|
d180d6820c | ||
|
|
7f252e9b7c | ||
|
|
41db8681f8 | ||
|
|
26cd58fd6d | ||
|
|
63c7e1aa9d | ||
|
|
d5a6063e5e | ||
|
|
00affdbdec | ||
|
|
db3f0bbd4f | ||
|
|
020a59cb97 | ||
|
|
ff4fa6402d | ||
|
|
80f7555499 | ||
|
|
10cc187c5d |
@@ -1,41 +1,3 @@
|
||||
# To ensure app dependencies are ported from your virtual environment/host machine into your container, run 'pip freeze > requirements.txt' in the terminal to overwrite this file
|
||||
asgiref==3.5.0
|
||||
celery==5.2.6
|
||||
channels==3.0.4
|
||||
channels_redis==3.4.0
|
||||
daphne==3.0.2
|
||||
Django==4.0.4
|
||||
django-cors-headers==3.11.0
|
||||
django-ipware==4.0.2
|
||||
django-rest-knox==4.2.0
|
||||
djangorestframework==3.13.1
|
||||
future==0.18.2
|
||||
msgpack==1.0.3
|
||||
nats-py==2.1.0
|
||||
packaging==21.3
|
||||
psycopg2-binary==2.9.3
|
||||
pycryptodome==3.14.1
|
||||
pyotp==2.6.0
|
||||
pytz==2022.1
|
||||
qrcode==7.3.1
|
||||
redis==4.2.2
|
||||
requests==2.27.1
|
||||
twilio==7.8.1
|
||||
urllib3==1.26.9
|
||||
validators==0.18.2
|
||||
websockets==10.2
|
||||
drf_spectacular==0.22.0
|
||||
meshctrl==0.1.15
|
||||
hiredis==2.0.0
|
||||
|
||||
# dev
|
||||
black==22.3.0
|
||||
django-extensions==3.1.5
|
||||
isort==5.10.1
|
||||
mypy==0.942
|
||||
types-pytz==2021.3.6
|
||||
model-bakery==1.5.0
|
||||
coverage==6.3.2
|
||||
django-silk==4.3.0
|
||||
django-stubs==1.10.1
|
||||
djangorestframework-stubs==1.5.0
|
||||
-r ../api/tacticalrmm/requirements.txt
|
||||
-r ../api/tacticalrmm/requirements-dev.txt
|
||||
-r ../api/tacticalrmm/requirements-test.txt
|
||||
34
.github/workflows/devskim-analysis.yml
vendored
34
.github/workflows/devskim-analysis.yml
vendored
@@ -1,34 +0,0 @@
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
name: DevSkim
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
pull_request:
|
||||
branches: [ develop ]
|
||||
schedule:
|
||||
- cron: '19 5 * * 0'
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: DevSkim
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Run DevSkim scanner
|
||||
uses: microsoft/DevSkim-Action@v1
|
||||
|
||||
- name: Upload DevSkim scan results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: devskim-results.sarif
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 0.12.2 | :white_check_mark: |
|
||||
| < 0.12.2 | :x: |
|
||||
| 0.14.1 | :white_check_mark: |
|
||||
| < 0.14.1 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
3
ansible/README.md
Normal file
3
ansible/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### tacticalrmm ansible WIP
|
||||
|
||||
ansible role to setup a Debian 11 VM for tacticalrmm local development
|
||||
37
ansible/roles/trmm_dev/defaults/main.yml
Normal file
37
ansible/roles/trmm_dev/defaults/main.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
user: "tactical"
|
||||
python_ver: "3.10.4"
|
||||
backend_repo: "https://github.com/amidaware/tacticalrmm.git"
|
||||
frontend_repo: "https://github.com/amidaware/tacticalrmm-web.git"
|
||||
scripts_repo: "https://github.com/amidaware/community-scripts.git"
|
||||
backend_dir: "/opt/trmm"
|
||||
frontend_dir: "/opt/trmm-web"
|
||||
scripts_dir: "/opt/community-scripts"
|
||||
trmm_dir: "/opt/trmm/api/tacticalrmm/tacticalrmm"
|
||||
settings_file: "{{ trmm_dir }}/settings.py"
|
||||
local_settings_file: "{{ trmm_dir }}/local_settings.py"
|
||||
|
||||
base_pkgs:
|
||||
- build-essential
|
||||
- curl
|
||||
- wget
|
||||
- dirmngr
|
||||
- gnupg
|
||||
- openssl
|
||||
- gcc
|
||||
- g++
|
||||
- make
|
||||
- ca-certificates
|
||||
- redis
|
||||
- git
|
||||
|
||||
python_pkgs:
|
||||
- zlib1g-dev
|
||||
- libncurses5-dev
|
||||
- libgdbm-dev
|
||||
- libnss3-dev
|
||||
- libssl-dev
|
||||
- libreadline-dev
|
||||
- libffi-dev
|
||||
- libsqlite3-dev
|
||||
- libbz2-dev
|
||||
25
ansible/roles/trmm_dev/files/nginx-default.conf
Normal file
25
ansible/roles/trmm_dev/files/nginx-default.conf
Normal file
@@ -0,0 +1,25 @@
|
||||
worker_rlimit_nofile 1000000;
|
||||
user www-data;
|
||||
worker_processes auto;
|
||||
pid /run/nginx.pid;
|
||||
include /etc/nginx/modules-enabled/*.conf;
|
||||
|
||||
events {
|
||||
worker_connections 2048;
|
||||
}
|
||||
|
||||
http {
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
types_hash_max_size 2048;
|
||||
server_names_hash_bucket_size 64;
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
|
||||
ssl_prefer_server_ciphers on;
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
gzip on;
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-enabled/*;
|
||||
}
|
||||
20
ansible/roles/trmm_dev/files/vimrc.local
Normal file
20
ansible/roles/trmm_dev/files/vimrc.local
Normal file
@@ -0,0 +1,20 @@
|
||||
" This file loads the default vim options at the beginning and prevents
|
||||
" that they are being loaded again later. All other options that will be set,
|
||||
" are added, or overwrite the default settings. Add as many options as you
|
||||
" whish at the end of this file.
|
||||
|
||||
" Load the defaults
|
||||
source $VIMRUNTIME/defaults.vim
|
||||
|
||||
" Prevent the defaults from being loaded again later, if the user doesn't
|
||||
" have a local vimrc (~/.vimrc)
|
||||
let skip_defaults_vim = 1
|
||||
|
||||
|
||||
" Set more options (overwrites settings from /usr/share/vim/vim80/defaults.vim)
|
||||
" Add as many options as you whish
|
||||
|
||||
" Set the mouse mode to 'r'
|
||||
if has('mouse')
|
||||
set mouse=r
|
||||
endif
|
||||
253
ansible/roles/trmm_dev/tasks/main.yml
Normal file
253
ansible/roles/trmm_dev/tasks/main.yml
Normal file
@@ -0,0 +1,253 @@
|
||||
---
|
||||
- name: set mouse mode for vim
|
||||
tags: vim
|
||||
become: yes
|
||||
ansible.builtin.copy:
|
||||
src: vimrc.local
|
||||
dest: /etc/vim/vimrc.local
|
||||
owner: "root"
|
||||
group: "root"
|
||||
mode: "0644"
|
||||
|
||||
- name: install base packages
|
||||
tags: base
|
||||
become: yes
|
||||
ansible.builtin.apt:
|
||||
pkg: "{{ item }}"
|
||||
state: present
|
||||
update_cache: yes
|
||||
with_items:
|
||||
- "{{ base_pkgs }}"
|
||||
|
||||
- name: install python prereqs
|
||||
tags: python
|
||||
become: yes
|
||||
ansible.builtin.apt:
|
||||
pkg: "{{ item }}"
|
||||
state: present
|
||||
with_items:
|
||||
- "{{ python_pkgs }}"
|
||||
|
||||
- name: get cpu core count
|
||||
tags: python
|
||||
ansible.builtin.command: nproc
|
||||
register: numprocs
|
||||
|
||||
- name: Create python tmpdir
|
||||
tags: python
|
||||
ansible.builtin.tempfile:
|
||||
state: directory
|
||||
suffix: python
|
||||
register: python_tmp
|
||||
|
||||
- name: download and extract python
|
||||
tags: python
|
||||
ansible.builtin.unarchive:
|
||||
src: "https://www.python.org/ftp/python/{{ python_ver }}/Python-{{ python_ver }}.tgz"
|
||||
dest: "{{ python_tmp.path }}"
|
||||
remote_src: yes
|
||||
|
||||
- name: compile python
|
||||
tags: python
|
||||
ansible.builtin.shell:
|
||||
chdir: "{{ python_tmp.path }}/Python-{{ python_ver }}"
|
||||
cmd: |
|
||||
./configure --enable-optimizations
|
||||
make -j {{ numprocs.stdout }}
|
||||
|
||||
- name: alt install python
|
||||
tags: python
|
||||
become: yes
|
||||
ansible.builtin.shell:
|
||||
chdir: "{{ python_tmp.path }}/Python-{{ python_ver }}"
|
||||
cmd: |
|
||||
make altinstall
|
||||
|
||||
- name: install nginx
|
||||
tags: nginx
|
||||
become: yes
|
||||
ansible.builtin.apt:
|
||||
pkg: nginx
|
||||
state: present
|
||||
|
||||
- name: set nginx default conf
|
||||
tags: nginx
|
||||
become: yes
|
||||
ansible.builtin.copy:
|
||||
src: nginx-default.conf
|
||||
dest: /etc/nginx/nginx.conf
|
||||
owner: "root"
|
||||
group: "root"
|
||||
mode: "0644"
|
||||
|
||||
- name: ensure nginx enabled and restarted
|
||||
tags: nginx
|
||||
become: yes
|
||||
ansible.builtin.service:
|
||||
name: nginx
|
||||
enabled: yes
|
||||
state: restarted
|
||||
|
||||
- name: create postgres repo
|
||||
tags: postgres
|
||||
become: yes
|
||||
ansible.builtin.copy:
|
||||
content: "deb http://apt.postgresql.org/pub/repos/apt bullseye-pgdg main"
|
||||
dest: /etc/apt/sources.list.d/pgdg.list
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0440"
|
||||
|
||||
- name: import postgres repo signing key
|
||||
tags: postgres
|
||||
become: yes
|
||||
ansible.builtin.apt_key:
|
||||
url: https://www.postgresql.org/media/keys/ACCC4CF8.asc
|
||||
state: present
|
||||
|
||||
- name: install postgresql
|
||||
tags: postgres
|
||||
become: yes
|
||||
ansible.builtin.apt:
|
||||
pkg: postgresql-14
|
||||
state: present
|
||||
update_cache: yes
|
||||
|
||||
- name: ensure postgres enabled and started
|
||||
tags: postgres
|
||||
become: yes
|
||||
ansible.builtin.service:
|
||||
name: postgresql
|
||||
enabled: yes
|
||||
state: started
|
||||
|
||||
- name: setup database
|
||||
tags: postgres
|
||||
become: yes
|
||||
become_user: postgres
|
||||
ansible.builtin.shell:
|
||||
cmd: |
|
||||
psql -c "CREATE DATABASE tacticalrmm"
|
||||
psql -c "CREATE USER {{ db_user }} WITH PASSWORD '{{ db_passwd }}'"
|
||||
psql -c "ALTER ROLE {{ db_user }} SET client_encoding TO 'utf8'"
|
||||
psql -c "ALTER ROLE {{ db_user }} SET default_transaction_isolation TO 'read committed'"
|
||||
psql -c "ALTER ROLE {{ db_user }} SET timezone TO 'UTC'"
|
||||
psql -c "ALTER ROLE {{ db_user }} CREATEDB"
|
||||
psql -c "GRANT ALL PRIVILEGES ON DATABASE tacticalrmm TO {{ db_user }}"
|
||||
|
||||
- name: create repo dirs
|
||||
become: yes
|
||||
tags: git
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ user }}"
|
||||
group: "{{ user }}"
|
||||
mode: "0755"
|
||||
with_items:
|
||||
- "{{ backend_dir }}"
|
||||
- "{{ frontend_dir }}"
|
||||
- "{{ scripts_dir }}"
|
||||
|
||||
- name: git clone repos
|
||||
tags: git
|
||||
ansible.builtin.git:
|
||||
repo: "{{ item.repo }}"
|
||||
dest: "{{ item.dest }}"
|
||||
version: "{{ item.version }}"
|
||||
with_items:
|
||||
- {
|
||||
repo: "{{ backend_repo }}",
|
||||
dest: "{{ backend_dir }}",
|
||||
version: develop,
|
||||
}
|
||||
- {
|
||||
repo: "{{ frontend_repo }}",
|
||||
dest: "{{ frontend_dir }}",
|
||||
version: develop,
|
||||
}
|
||||
- { repo: "{{ scripts_repo }}", dest: "{{ scripts_dir }}", version: main }
|
||||
|
||||
- name: get nats_server_ver
|
||||
tags: nats
|
||||
ansible.builtin.shell: grep "^NATS_SERVER_VER" {{ settings_file }} | awk -F'[= "]' '{print $5}'
|
||||
register: nats_server_ver
|
||||
|
||||
- name: Create nats tmpdir
|
||||
tags: nats
|
||||
ansible.builtin.tempfile:
|
||||
state: directory
|
||||
suffix: nats
|
||||
register: nats_tmp
|
||||
|
||||
- name: download and extract nats
|
||||
tags: nats
|
||||
ansible.builtin.unarchive:
|
||||
src: "https://github.com/nats-io/nats-server/releases/download/v{{ nats_server_ver.stdout }}/nats-server-v{{ nats_server_ver.stdout }}-linux-amd64.tar.gz"
|
||||
dest: "{{ nats_tmp.path }}"
|
||||
remote_src: yes
|
||||
|
||||
- name: install nats
|
||||
tags: nats
|
||||
become: yes
|
||||
ansible.builtin.copy:
|
||||
remote_src: yes
|
||||
src: "{{ nats_tmp.path }}/nats-server-v{{ nats_server_ver.stdout }}-linux-amd64/nats-server"
|
||||
dest: /usr/local/bin/nats-server
|
||||
owner: "{{ user }}"
|
||||
group: "{{ user }}"
|
||||
mode: "0755"
|
||||
|
||||
- name: Create nodejs tmpdir
|
||||
tags: nodejs
|
||||
ansible.builtin.tempfile:
|
||||
state: directory
|
||||
suffix: nodejs
|
||||
register: nodejs_tmp
|
||||
|
||||
- name: download nodejs setup
|
||||
tags: nodejs
|
||||
ansible.builtin.get_url:
|
||||
url: https://deb.nodesource.com/setup_16.x
|
||||
dest: "{{ nodejs_tmp.path }}/setup_node.sh"
|
||||
mode: "0755"
|
||||
|
||||
- name: run node setup script
|
||||
tags: nodejs
|
||||
become: yes
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ nodejs_tmp.path }}/setup_node.sh"
|
||||
|
||||
- name: install nodejs
|
||||
tags: nodejs
|
||||
become: yes
|
||||
ansible.builtin.apt:
|
||||
pkg: nodejs
|
||||
state: present
|
||||
update_cache: yes
|
||||
|
||||
- name: update npm
|
||||
tags: nodejs
|
||||
become: yes
|
||||
ansible.builtin.shell:
|
||||
cmd: npm install -g npm
|
||||
|
||||
- name: deploy django local settings
|
||||
tags: django
|
||||
ansible.builtin.template:
|
||||
src: local_settings.j2
|
||||
dest: "{{ local_settings_file }}"
|
||||
mode: "0644"
|
||||
owner: "{{ user }}"
|
||||
group: "{{ user }}"
|
||||
|
||||
- name: remove tempdirs
|
||||
tags: cleanup
|
||||
become: yes
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
with_items:
|
||||
- "{{ nats_tmp.path }}"
|
||||
- "{{ python_tmp.path }}"
|
||||
- "{{ nodejs_tmp.path }}"
|
||||
19
ansible/roles/trmm_dev/templates/local_settings.j2
Normal file
19
ansible/roles/trmm_dev/templates/local_settings.j2
Normal file
@@ -0,0 +1,19 @@
|
||||
SECRET_KEY = "{{ django_secret }}"
|
||||
DEBUG = True
|
||||
ALLOWED_HOSTS = ['{{ api }}']
|
||||
ADMIN_URL = "admin/"
|
||||
CORS_ORIGIN_WHITELIST = [
|
||||
"https://{{ rmm }}"
|
||||
]
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': 'tacticalrmm',
|
||||
'USER': '{{ db_user }}',
|
||||
'PASSWORD': '{{ db_passwd }}',
|
||||
'HOST': 'localhost',
|
||||
'PORT': '5432',
|
||||
}
|
||||
}
|
||||
REDIS_HOST = "localhost"
|
||||
ADMIN_ENABLED = True
|
||||
14
ansible/roles/trmm_dev/vars/main.yml
Normal file
14
ansible/roles/trmm_dev/vars/main.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
api: 'api.example.com'
|
||||
rmm: 'rmm.example.com'
|
||||
mesh: 'mesh.example.com'
|
||||
github_username: 'changeme'
|
||||
github_email: 'changeme@example.com'
|
||||
mesh_site: 'changeme'
|
||||
mesh_user: 'changeme'
|
||||
mesh_token: 'changeme'
|
||||
db_user: 'changeme'
|
||||
db_passwd: 'changeme'
|
||||
django_secret: 'changeme'
|
||||
|
||||
|
||||
6
ansible/setup_dev.yml
Normal file
6
ansible/setup_dev.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
- hosts: "{{ target }}"
|
||||
vars:
|
||||
ansible_user: tactical
|
||||
roles:
|
||||
- trmm_dev
|
||||
@@ -93,7 +93,7 @@ class LoginView(KnoxLoginView):
|
||||
login(request, user)
|
||||
|
||||
# save ip information
|
||||
client_ip, is_routable = get_client_ip(request)
|
||||
client_ip, _ = get_client_ip(request)
|
||||
user.last_login_ip = client_ip
|
||||
user.save()
|
||||
|
||||
|
||||
@@ -417,16 +417,20 @@ class TestAgentViews(TacticalTestCase):
|
||||
|
||||
@patch("agents.models.Agent.nats_cmd")
|
||||
def test_reboot_later(self, nats_cmd):
|
||||
nats_cmd.return_value = "ok"
|
||||
url = f"{base_url}/{self.agent.agent_id}/reboot/"
|
||||
|
||||
data = {
|
||||
"datetime": "2025-08-29T18:41:02",
|
||||
}
|
||||
# ensure we don't allow dates in past
|
||||
data = {"datetime": "2022-07-11T01:51:11"}
|
||||
r = self.client.patch(url, data, format="json")
|
||||
self.assertEqual(r.status_code, 400)
|
||||
self.assertEqual(r.data, "Date cannot be set in the past")
|
||||
|
||||
nats_cmd.return_value = "ok"
|
||||
# test with date in future
|
||||
data["datetime"] = "2027-08-29T18:41:02"
|
||||
r = self.client.patch(url, data, format="json")
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.data["time"], "August 29, 2025 at 06:41 PM")
|
||||
self.assertEqual(r.data["time"], "August 29, 2027 at 06:41 PM")
|
||||
self.assertEqual(r.data["agent"], self.agent.hostname)
|
||||
|
||||
nats_data = {
|
||||
@@ -439,12 +443,12 @@ class TestAgentViews(TacticalTestCase):
|
||||
"multiple_instances": 2,
|
||||
"trigger": "runonce",
|
||||
"name": r.data["task_name"],
|
||||
"start_year": 2025,
|
||||
"start_year": 2027,
|
||||
"start_month": 8,
|
||||
"start_day": 29,
|
||||
"start_hour": 18,
|
||||
"start_min": 41,
|
||||
"expire_year": 2025,
|
||||
"expire_year": 2027,
|
||||
"expire_month": 8,
|
||||
"expire_day": 29,
|
||||
"expire_hour": 18,
|
||||
|
||||
@@ -35,11 +35,12 @@ from tacticalrmm.constants import (
|
||||
AgentMonType,
|
||||
AgentPlat,
|
||||
CustomFieldModel,
|
||||
DebugLogType,
|
||||
EvtLogNames,
|
||||
PAAction,
|
||||
PAStatus,
|
||||
)
|
||||
from tacticalrmm.helpers import notify_error
|
||||
from tacticalrmm.helpers import date_is_in_past, notify_error
|
||||
from tacticalrmm.permissions import (
|
||||
_has_perm_on_agent,
|
||||
_has_perm_on_client,
|
||||
@@ -225,8 +226,14 @@ class GetUpdateDeleteAgent(APIView):
|
||||
mesh_id = agent.mesh_node_id
|
||||
agent.delete()
|
||||
reload_nats()
|
||||
uri = get_mesh_ws_url()
|
||||
asyncio.run(remove_mesh_agent(uri, mesh_id))
|
||||
try:
|
||||
uri = get_mesh_ws_url()
|
||||
asyncio.run(remove_mesh_agent(uri, mesh_id))
|
||||
except Exception as e:
|
||||
DebugLog.error(
|
||||
message=f"Unable to remove agent {name} from meshcentral database: {str(e)}",
|
||||
log_type=DebugLogType.AGENT_ISSUES,
|
||||
)
|
||||
return Response(f"{name} will now be uninstalled.")
|
||||
|
||||
|
||||
@@ -456,6 +463,9 @@ class Reboot(APIView):
|
||||
except Exception:
|
||||
return notify_error("Invalid date")
|
||||
|
||||
if date_is_in_past(datetime_obj=obj, agent_tz=agent.timezone):
|
||||
return notify_error("Date cannot be set in the past")
|
||||
|
||||
task_name = "TacticalRMM_SchedReboot_" + "".join(
|
||||
random.choice(string.ascii_letters) for _ in range(10)
|
||||
)
|
||||
|
||||
@@ -424,6 +424,7 @@ def status(request):
|
||||
|
||||
ret = {
|
||||
"version": settings.TRMM_VERSION,
|
||||
"latest_agent_version": settings.LATEST_AGENT_VER,
|
||||
"agent_count": Agent.objects.count(),
|
||||
"client_count": Client.objects.count(),
|
||||
"site_count": Site.objects.count(),
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import datetime as dt
|
||||
|
||||
import pytz
|
||||
from django.db.models.signals import post_init
|
||||
from django.dispatch import receiver
|
||||
from django.utils import timezone as djangotime
|
||||
|
||||
from tacticalrmm.constants import PAAction, PAStatus
|
||||
from tacticalrmm.helpers import date_is_in_past
|
||||
|
||||
from .models import PendingAction
|
||||
|
||||
@@ -22,14 +21,8 @@ def handle_status(sender, instance: PendingAction, **kwargs):
|
||||
reboot_time = dt.datetime.strptime(
|
||||
instance.details["time"], "%Y-%m-%d %H:%M:%S"
|
||||
)
|
||||
|
||||
# need to convert agent tz to UTC in order to compare
|
||||
agent_tz = pytz.timezone(instance.agent.timezone)
|
||||
localized = agent_tz.localize(reboot_time)
|
||||
|
||||
now = djangotime.now()
|
||||
reboot_time_utc = localized.astimezone(pytz.utc)
|
||||
|
||||
if now > reboot_time_utc:
|
||||
if date_is_in_past(
|
||||
datetime_obj=reboot_time, agent_tz=instance.agent.timezone
|
||||
):
|
||||
instance.status = PAStatus.COMPLETED
|
||||
instance.save(update_fields=["status"])
|
||||
|
||||
@@ -3,7 +3,7 @@ celery==5.2.7
|
||||
certifi==2022.6.15
|
||||
cffi==1.15.1
|
||||
channels==3.0.5
|
||||
channels_redis==3.4.0
|
||||
channels_redis==3.4.1
|
||||
chardet==4.0.0
|
||||
cryptography==37.0.4
|
||||
daphne==3.0.2
|
||||
@@ -14,7 +14,7 @@ django-rest-knox==4.2.0
|
||||
djangorestframework==3.13.1
|
||||
future==0.18.2
|
||||
msgpack==1.0.4
|
||||
nats-py==2.1.3
|
||||
nats-py==2.1.4
|
||||
psutil==5.9.1
|
||||
psycopg2-binary==2.9.3
|
||||
pycparser==2.21
|
||||
@@ -28,12 +28,12 @@ hiredis==2.0.0
|
||||
requests==2.28.1
|
||||
six==1.16.0
|
||||
sqlparse==0.4.2
|
||||
twilio==7.10.0
|
||||
urllib3==1.26.9
|
||||
twilio==7.12.0
|
||||
urllib3==1.26.11
|
||||
uWSGI==2.0.20
|
||||
validators==0.20.0
|
||||
vine==5.0.0
|
||||
websockets==10.3
|
||||
zipp==3.8.0
|
||||
zipp==3.8.1
|
||||
drf_spectacular==0.22.1
|
||||
meshctrl==0.1.15
|
||||
|
||||
@@ -17,6 +17,7 @@ from .serializers import (
|
||||
ScriptSnippetSerializer,
|
||||
ScriptTableSerializer,
|
||||
)
|
||||
from core.utils import clear_entire_cache
|
||||
|
||||
|
||||
class GetAddScripts(APIView):
|
||||
@@ -58,7 +59,7 @@ class GetUpdateDeleteScript(APIView):
|
||||
return Response(ScriptSerializer(script).data)
|
||||
|
||||
def put(self, request, pk):
|
||||
script = get_object_or_404(Script, pk=pk)
|
||||
script = get_object_or_404(Script.objects.prefetch_related("script"), pk=pk)
|
||||
|
||||
data = request.data
|
||||
|
||||
@@ -76,7 +77,12 @@ class GetUpdateDeleteScript(APIView):
|
||||
serializer.is_valid(raise_exception=True)
|
||||
obj = serializer.save()
|
||||
|
||||
# obj.hash_script_body()
|
||||
# TODO rename the related field from 'script' to 'scriptchecks' so it's not so confusing
|
||||
if script.script.exists():
|
||||
for script_check in script.script.all():
|
||||
if script_check.policy:
|
||||
clear_entire_cache()
|
||||
break
|
||||
|
||||
return Response(f"{obj.name} was edited!")
|
||||
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.utils import timezone as djangotime
|
||||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def get_certs() -> tuple[str, str]:
|
||||
domain = settings.ALLOWED_HOSTS[0].split(".", 1)[1]
|
||||
@@ -27,3 +34,15 @@ def get_nats_ports() -> tuple[int, int]:
|
||||
nats_websocket_port = getattr(settings, "NATS_WEBSOCKET_PORT", 9235)
|
||||
|
||||
return (nats_standard_port, nats_websocket_port)
|
||||
|
||||
|
||||
def date_is_in_past(*, datetime_obj: "datetime", agent_tz: str) -> bool:
|
||||
"""
|
||||
datetime_obj must be a naive datetime
|
||||
"""
|
||||
now = djangotime.now()
|
||||
# convert agent tz to UTC to compare
|
||||
agent_pytz = pytz.timezone(agent_tz)
|
||||
localized = agent_pytz.localize(datetime_obj)
|
||||
utc_time = localized.astimezone(pytz.utc)
|
||||
return now > utc_time
|
||||
|
||||
@@ -17,19 +17,19 @@ LINUX_AGENT_SCRIPT = BASE_DIR / "core" / "agent_linux.sh"
|
||||
AUTH_USER_MODEL = "accounts.User"
|
||||
|
||||
# latest release
|
||||
TRMM_VERSION = "0.14.0"
|
||||
TRMM_VERSION = "0.14.3"
|
||||
|
||||
# https://github.com/amidaware/tacticalrmm-web
|
||||
WEB_VERSION = "0.100.4"
|
||||
WEB_VERSION = "0.100.6"
|
||||
|
||||
# bump this version everytime vue code is changed
|
||||
# to alert user they need to manually refresh their browser
|
||||
APP_VER = "0.0.165"
|
||||
APP_VER = "0.0.167"
|
||||
|
||||
# https://github.com/amidaware/rmmagent
|
||||
LATEST_AGENT_VER = "2.1.0"
|
||||
LATEST_AGENT_VER = "2.1.2"
|
||||
|
||||
MESH_VER = "1.0.43"
|
||||
MESH_VER = "1.0.60"
|
||||
|
||||
NATS_SERVER_VER = "2.8.4"
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
set -e
|
||||
|
||||
: "${WORKER_CONNECTIONS:=2048}"
|
||||
: "${WORKER_CONNECTIONS:=4096}"
|
||||
: "${APP_PORT:=8080}"
|
||||
: "${API_PORT:=8080}"
|
||||
: "${NGINX_RESOLVER:=127.0.0.11}"
|
||||
@@ -31,9 +31,12 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
nginxdefaultconf='/etc/nginx/nginx.conf'
|
||||
# increase default nginx worker connections
|
||||
/bin/bash -c "sed -i 's/worker_connections.*/worker_connections ${WORKER_CONNECTIONS};/g' /etc/nginx/nginx.conf"
|
||||
/bin/bash -c "sed -i 's/worker_connections.*/worker_connections ${WORKER_CONNECTIONS};/g' $nginxdefaultconf"
|
||||
|
||||
sed -i '1s/^/worker_rlimit_nofile 1000000;\
|
||||
/' $nginxdefaultconf
|
||||
|
||||
if [[ $DEV -eq 1 ]]; then
|
||||
API_NGINX="
|
||||
@@ -113,7 +116,7 @@ server {
|
||||
|
||||
client_max_body_size 300M;
|
||||
|
||||
listen 4443 ssl;
|
||||
listen 4443 ssl reuseport;
|
||||
ssl_certificate ${CERT_PUB_PATH};
|
||||
ssl_certificate_key ${CERT_PRIV_PATH};
|
||||
|
||||
|
||||
@@ -86,7 +86,6 @@ services:
|
||||
API_HOST: ${API_HOST}
|
||||
ports:
|
||||
- "4222:4222"
|
||||
- "9235:9235"
|
||||
volumes:
|
||||
- tactical_data:/opt/tactical
|
||||
networks:
|
||||
|
||||
53
install.sh
53
install.sh
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_VERSION="64"
|
||||
SCRIPT_VERSION="66"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/install.sh'
|
||||
|
||||
sudo apt install -y curl wget dirmngr gnupg lsb-release
|
||||
@@ -172,10 +172,55 @@ sudo chmod 775 -R /etc/letsencrypt
|
||||
|
||||
print_green 'Installing Nginx'
|
||||
|
||||
wget -qO - https://nginx.org/packages/keys/nginx_signing.key | sudo apt-key add -
|
||||
|
||||
nginxrepo="$(cat << EOF
|
||||
deb https://nginx.org/packages/$osname/ $codename nginx
|
||||
deb-src https://nginx.org/packages/$osname/ $codename nginx
|
||||
EOF
|
||||
)"
|
||||
echo "${nginxrepo}" | sudo tee /etc/apt/sources.list.d/nginx.list > /dev/null
|
||||
|
||||
sudo apt update
|
||||
sudo apt install -y nginx
|
||||
sudo systemctl stop nginx
|
||||
sudo sed -i 's/worker_connections.*/worker_connections 2048;/g' /etc/nginx/nginx.conf
|
||||
sudo sed -i 's/# server_names_hash_bucket_size.*/server_names_hash_bucket_size 64;/g' /etc/nginx/nginx.conf
|
||||
|
||||
nginxdefaultconf='/etc/nginx/nginx.conf'
|
||||
|
||||
nginxconf="$(cat << EOF
|
||||
worker_rlimit_nofile 1000000;
|
||||
user www-data;
|
||||
worker_processes auto;
|
||||
pid /run/nginx.pid;
|
||||
include /etc/nginx/modules-enabled/*.conf;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
}
|
||||
|
||||
http {
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
types_hash_max_size 2048;
|
||||
server_names_hash_bucket_size 64;
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_prefer_server_ciphers on;
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
gzip on;
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-enabled/*;
|
||||
}
|
||||
EOF
|
||||
)"
|
||||
echo "${nginxconf}" | sudo tee $nginxdefaultconf > /dev/null
|
||||
|
||||
for i in sites-available sites-enabled
|
||||
do
|
||||
sudo mkdir -p /etc/nginx/$i
|
||||
done
|
||||
|
||||
print_green 'Installing NodeJS'
|
||||
|
||||
@@ -515,7 +560,7 @@ server {
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen 443 ssl reuseport;
|
||||
listen [::]:443 ssl;
|
||||
server_name ${rmmdomain};
|
||||
client_max_body_size 300M;
|
||||
|
||||
14
restore.sh
14
restore.sh
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_VERSION="38"
|
||||
SCRIPT_VERSION="40"
|
||||
SCRIPT_URL='https://raw.githubusercontent.com/amidaware/tacticalrmm/master/restore.sh'
|
||||
|
||||
sudo apt update
|
||||
@@ -121,12 +121,22 @@ sudo npm install -g npm
|
||||
|
||||
print_green 'Restoring Nginx'
|
||||
|
||||
wget -qO - https://nginx.org/packages/keys/nginx_signing.key | sudo apt-key add -
|
||||
|
||||
nginxrepo="$(cat << EOF
|
||||
deb https://nginx.org/packages/$osname/ $codename nginx
|
||||
deb-src https://nginx.org/packages/$osname/ $codename nginx
|
||||
EOF
|
||||
)"
|
||||
echo "${nginxrepo}" | sudo tee /etc/apt/sources.list.d/nginx.list > /dev/null
|
||||
|
||||
sudo apt update
|
||||
sudo apt install -y nginx
|
||||
sudo systemctl stop nginx
|
||||
sudo rm -rf /etc/nginx
|
||||
sudo mkdir /etc/nginx
|
||||
sudo tar -xzf $tmp_dir/nginx/etc-nginx.tar.gz -C /etc/nginx
|
||||
sudo sed -i 's/worker_connections.*/worker_connections 2048;/g' /etc/nginx/nginx.conf
|
||||
|
||||
rmmdomain=$(grep server_name /etc/nginx/sites-available/rmm.conf | grep -v 301 | head -1 | tr -d " \t" | sed 's/.*server_name//' | tr -d ';')
|
||||
frontenddomain=$(grep server_name /etc/nginx/sites-available/frontend.conf | grep -v 301 | head -1 | tr -d " \t" | sed 's/.*server_name//' | tr -d ';')
|
||||
meshdomain=$(grep server_name /etc/nginx/sites-available/meshcentral.conf | grep -v 301 | head -1 | tr -d " \t" | sed 's/.*server_name//' | tr -d ';')
|
||||
|
||||
59
update.sh
59
update.sh
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_VERSION="136"
|
||||
SCRIPT_VERSION="138"
|
||||
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'
|
||||
YELLOW='\033[1;33m'
|
||||
@@ -120,13 +120,6 @@ if ! [[ $CHECK_NATS_WEBSOCKET ]]; then
|
||||
' $rmmconf)" | sudo tee $rmmconf > /dev/null
|
||||
fi
|
||||
|
||||
if ! sudo nginx -t > /dev/null 2>&1; then
|
||||
sudo nginx -t
|
||||
echo -ne "\n"
|
||||
echo -ne "${RED}You have syntax errors in your nginx configs. See errors above. Please fix them and re-run this script.${NC}\n"
|
||||
echo -ne "${RED}Aborting...${NC}\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for i in nginx nats-api nats rmm daphne celery celerybeat
|
||||
do
|
||||
@@ -165,13 +158,53 @@ EOF
|
||||
)"
|
||||
echo "${uwsgini}" > /rmm/api/tacticalrmm/app.ini
|
||||
|
||||
CHECK_NGINX_WORKER_CONN=$(grep "worker_connections 2048" /etc/nginx/nginx.conf)
|
||||
if ! [[ $CHECK_NGINX_WORKER_CONN ]]; then
|
||||
printf >&2 "${GREEN}Changing nginx worker connections to 2048${NC}\n"
|
||||
sudo sed -i 's/worker_connections.*/worker_connections 2048;/g' /etc/nginx/nginx.conf
|
||||
|
||||
if [ ! -f /etc/apt/sources.list.d/nginx.list ]; then
|
||||
osname=$(lsb_release -si); osname=${osname^}
|
||||
osname=$(echo "$osname" | tr '[A-Z]' '[a-z]')
|
||||
codename=$(lsb_release -sc)
|
||||
nginxrepo="$(cat << EOF
|
||||
deb https://nginx.org/packages/$osname/ $codename nginx
|
||||
deb-src https://nginx.org/packages/$osname/ $codename nginx
|
||||
EOF
|
||||
)"
|
||||
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 -
|
||||
sudo apt update
|
||||
sudo apt install -y nginx
|
||||
fi
|
||||
|
||||
sudo sed -i 's/# server_names_hash_bucket_size.*/server_names_hash_bucket_size 64;/g' /etc/nginx/nginx.conf
|
||||
nginxdefaultconf='/etc/nginx/nginx.conf'
|
||||
CHECK_NGINX_WORKER_CONN=$(grep "worker_connections 4096" $nginxdefaultconf)
|
||||
if ! [[ $CHECK_NGINX_WORKER_CONN ]]; then
|
||||
printf >&2 "${GREEN}Changing nginx worker connections to 4096${NC}\n"
|
||||
sudo sed -i 's/worker_connections.*/worker_connections 4096;/g' $nginxdefaultconf
|
||||
fi
|
||||
|
||||
CHECK_NGINX_NOLIMIT=$(grep "worker_rlimit_nofile 1000000" $nginxdefaultconf)
|
||||
if ! [[ $CHECK_NGINX_NOLIMIT ]]; then
|
||||
sudo sed -i '/worker_rlimit_nofile.*/d' $nginxdefaultconf
|
||||
printf >&2 "${GREEN}Increasing nginx open file limit${NC}\n"
|
||||
sudo sed -i '1s/^/worker_rlimit_nofile 1000000;\
|
||||
/' $nginxdefaultconf
|
||||
fi
|
||||
|
||||
backend_conf='/etc/nginx/sites-available/rmm.conf'
|
||||
CHECK_NGINX_REUSEPORT=$(grep reuseport $backend_conf)
|
||||
if ! [[ $CHECK_NGINX_REUSEPORT ]]; then
|
||||
printf >&2 "${GREEN}Setting nginx reuseport${NC}\n"
|
||||
sudo sed -i 's/listen 443 ssl;/listen 443 ssl reuseport;/g' $backend_conf
|
||||
fi
|
||||
|
||||
sudo sed -i 's/# server_names_hash_bucket_size.*/server_names_hash_bucket_size 64;/g' $nginxdefaultconf
|
||||
|
||||
if ! sudo nginx -t > /dev/null 2>&1; then
|
||||
sudo nginx -t
|
||||
echo -ne "\n"
|
||||
echo -ne "${RED}You have syntax errors in your nginx configs. See errors above. Please fix them and re-run this script.${NC}\n"
|
||||
echo -ne "${RED}Aborting...${NC}\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
HAS_PY310=$(python3.10 --version | grep ${PYTHON_VER})
|
||||
if ! [[ $HAS_PY310 ]]; then
|
||||
|
||||
Reference in New Issue
Block a user