Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72126052ad | ||
|
|
75d9f6a7e7 | ||
|
|
de677294c6 | ||
|
|
da1e6b8259 | ||
|
|
a9633b3990 | ||
|
|
7ac9af1cc1 | ||
|
|
00d8b8cd61 | ||
|
|
af7ff7f5cf | ||
|
|
2a20719130 | ||
|
|
f481940180 | ||
|
|
ca8824d1e3 | ||
|
|
f4be199b77 | ||
|
|
6bcef8334e | ||
|
|
3955eff683 | ||
|
|
aa0f6ecd75 | ||
|
|
ef4a94ed78 | ||
|
|
b5c803ce65 | ||
|
|
d4325ed82e | ||
|
|
3805fb8f26 | ||
|
|
d4d938c655 | ||
|
|
1c6911e361 | ||
|
|
a1b364f337 | ||
|
|
ece5c3da86 | ||
|
|
5d1ae6047b | ||
|
|
5605c72253 | ||
|
|
66bbcf0733 | ||
|
|
acc23ea7bb | ||
|
|
663bd0c9f0 | ||
|
|
39b1025dfa | ||
|
|
d2875e90b2 | ||
|
|
ff461d1d02 | ||
|
|
58164ea2d3 | ||
|
|
1bf4834004 | ||
|
|
bf58d78281 |
@@ -1,11 +1,11 @@
|
|||||||
# pulls community scripts from git repo
|
# pulls community scripts from git repo
|
||||||
FROM python:3.10-slim AS GET_SCRIPTS_STAGE
|
FROM python:3.10.6-slim AS GET_SCRIPTS_STAGE
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y --no-install-recommends git && \
|
apt-get install -y --no-install-recommends git && \
|
||||||
git clone https://github.com/amidaware/community-scripts.git /community-scripts
|
git clone https://github.com/amidaware/community-scripts.git /community-scripts
|
||||||
|
|
||||||
FROM python:3.10-slim
|
FROM python:3.10.6-slim
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -22,22 +22,6 @@ services:
|
|||||||
aliases:
|
aliases:
|
||||||
- tactical-backend
|
- tactical-backend
|
||||||
|
|
||||||
app-dev:
|
|
||||||
container_name: trmm-app-dev
|
|
||||||
image: node:16-alpine
|
|
||||||
restart: always
|
|
||||||
command: /bin/sh -c "npm install --cache ~/.npm && npm run serve"
|
|
||||||
user: 1000:1000
|
|
||||||
working_dir: /workspace/web
|
|
||||||
volumes:
|
|
||||||
- ..:/workspace:cached
|
|
||||||
ports:
|
|
||||||
- "8080:${APP_PORT}"
|
|
||||||
networks:
|
|
||||||
dev:
|
|
||||||
aliases:
|
|
||||||
- tactical-frontend
|
|
||||||
|
|
||||||
# nats
|
# nats
|
||||||
nats-dev:
|
nats-dev:
|
||||||
container_name: trmm-nats-dev
|
container_name: trmm-nats-dev
|
||||||
|
|||||||
@@ -15,10 +15,7 @@ set -e
|
|||||||
: "${MESH_PASS:=meshcentralpass}"
|
: "${MESH_PASS:=meshcentralpass}"
|
||||||
: "${MESH_HOST:=tactical-meshcentral}"
|
: "${MESH_HOST:=tactical-meshcentral}"
|
||||||
: "${API_HOST:=tactical-backend}"
|
: "${API_HOST:=tactical-backend}"
|
||||||
: "${APP_HOST:=tactical-frontend}"
|
|
||||||
: "${REDIS_HOST:=tactical-redis}"
|
: "${REDIS_HOST:=tactical-redis}"
|
||||||
: "${HTTP_PROTOCOL:=http}"
|
|
||||||
: "${APP_PORT:=8080}"
|
|
||||||
: "${API_PORT:=8000}"
|
: "${API_PORT:=8000}"
|
||||||
|
|
||||||
: "${CERT_PRIV_PATH:=${TACTICAL_DIR}/certs/privkey.pem}"
|
: "${CERT_PRIV_PATH:=${TACTICAL_DIR}/certs/privkey.pem}"
|
||||||
@@ -142,16 +139,6 @@ if [ "$1" = 'tactical-init-dev' ]; then
|
|||||||
|
|
||||||
django_setup
|
django_setup
|
||||||
|
|
||||||
# create .env file for frontend
|
|
||||||
webenv="$(cat << EOF
|
|
||||||
PROD_URL = "${HTTP_PROTOCOL}://${API_HOST}"
|
|
||||||
DEV_URL = "${HTTP_PROTOCOL}://${API_HOST}"
|
|
||||||
DEV_PORT = ${APP_PORT}
|
|
||||||
DOCKER_BUILD = 1
|
|
||||||
EOF
|
|
||||||
)"
|
|
||||||
echo "${webenv}" | tee "${WORKSPACE_DIR}"/web/.env > /dev/null
|
|
||||||
|
|
||||||
# chown everything to tactical user
|
# chown everything to tactical user
|
||||||
chown -R "${TACTICAL_USER}":"${TACTICAL_USER}" "${WORKSPACE_DIR}"
|
chown -R "${TACTICAL_USER}":"${TACTICAL_USER}" "${WORKSPACE_DIR}"
|
||||||
chown -R "${TACTICAL_USER}":"${TACTICAL_USER}" "${TACTICAL_DIR}"
|
chown -R "${TACTICAL_USER}":"${TACTICAL_USER}" "${TACTICAL_DIR}"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
-r ../api/tacticalrmm/requirements.txt
|
-r /workspace/api/tacticalrmm/requirements.txt
|
||||||
-r ../api/tacticalrmm/requirements-dev.txt
|
-r /workspace/api/tacticalrmm/requirements-dev.txt
|
||||||
-r ../api/tacticalrmm/requirements-test.txt
|
-r /workspace/api/tacticalrmm/requirements-test.txt
|
||||||
|
|||||||
2
.github/workflows/ci-tests.yml
vendored
2
.github/workflows/ci-tests.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
name: Tests
|
name: Tests
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.10.4"]
|
python-version: ["3.10.6"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -55,3 +55,4 @@ coverage.lcov
|
|||||||
daphne.sock.lock
|
daphne.sock.lock
|
||||||
.pytest_cache
|
.pytest_cache
|
||||||
coverage.xml
|
coverage.xml
|
||||||
|
setup_dev.yml
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
---
|
---
|
||||||
user: "tactical"
|
user: "tactical"
|
||||||
python_ver: "3.10.4"
|
python_ver: "3.10.6"
|
||||||
|
go_ver: "1.18.5"
|
||||||
backend_repo: "https://github.com/amidaware/tacticalrmm.git"
|
backend_repo: "https://github.com/amidaware/tacticalrmm.git"
|
||||||
frontend_repo: "https://github.com/amidaware/tacticalrmm-web.git"
|
frontend_repo: "https://github.com/amidaware/tacticalrmm-web.git"
|
||||||
scripts_repo: "https://github.com/amidaware/community-scripts.git"
|
scripts_repo: "https://github.com/amidaware/community-scripts.git"
|
||||||
backend_dir: "/opt/trmm"
|
backend_dir: "/opt/trmm"
|
||||||
frontend_dir: "/opt/trmm-web"
|
frontend_dir: "/opt/trmm-web"
|
||||||
scripts_dir: "/opt/community-scripts"
|
scripts_dir: "/opt/trmm-community-scripts"
|
||||||
trmm_dir: "/opt/trmm/api/tacticalrmm/tacticalrmm"
|
trmm_dir: "{{ backend_dir }}/api/tacticalrmm/tacticalrmm"
|
||||||
|
mesh_dir: "/opt/meshcentral"
|
||||||
settings_file: "{{ trmm_dir }}/settings.py"
|
settings_file: "{{ trmm_dir }}/settings.py"
|
||||||
local_settings_file: "{{ trmm_dir }}/local_settings.py"
|
local_settings_file: "{{ trmm_dir }}/local_settings.py"
|
||||||
|
fullchain_dest: /etc/ssl/certs/fullchain.pem
|
||||||
|
privkey_dest: /etc/ssl/certs/privkey.pem
|
||||||
|
|
||||||
base_pkgs:
|
base_pkgs:
|
||||||
- build-essential
|
- build-essential
|
||||||
@@ -22,7 +26,6 @@ base_pkgs:
|
|||||||
- g++
|
- g++
|
||||||
- make
|
- make
|
||||||
- ca-certificates
|
- ca-certificates
|
||||||
- redis
|
|
||||||
- git
|
- git
|
||||||
|
|
||||||
python_pkgs:
|
python_pkgs:
|
||||||
|
|||||||
@@ -5,18 +5,24 @@ pid /run/nginx.pid;
|
|||||||
include /etc/nginx/modules-enabled/*.conf;
|
include /etc/nginx/modules-enabled/*.conf;
|
||||||
|
|
||||||
events {
|
events {
|
||||||
worker_connections 2048;
|
worker_connections 4096;
|
||||||
}
|
}
|
||||||
|
|
||||||
http {
|
http {
|
||||||
sendfile on;
|
sendfile on;
|
||||||
|
server_tokens off;
|
||||||
tcp_nopush on;
|
tcp_nopush on;
|
||||||
types_hash_max_size 2048;
|
types_hash_max_size 2048;
|
||||||
server_names_hash_bucket_size 64;
|
server_names_hash_bucket_size 64;
|
||||||
include /etc/nginx/mime.types;
|
include /etc/nginx/mime.types;
|
||||||
default_type application/octet-stream;
|
default_type application/octet-stream;
|
||||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
ssl_prefer_server_ciphers on;
|
ssl_prefer_server_ciphers on;
|
||||||
|
ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
|
||||||
|
ssl_ecdh_curve secp384r1;
|
||||||
|
ssl_stapling on;
|
||||||
|
ssl_stapling_verify on;
|
||||||
|
add_header X-Content-Type-Options nosniff;
|
||||||
access_log /var/log/nginx/access.log;
|
access_log /var/log/nginx/access.log;
|
||||||
error_log /var/log/nginx/error.log;
|
error_log /var/log/nginx/error.log;
|
||||||
gzip on;
|
gzip on;
|
||||||
|
|||||||
2
ansible/roles/trmm_dev/files/nginx.repo
Normal file
2
ansible/roles/trmm_dev/files/nginx.repo
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
deb https://nginx.org/packages/debian/ bullseye nginx
|
||||||
|
deb-src https://nginx.org/packages/debian/ bullseye nginx
|
||||||
@@ -9,6 +9,19 @@
|
|||||||
group: "root"
|
group: "root"
|
||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: set max_user_watches
|
||||||
|
tags: sysctl
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /etc/sysctl.conf
|
||||||
|
line: fs.inotify.max_user_watches=524288
|
||||||
|
|
||||||
|
- name: reload sysctl
|
||||||
|
tags: sysctl
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: sysctl -p
|
||||||
|
|
||||||
- name: install base packages
|
- name: install base packages
|
||||||
tags: base
|
tags: base
|
||||||
become: yes
|
become: yes
|
||||||
@@ -19,6 +32,21 @@
|
|||||||
with_items:
|
with_items:
|
||||||
- "{{ base_pkgs }}"
|
- "{{ base_pkgs }}"
|
||||||
|
|
||||||
|
- name: download and install golang
|
||||||
|
tags: golang
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.unarchive:
|
||||||
|
src: "https://go.dev/dl/go{{ go_ver }}.linux-amd64.tar.gz"
|
||||||
|
dest: /usr/local
|
||||||
|
remote_src: yes
|
||||||
|
|
||||||
|
- name: add golang to path
|
||||||
|
become: yes
|
||||||
|
tags: golang
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/profile.d/golang.sh
|
||||||
|
content: "PATH=$PATH:/usr/local/go/bin"
|
||||||
|
|
||||||
- name: install python prereqs
|
- name: install python prereqs
|
||||||
tags: python
|
tags: python
|
||||||
become: yes
|
become: yes
|
||||||
@@ -63,31 +91,13 @@
|
|||||||
cmd: |
|
cmd: |
|
||||||
make altinstall
|
make altinstall
|
||||||
|
|
||||||
- name: install nginx
|
- name: install redis
|
||||||
tags: nginx
|
tags: redis
|
||||||
become: yes
|
become: yes
|
||||||
ansible.builtin.apt:
|
ansible.builtin.apt:
|
||||||
pkg: nginx
|
pkg: redis
|
||||||
state: present
|
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
|
- name: create postgres repo
|
||||||
tags: postgres
|
tags: postgres
|
||||||
become: yes
|
become: yes
|
||||||
@@ -96,7 +106,7 @@
|
|||||||
dest: /etc/apt/sources.list.d/pgdg.list
|
dest: /etc/apt/sources.list.d/pgdg.list
|
||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: "0440"
|
mode: "0644"
|
||||||
|
|
||||||
- name: import postgres repo signing key
|
- name: import postgres repo signing key
|
||||||
tags: postgres
|
tags: postgres
|
||||||
@@ -232,6 +242,200 @@
|
|||||||
ansible.builtin.shell:
|
ansible.builtin.shell:
|
||||||
cmd: npm install -g npm
|
cmd: npm install -g npm
|
||||||
|
|
||||||
|
- name: install quasar cli
|
||||||
|
tags: quasar
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.shell:
|
||||||
|
cmd: npm install -g @quasar/cli
|
||||||
|
|
||||||
|
- name: install frontend
|
||||||
|
tags: quasar
|
||||||
|
ansible.builtin.shell:
|
||||||
|
chdir: "{{ frontend_dir }}"
|
||||||
|
cmd: npm install
|
||||||
|
|
||||||
|
- name: add quasar env
|
||||||
|
tags: quasar
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: quasar.env.j2
|
||||||
|
dest: "{{ frontend_dir }}/.env"
|
||||||
|
owner: "{{ user }}"
|
||||||
|
group: "{{ user }}"
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: remove tempdirs
|
||||||
|
tags: cleanup
|
||||||
|
become: yes
|
||||||
|
ignore_errors: yes
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: absent
|
||||||
|
with_items:
|
||||||
|
- "{{ nats_tmp.path }}"
|
||||||
|
- "{{ python_tmp.path }}"
|
||||||
|
- "{{ nodejs_tmp.path }}"
|
||||||
|
|
||||||
|
- name: deploy fullchain
|
||||||
|
tags: certs
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "{{ fullchain_src }}"
|
||||||
|
dest: "{{ fullchain_dest }}"
|
||||||
|
owner: "{{ user }}"
|
||||||
|
group: "{{ user }}"
|
||||||
|
mode: "0440"
|
||||||
|
|
||||||
|
- name: deploy privkey
|
||||||
|
tags: certs
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "{{ privkey_src }}"
|
||||||
|
dest: "{{ privkey_dest }}"
|
||||||
|
owner: "{{ user }}"
|
||||||
|
group: "{{ user }}"
|
||||||
|
mode: "0440"
|
||||||
|
|
||||||
|
- name: import nginx signing key
|
||||||
|
tags: nginx
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.apt_key:
|
||||||
|
url: https://nginx.org/packages/keys/nginx_signing.key
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: add nginx repo
|
||||||
|
tags: nginx
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: nginx.repo
|
||||||
|
dest: /etc/apt/sources.list.d/nginx.list
|
||||||
|
owner: "root"
|
||||||
|
group: "root"
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: install nginx
|
||||||
|
tags: nginx
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.apt:
|
||||||
|
pkg: nginx
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- 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: create nginx dirs
|
||||||
|
become: yes
|
||||||
|
tags: nginx
|
||||||
|
ansible.builtin.file:
|
||||||
|
state: directory
|
||||||
|
path: "{{ item }}"
|
||||||
|
mode: "0755"
|
||||||
|
with_items:
|
||||||
|
- /etc/nginx/sites-available
|
||||||
|
- /etc/nginx/sites-enabled
|
||||||
|
|
||||||
|
- name: deploy nginx sites
|
||||||
|
become: yes
|
||||||
|
tags: nginx
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "{{ item.src }}"
|
||||||
|
dest: "{{ item.dest }}"
|
||||||
|
mode: "0644"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
with_items:
|
||||||
|
- { src: backend.nginx.j2, dest: /etc/nginx/sites-available/backend.conf }
|
||||||
|
- { src: mesh.nginx.j2, dest: /etc/nginx/sites-available/mesh.conf }
|
||||||
|
|
||||||
|
- name: enable nginx sites
|
||||||
|
become: yes
|
||||||
|
tags: nginx
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: "{{ item.src }}"
|
||||||
|
dest: "{{ item.dest }}"
|
||||||
|
mode: "0644"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
state: link
|
||||||
|
with_items:
|
||||||
|
- {
|
||||||
|
src: /etc/nginx/sites-available/backend.conf,
|
||||||
|
dest: /etc/nginx/sites-enabled/backend.conf,
|
||||||
|
}
|
||||||
|
- {
|
||||||
|
src: /etc/nginx/sites-available/mesh.conf,
|
||||||
|
dest: /etc/nginx/sites-enabled/mesh.conf,
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: ensure nginx enabled and restarted
|
||||||
|
tags: nginx
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: nginx
|
||||||
|
enabled: yes
|
||||||
|
state: restarted
|
||||||
|
|
||||||
|
- name: copy nats-api bin
|
||||||
|
tags: nats-api
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.copy:
|
||||||
|
remote_src: yes
|
||||||
|
src: "{{ backend_dir }}/natsapi/bin/nats-api"
|
||||||
|
dest: /usr/local/bin/nats-api
|
||||||
|
owner: "{{ user }}"
|
||||||
|
group: "{{ user }}"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: get setuptools_ver
|
||||||
|
tags: pip
|
||||||
|
ansible.builtin.shell: grep "^SETUPTOOLS_VER" {{ settings_file }} | awk -F'[= "]' '{print $5}'
|
||||||
|
register: setuptools_ver
|
||||||
|
|
||||||
|
- name: get wheel_ver
|
||||||
|
tags: pip
|
||||||
|
ansible.builtin.shell: grep "^WHEEL_VER" {{ settings_file }} | awk -F'[= "]' '{print $5}'
|
||||||
|
register: wheel_ver
|
||||||
|
|
||||||
|
- name: setup virtual env
|
||||||
|
tags: pip
|
||||||
|
ansible.builtin.shell:
|
||||||
|
chdir: "{{ backend_dir }}/api"
|
||||||
|
cmd: python3.10 -m venv env
|
||||||
|
|
||||||
|
- name: update pip to latest
|
||||||
|
tags: pip
|
||||||
|
ansible.builtin.pip:
|
||||||
|
virtualenv: "{{ backend_dir }}/api/env"
|
||||||
|
name: pip
|
||||||
|
state: latest
|
||||||
|
|
||||||
|
- name: install setuptools and wheel
|
||||||
|
tags: pip
|
||||||
|
ansible.builtin.pip:
|
||||||
|
virtualenv: "{{ backend_dir }}/api/env"
|
||||||
|
name: "{{ item }}"
|
||||||
|
with_items:
|
||||||
|
- "setuptools=={{ setuptools_ver.stdout }}"
|
||||||
|
- "wheel=={{ wheel_ver.stdout }}"
|
||||||
|
|
||||||
|
- name: install python packages
|
||||||
|
tags: pip
|
||||||
|
ansible.builtin.pip:
|
||||||
|
virtualenv: "{{ backend_dir }}/api/env"
|
||||||
|
chdir: "{{ backend_dir }}/api/tacticalrmm"
|
||||||
|
requirements: "{{ item }}"
|
||||||
|
with_items:
|
||||||
|
- requirements.txt
|
||||||
|
- requirements-dev.txt
|
||||||
|
- requirements-test.txt
|
||||||
|
|
||||||
- name: deploy django local settings
|
- name: deploy django local settings
|
||||||
tags: django
|
tags: django
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
@@ -241,13 +445,189 @@
|
|||||||
owner: "{{ user }}"
|
owner: "{{ user }}"
|
||||||
group: "{{ user }}"
|
group: "{{ user }}"
|
||||||
|
|
||||||
- name: remove tempdirs
|
- name: setup django
|
||||||
tags: cleanup
|
tags: django
|
||||||
|
ansible.builtin.shell:
|
||||||
|
chdir: "{{ backend_dir }}/api/tacticalrmm"
|
||||||
|
cmd: |
|
||||||
|
. ../env/bin/activate
|
||||||
|
python manage.py migrate --no-input
|
||||||
|
python manage.py collectstatic --no-input
|
||||||
|
python manage.py create_natsapi_conf
|
||||||
|
python manage.py load_chocos
|
||||||
|
python manage.py load_community_scripts
|
||||||
|
echo "from accounts.models import User; User.objects.create_superuser('{{ django_user }}', '{{ github_email }}', '{{ django_password }}') if not User.objects.filter(username='{{ django_user }}').exists() else 0;" | python manage.py shell
|
||||||
|
python manage.py create_installer_user
|
||||||
|
|
||||||
|
- name: deploy services
|
||||||
|
tags: services
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "{{ item.src }}"
|
||||||
|
dest: "{{ item.dest }}"
|
||||||
|
mode: "0644"
|
||||||
|
owner: "root"
|
||||||
|
group: "root"
|
||||||
|
with_items:
|
||||||
|
- { src: nats-api.systemd.j2, dest: /etc/systemd/system/nats-api.service }
|
||||||
|
- { src: nats-server.systemd.j2, dest: /etc/systemd/system/nats.service }
|
||||||
|
- { src: mesh.systemd.j2, dest: /etc/systemd/system/meshcentral.service }
|
||||||
|
|
||||||
|
- name: import mongodb repo signing key
|
||||||
|
tags: mongo
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.apt_key:
|
||||||
|
url: https://www.mongodb.org/static/pgp/server-4.4.asc
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: setup mongodb repo
|
||||||
|
tags: mongo
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: "deb https://repo.mongodb.org/apt/debian buster/mongodb-org/4.4 main"
|
||||||
|
dest: /etc/apt/sources.list.d/mongodb-org-4.4.list
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: install mongodb
|
||||||
|
tags: mongo
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.apt:
|
||||||
|
pkg: mongodb-org
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: ensure mongodb enabled and started
|
||||||
|
tags: mongo
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: mongod
|
||||||
|
enabled: yes
|
||||||
|
state: started
|
||||||
|
|
||||||
|
- name: get mesh_ver
|
||||||
|
tags: mesh
|
||||||
|
ansible.builtin.shell: grep "^MESH_VER" {{ settings_file }} | awk -F'[= "]' '{print $5}'
|
||||||
|
register: mesh_ver
|
||||||
|
|
||||||
|
- name: create meshcentral data directory
|
||||||
|
tags: mesh
|
||||||
become: yes
|
become: yes
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ item }}"
|
path: "{{ mesh_dir }}/meshcentral-data"
|
||||||
state: absent
|
state: directory
|
||||||
|
owner: "{{ user }}"
|
||||||
|
group: "{{ user }}"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: install meshcentral
|
||||||
|
tags: mesh
|
||||||
|
ansible.builtin.command:
|
||||||
|
chdir: "{{ mesh_dir }}"
|
||||||
|
cmd: "npm install meshcentral@{{ mesh_ver.stdout }}"
|
||||||
|
|
||||||
|
- name: deploy mesh config
|
||||||
|
tags: mesh
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: mesh.cfg.j2
|
||||||
|
dest: "{{ mesh_dir }}/meshcentral-data/config.json"
|
||||||
|
mode: "0644"
|
||||||
|
owner: "{{ user }}"
|
||||||
|
group: "{{ user }}"
|
||||||
|
|
||||||
|
- name: start meshcentral
|
||||||
|
tags: mesh
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: meshcentral.service
|
||||||
|
state: started
|
||||||
|
enabled: yes
|
||||||
|
daemon_reload: yes
|
||||||
|
|
||||||
|
- name: wait for meshcentral to be ready
|
||||||
|
tags: mesh
|
||||||
|
uri:
|
||||||
|
url: "https://{{ mesh }}"
|
||||||
|
return_content: yes
|
||||||
|
validate_certs: yes
|
||||||
|
status_code: 200
|
||||||
|
register: mesh_status
|
||||||
|
until: mesh_status.status == 200
|
||||||
|
retries: 20
|
||||||
|
delay: 3
|
||||||
|
|
||||||
|
- name: get meshcentral login token key
|
||||||
|
tags: mesh_key
|
||||||
|
ansible.builtin.command:
|
||||||
|
chdir: "{{ mesh_dir }}"
|
||||||
|
cmd: node node_modules/meshcentral --logintokenkey
|
||||||
|
register: mesh_token_key
|
||||||
|
|
||||||
|
- name: add mesh key to django settings file
|
||||||
|
tags: mesh_key
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "{{ local_settings_file }}"
|
||||||
|
line: 'MESH_TOKEN_KEY = "{{ mesh_token_key.stdout }}"'
|
||||||
|
|
||||||
|
- name: stop meshcentral service
|
||||||
|
tags: mesh_user
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: meshcentral.service
|
||||||
|
state: stopped
|
||||||
|
|
||||||
|
- name: create mesh user
|
||||||
|
tags: mesh_user
|
||||||
|
ansible.builtin.shell:
|
||||||
|
chdir: "{{ mesh_dir }}"
|
||||||
|
cmd: |
|
||||||
|
node node_modules/meshcentral --createaccount {{ mesh_user }} --pass {{ mesh_password }} --email {{ github_email }}
|
||||||
|
node node_modules/meshcentral --adminaccount {{ mesh_user }}
|
||||||
|
|
||||||
|
- name: start meshcentral service
|
||||||
|
tags: mesh_user
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: meshcentral.service
|
||||||
|
state: started
|
||||||
|
|
||||||
|
- name: wait for meshcentral to be ready
|
||||||
|
tags: mesh_user
|
||||||
|
uri:
|
||||||
|
url: "https://{{ mesh }}"
|
||||||
|
return_content: yes
|
||||||
|
validate_certs: yes
|
||||||
|
status_code: 200
|
||||||
|
register: mesh_status
|
||||||
|
until: mesh_status.status == 200
|
||||||
|
retries: 20
|
||||||
|
delay: 3
|
||||||
|
|
||||||
|
- name: create mesh device group
|
||||||
|
tags: mesh_user
|
||||||
|
ansible.builtin.shell:
|
||||||
|
chdir: "{{ mesh_dir }}"
|
||||||
|
cmd: |
|
||||||
|
node node_modules/meshcentral/meshctrl.js --url wss://{{ mesh }}:443 --loginuser {{ mesh_user }} --loginpass {{ mesh_password }} AddDeviceGroup --name TacticalRMM
|
||||||
|
|
||||||
|
- name: finish up django
|
||||||
|
tags: mesh_user
|
||||||
|
ansible.builtin.shell:
|
||||||
|
chdir: "{{ backend_dir }}/api/tacticalrmm"
|
||||||
|
cmd: |
|
||||||
|
. ../env/bin/activate
|
||||||
|
python manage.py initial_db_setup
|
||||||
|
python manage.py reload_nats
|
||||||
|
|
||||||
|
- name: restart services
|
||||||
|
tags: services
|
||||||
|
become: yes
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: yes
|
||||||
|
enabled: yes
|
||||||
|
state: restarted
|
||||||
|
name: "{{ item }}.service"
|
||||||
with_items:
|
with_items:
|
||||||
- "{{ nats_tmp.path }}"
|
- nats
|
||||||
- "{{ python_tmp.path }}"
|
- nats-api
|
||||||
- "{{ nodejs_tmp.path }}"
|
|
||||||
|
|||||||
20
ansible/roles/trmm_dev/templates/backend.nginx.j2
Normal file
20
ansible/roles/trmm_dev/templates/backend.nginx.j2
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
server {
|
||||||
|
listen 443 ssl reuseport;
|
||||||
|
listen [::]:443 ssl;
|
||||||
|
server_name {{ api }};
|
||||||
|
client_max_body_size 300M;
|
||||||
|
ssl_certificate {{ fullchain_dest }};
|
||||||
|
ssl_certificate_key {{ privkey_dest }};
|
||||||
|
|
||||||
|
|
||||||
|
location ~ ^/natsws {
|
||||||
|
proxy_pass http://127.0.0.1:9235;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header X-Forwarded-Host $host:$server_port;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,7 @@ SECRET_KEY = "{{ django_secret }}"
|
|||||||
DEBUG = True
|
DEBUG = True
|
||||||
ALLOWED_HOSTS = ['{{ api }}']
|
ALLOWED_HOSTS = ['{{ api }}']
|
||||||
ADMIN_URL = "admin/"
|
ADMIN_URL = "admin/"
|
||||||
CORS_ORIGIN_WHITELIST = [
|
CORS_ORIGIN_ALLOW_ALL = True
|
||||||
"https://{{ rmm }}"
|
|
||||||
]
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.postgresql',
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
@@ -17,3 +15,7 @@ DATABASES = {
|
|||||||
}
|
}
|
||||||
REDIS_HOST = "localhost"
|
REDIS_HOST = "localhost"
|
||||||
ADMIN_ENABLED = True
|
ADMIN_ENABLED = True
|
||||||
|
CERT_FILE = "{{ fullchain_src }}"
|
||||||
|
KEY_FILE = "{{ privkey_src }}"
|
||||||
|
MESH_USERNAME = "{{ mesh_user }}"
|
||||||
|
MESH_SITE = "{{ mesh }}"
|
||||||
|
|||||||
33
ansible/roles/trmm_dev/templates/mesh.cfg.j2
Normal file
33
ansible/roles/trmm_dev/templates/mesh.cfg.j2
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"settings": {
|
||||||
|
"Cert": "{{ mesh }}",
|
||||||
|
"MongoDb": "mongodb://127.0.0.1:27017",
|
||||||
|
"MongoDbName": "meshcentral",
|
||||||
|
"WANonly": true,
|
||||||
|
"Minify": 1,
|
||||||
|
"Port": 4430,
|
||||||
|
"AliasPort": 443,
|
||||||
|
"RedirPort": 800,
|
||||||
|
"AllowLoginToken": true,
|
||||||
|
"AllowFraming": true,
|
||||||
|
"AgentPong": 300,
|
||||||
|
"AllowHighQualityDesktop": true,
|
||||||
|
"TlsOffload": "127.0.0.1",
|
||||||
|
"agentCoreDump": false,
|
||||||
|
"Compression": true,
|
||||||
|
"WsCompression": true,
|
||||||
|
"AgentWsCompression": true,
|
||||||
|
"MaxInvalidLogin": { "time": 5, "count": 5, "coolofftime": 30 }
|
||||||
|
},
|
||||||
|
"domains": {
|
||||||
|
"": {
|
||||||
|
"Title": "Tactical RMM",
|
||||||
|
"Title2": "Tactical RMM",
|
||||||
|
"NewAccounts": false,
|
||||||
|
"CertUrl": "https://{{ mesh }}:443/",
|
||||||
|
"GeoLocation": true,
|
||||||
|
"CookieIpCheck": false,
|
||||||
|
"mstsc": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
ansible/roles/trmm_dev/templates/mesh.nginx.j2
Normal file
22
ansible/roles/trmm_dev/templates/mesh.nginx.j2
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
listen [::]:443 ssl;
|
||||||
|
proxy_send_timeout 330s;
|
||||||
|
proxy_read_timeout 330s;
|
||||||
|
server_name {{ mesh }};
|
||||||
|
ssl_certificate {{ fullchain_dest }};
|
||||||
|
ssl_certificate_key {{ privkey_dest }};
|
||||||
|
|
||||||
|
ssl_session_cache shared:WEBSSL:10m;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:4430/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header X-Forwarded-Host $host:$server_port;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
ansible/roles/trmm_dev/templates/mesh.systemd.j2
Normal file
17
ansible/roles/trmm_dev/templates/mesh.systemd.j2
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=MeshCentral Server
|
||||||
|
After=network.target mongod.service nginx.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
LimitNOFILE=1000000
|
||||||
|
ExecStart=/usr/bin/node node_modules/meshcentral
|
||||||
|
Environment=NODE_ENV=production
|
||||||
|
WorkingDirectory={{ mesh_dir }}
|
||||||
|
User={{ user }}
|
||||||
|
Group={{ user }}
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10s
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
14
ansible/roles/trmm_dev/templates/nats-api.systemd.j2
Normal file
14
ansible/roles/trmm_dev/templates/nats-api.systemd.j2
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=TacticalRMM Nats Api
|
||||||
|
After=nats.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/local/bin/nats-api
|
||||||
|
User={{ user }}
|
||||||
|
Group={{ user }}
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5s
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
18
ansible/roles/trmm_dev/templates/nats-server.systemd.j2
Normal file
18
ansible/roles/trmm_dev/templates/nats-server.systemd.j2
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=NATS Server
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
PrivateTmp=true
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/local/bin/nats-server -c {{ backend_dir }}/api/tacticalrmm/nats-rmm.conf
|
||||||
|
ExecReload=/usr/bin/kill -s HUP $MAINPID
|
||||||
|
ExecStop=/usr/bin/kill -s SIGINT $MAINPID
|
||||||
|
User={{ user }}
|
||||||
|
Group={{ user }}
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5s
|
||||||
|
LimitNOFILE=1000000
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
4
ansible/roles/trmm_dev/templates/quasar.env.j2
Normal file
4
ansible/roles/trmm_dev/templates/quasar.env.j2
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
DEV_URL = "http://{{ api }}:8000"
|
||||||
|
DEV_HOST = "{{ rmm }}"
|
||||||
|
DEV_PORT = "8080"
|
||||||
|
USE_HTTPS = false
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
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'
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
- hosts: "{{ target }}"
|
|
||||||
vars:
|
|
||||||
ansible_user: tactical
|
|
||||||
roles:
|
|
||||||
- trmm_dev
|
|
||||||
20
ansible/setup_dev.yml.example
Normal file
20
ansible/setup_dev.yml.example
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
- hosts: "{{ target }}"
|
||||||
|
vars:
|
||||||
|
ansible_user: tactical
|
||||||
|
fullchain_src: /path/to/fullchain.pem
|
||||||
|
privkey_src: /path/to/privkey.pem
|
||||||
|
api: "api.example.com"
|
||||||
|
rmm: "rmm.example.com"
|
||||||
|
mesh: "mesh.example.com"
|
||||||
|
github_username: "changeme"
|
||||||
|
github_email: "changeme@example.com"
|
||||||
|
mesh_user: "changeme"
|
||||||
|
mesh_password: "changeme"
|
||||||
|
db_user: "changeme"
|
||||||
|
db_passwd: "changeme"
|
||||||
|
django_secret: "changeme"
|
||||||
|
django_user: "changeme"
|
||||||
|
django_password: "changeme"
|
||||||
|
roles:
|
||||||
|
- trmm_dev
|
||||||
18
api/tacticalrmm/accounts/utils.py
Normal file
18
api/tacticalrmm/accounts/utils.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from typing import TYPE_CHECKING
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from accounts.models import User
|
||||||
|
|
||||||
|
|
||||||
|
def is_root_user(*, request: "HttpRequest", user: "User") -> bool:
|
||||||
|
root = (
|
||||||
|
hasattr(settings, "ROOT_USER")
|
||||||
|
and request.user != user
|
||||||
|
and user.username == settings.ROOT_USER
|
||||||
|
)
|
||||||
|
demo = (
|
||||||
|
getattr(settings, "DEMO", False) and request.user.username == settings.ROOT_USER
|
||||||
|
)
|
||||||
|
return root or demo
|
||||||
@@ -22,18 +22,7 @@ from .serializers import (
|
|||||||
UserSerializer,
|
UserSerializer,
|
||||||
UserUISerializer,
|
UserUISerializer,
|
||||||
)
|
)
|
||||||
|
from accounts.utils import is_root_user
|
||||||
|
|
||||||
def _is_root_user(request, user) -> bool:
|
|
||||||
root = (
|
|
||||||
hasattr(settings, "ROOT_USER")
|
|
||||||
and request.user != user
|
|
||||||
and user.username == settings.ROOT_USER
|
|
||||||
)
|
|
||||||
demo = (
|
|
||||||
getattr(settings, "DEMO", False) and request.user.username == settings.ROOT_USER
|
|
||||||
)
|
|
||||||
return root or demo
|
|
||||||
|
|
||||||
|
|
||||||
class CheckCreds(KnoxLoginView):
|
class CheckCreds(KnoxLoginView):
|
||||||
@@ -159,7 +148,7 @@ class GetUpdateDeleteUser(APIView):
|
|||||||
def put(self, request, pk):
|
def put(self, request, pk):
|
||||||
user = get_object_or_404(User, pk=pk)
|
user = get_object_or_404(User, pk=pk)
|
||||||
|
|
||||||
if _is_root_user(request, user):
|
if is_root_user(request=request, user=user):
|
||||||
return notify_error("The root user cannot be modified from the UI")
|
return notify_error("The root user cannot be modified from the UI")
|
||||||
|
|
||||||
serializer = UserSerializer(instance=user, data=request.data, partial=True)
|
serializer = UserSerializer(instance=user, data=request.data, partial=True)
|
||||||
@@ -170,7 +159,7 @@ class GetUpdateDeleteUser(APIView):
|
|||||||
|
|
||||||
def delete(self, request, pk):
|
def delete(self, request, pk):
|
||||||
user = get_object_or_404(User, pk=pk)
|
user = get_object_or_404(User, pk=pk)
|
||||||
if _is_root_user(request, user):
|
if is_root_user(request=request, user=user):
|
||||||
return notify_error("The root user cannot be deleted from the UI")
|
return notify_error("The root user cannot be deleted from the UI")
|
||||||
|
|
||||||
user.delete()
|
user.delete()
|
||||||
@@ -183,7 +172,7 @@ class UserActions(APIView):
|
|||||||
# reset password
|
# reset password
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
user = get_object_or_404(User, pk=request.data["id"])
|
user = get_object_or_404(User, pk=request.data["id"])
|
||||||
if _is_root_user(request, user):
|
if is_root_user(request=request, user=user):
|
||||||
return notify_error("The root user cannot be modified from the UI")
|
return notify_error("The root user cannot be modified from the UI")
|
||||||
|
|
||||||
user.set_password(request.data["password"])
|
user.set_password(request.data["password"])
|
||||||
@@ -194,7 +183,7 @@ class UserActions(APIView):
|
|||||||
# reset two factor token
|
# reset two factor token
|
||||||
def put(self, request):
|
def put(self, request):
|
||||||
user = get_object_or_404(User, pk=request.data["id"])
|
user = get_object_or_404(User, pk=request.data["id"])
|
||||||
if _is_root_user(request, user):
|
if is_root_user(request=request, user=user):
|
||||||
return notify_error("The root user cannot be modified from the UI")
|
return notify_error("The root user cannot be modified from the UI")
|
||||||
|
|
||||||
user.totp_key = ""
|
user.totp_key = ""
|
||||||
|
|||||||
@@ -568,6 +568,12 @@ class Command(BaseCommand):
|
|||||||
check5_history.y = 1
|
check5_history.y = 1
|
||||||
else:
|
else:
|
||||||
check5_history.y = 0
|
check5_history.y = 0
|
||||||
|
check5_history.results = {
|
||||||
|
"retcode": 0,
|
||||||
|
"stdout": None,
|
||||||
|
"stderr": None,
|
||||||
|
"execution_time": "4.0000",
|
||||||
|
}
|
||||||
check5_history.save()
|
check5_history.save()
|
||||||
|
|
||||||
check6 = Check()
|
check6 = Check()
|
||||||
@@ -595,6 +601,12 @@ class Command(BaseCommand):
|
|||||||
check6_history.agent_id = agent.agent_id
|
check6_history.agent_id = agent.agent_id
|
||||||
check6_history.x = django_now - djangotime.timedelta(minutes=i * 2)
|
check6_history.x = django_now - djangotime.timedelta(minutes=i * 2)
|
||||||
check6_history.y = 0
|
check6_history.y = 0
|
||||||
|
check6_history.results = {
|
||||||
|
"retcode": 0,
|
||||||
|
"stdout": None,
|
||||||
|
"stderr": None,
|
||||||
|
"execution_time": "4.0000",
|
||||||
|
}
|
||||||
check6_history.save()
|
check6_history.save()
|
||||||
|
|
||||||
nla_task = AutomatedTask()
|
nla_task = AutomatedTask()
|
||||||
@@ -712,6 +724,12 @@ class Command(BaseCommand):
|
|||||||
check7_history.agent_id = agent.agent_id
|
check7_history.agent_id = agent.agent_id
|
||||||
check7_history.x = django_now - djangotime.timedelta(minutes=i * 2)
|
check7_history.x = django_now - djangotime.timedelta(minutes=i * 2)
|
||||||
check7_history.y = 0
|
check7_history.y = 0
|
||||||
|
check7_history.results = {
|
||||||
|
"retcode": 0,
|
||||||
|
"stdout": spooler_stdout,
|
||||||
|
"stderr": None,
|
||||||
|
"execution_time": "3.1337",
|
||||||
|
}
|
||||||
check7_history.save()
|
check7_history.save()
|
||||||
|
|
||||||
if agent.plat == AgentPlat.WINDOWS:
|
if agent.plat == AgentPlat.WINDOWS:
|
||||||
|
|||||||
@@ -748,10 +748,10 @@ class Agent(BaseAuditModel):
|
|||||||
cache_key = f"agent_{self.agent_id}_checks"
|
cache_key = f"agent_{self.agent_id}_checks"
|
||||||
|
|
||||||
elif self.policy:
|
elif self.policy:
|
||||||
cache_key = f"site_{self.monitoring_type}_{self.site_id}_policy_{self.policy_id}_checks"
|
cache_key = f"site_{self.monitoring_type}_{self.plat}_{self.site_id}_policy_{self.policy_id}_checks"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
cache_key = f"site_{self.monitoring_type}_{self.site_id}_checks"
|
cache_key = f"site_{self.monitoring_type}_{self.plat}_{self.site_id}_checks"
|
||||||
|
|
||||||
cached_checks = cache.get(cache_key)
|
cached_checks = cache.get(cache_key)
|
||||||
if isinstance(cached_checks, list):
|
if isinstance(cached_checks, list):
|
||||||
@@ -773,10 +773,10 @@ class Agent(BaseAuditModel):
|
|||||||
cache_key = f"agent_{self.agent_id}_tasks"
|
cache_key = f"agent_{self.agent_id}_tasks"
|
||||||
|
|
||||||
elif self.policy:
|
elif self.policy:
|
||||||
cache_key = f"site_{self.monitoring_type}_{self.site_id}_policy_{self.policy_id}_tasks"
|
cache_key = f"site_{self.monitoring_type}_{self.plat}_{self.site_id}_policy_{self.policy_id}_tasks"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
cache_key = f"site_{self.monitoring_type}_{self.site_id}_tasks"
|
cache_key = f"site_{self.monitoring_type}_{self.plat}_{self.site_id}_tasks"
|
||||||
|
|
||||||
cached_tasks = cache.get(cache_key)
|
cached_tasks = cache.get(cache_key)
|
||||||
if isinstance(cached_tasks, list):
|
if isinstance(cached_tasks, list):
|
||||||
@@ -784,7 +784,7 @@ class Agent(BaseAuditModel):
|
|||||||
else:
|
else:
|
||||||
# get agent tasks based on policies
|
# get agent tasks based on policies
|
||||||
tasks = Policy.get_policy_tasks(self)
|
tasks = Policy.get_policy_tasks(self)
|
||||||
cache.set(f"site_{self.site_id}_tasks", tasks, 600)
|
cache.set(cache_key, tasks, 600)
|
||||||
return tasks
|
return tasks
|
||||||
|
|
||||||
def _do_nats_debug(self, agent: "Agent", message: str) -> None:
|
def _do_nats_debug(self, agent: "Agent", message: str) -> None:
|
||||||
@@ -845,22 +845,22 @@ class Agent(BaseAuditModel):
|
|||||||
asyncio.run(
|
asyncio.run(
|
||||||
send_command_with_mesh(cmd, mesh_uri, self.mesh_node_id, shell, 0)
|
send_command_with_mesh(cmd, mesh_uri, self.mesh_node_id, shell, 0)
|
||||||
)
|
)
|
||||||
return ("ok", False)
|
return "ok", False
|
||||||
|
|
||||||
elif mode == "mesh":
|
elif mode == "mesh":
|
||||||
data = {"func": "recover", "payload": {"mode": mode}}
|
data = {"func": "recover", "payload": {"mode": mode}}
|
||||||
if wait:
|
if wait:
|
||||||
r = asyncio.run(self.nats_cmd(data, timeout=20))
|
r = asyncio.run(self.nats_cmd(data, timeout=20))
|
||||||
if r == "ok":
|
if r == "ok":
|
||||||
return ("ok", False)
|
return "ok", False
|
||||||
else:
|
else:
|
||||||
return (str(r), True)
|
return str(r), True
|
||||||
else:
|
else:
|
||||||
asyncio.run(self.nats_cmd(data, timeout=20, wait=False))
|
asyncio.run(self.nats_cmd(data, timeout=20, wait=False))
|
||||||
|
|
||||||
return ("ok", False)
|
return "ok", False
|
||||||
|
|
||||||
return ("invalid", True)
|
return "invalid", True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def serialize(agent: "Agent") -> Dict[str, Any]:
|
def serialize(agent: "Agent") -> Dict[str, Any]:
|
||||||
|
|||||||
@@ -90,6 +90,11 @@ class AgentTableSerializer(serializers.ModelSerializer):
|
|||||||
last_seen = serializers.ReadOnlyField()
|
last_seen = serializers.ReadOnlyField()
|
||||||
pending_actions_count = serializers.ReadOnlyField()
|
pending_actions_count = serializers.ReadOnlyField()
|
||||||
has_patches_pending = serializers.ReadOnlyField()
|
has_patches_pending = serializers.ReadOnlyField()
|
||||||
|
cpu_model = serializers.ReadOnlyField()
|
||||||
|
graphics = serializers.ReadOnlyField()
|
||||||
|
local_ips = serializers.ReadOnlyField()
|
||||||
|
make_model = serializers.ReadOnlyField()
|
||||||
|
physical_disks = serializers.ReadOnlyField()
|
||||||
|
|
||||||
def get_alert_template(self, obj):
|
def get_alert_template(self, obj):
|
||||||
|
|
||||||
@@ -141,16 +146,18 @@ class AgentTableSerializer(serializers.ModelSerializer):
|
|||||||
"plat",
|
"plat",
|
||||||
"goarch",
|
"goarch",
|
||||||
"has_patches_pending",
|
"has_patches_pending",
|
||||||
|
"version",
|
||||||
|
"operating_system",
|
||||||
|
"public_ip",
|
||||||
|
"cpu_model",
|
||||||
|
"graphics",
|
||||||
|
"local_ips",
|
||||||
|
"make_model",
|
||||||
|
"physical_disks",
|
||||||
]
|
]
|
||||||
depth = 2
|
depth = 2
|
||||||
|
|
||||||
|
|
||||||
class WinAgentSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Agent
|
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
|
|
||||||
class AgentHostnameSerializer(serializers.ModelSerializer):
|
class AgentHostnameSerializer(serializers.ModelSerializer):
|
||||||
client = serializers.ReadOnlyField(source="client.name")
|
client = serializers.ReadOnlyField(source="client.name")
|
||||||
site = serializers.ReadOnlyField(source="site.name")
|
site = serializers.ReadOnlyField(source="site.name")
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ class TestAgentUpdate(TacticalTestCase):
|
|||||||
site=self.site1,
|
site=self.site1,
|
||||||
monitoring_type=AgentMonType.SERVER,
|
monitoring_type=AgentMonType.SERVER,
|
||||||
plat=AgentPlat.WINDOWS,
|
plat=AgentPlat.WINDOWS,
|
||||||
version="2.3.0",
|
version="2.1.1",
|
||||||
)
|
)
|
||||||
r = agent_noarch.do_update(token="", force=True)
|
r = agent_noarch.do_update(token="", force=True)
|
||||||
self.assertEqual(r, "noarch")
|
self.assertEqual(r, "noarch")
|
||||||
@@ -106,7 +106,7 @@ class TestAgentUpdate(TacticalTestCase):
|
|||||||
site=self.site1,
|
site=self.site1,
|
||||||
monitoring_type=AgentMonType.SERVER,
|
monitoring_type=AgentMonType.SERVER,
|
||||||
plat=AgentPlat.WINDOWS,
|
plat=AgentPlat.WINDOWS,
|
||||||
version="2.3.0",
|
version="2.1.1",
|
||||||
goarch=GoArch.AMD64,
|
goarch=GoArch.AMD64,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ class TestAgentUpdate(TacticalTestCase):
|
|||||||
site=self.site3,
|
site=self.site3,
|
||||||
monitoring_type=AgentMonType.WORKSTATION,
|
monitoring_type=AgentMonType.WORKSTATION,
|
||||||
plat=AgentPlat.LINUX,
|
plat=AgentPlat.LINUX,
|
||||||
version="2.3.0",
|
version="2.1.1",
|
||||||
goarch=GoArch.ARM32,
|
goarch=GoArch.ARM32,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ class TestAgentUpdate(TacticalTestCase):
|
|||||||
site=self.site2,
|
site=self.site2,
|
||||||
monitoring_type=AgentMonType.SERVER,
|
monitoring_type=AgentMonType.SERVER,
|
||||||
plat=AgentPlat.WINDOWS,
|
plat=AgentPlat.WINDOWS,
|
||||||
version="2.3.0",
|
version="2.1.1",
|
||||||
goarch=GoArch.AMD64,
|
goarch=GoArch.AMD64,
|
||||||
_quantity=6,
|
_quantity=6,
|
||||||
)
|
)
|
||||||
@@ -215,7 +215,7 @@ class TestAgentUpdate(TacticalTestCase):
|
|||||||
site=self.site2,
|
site=self.site2,
|
||||||
monitoring_type=AgentMonType.SERVER,
|
monitoring_type=AgentMonType.SERVER,
|
||||||
plat=AgentPlat.WINDOWS,
|
plat=AgentPlat.WINDOWS,
|
||||||
version="2.3.0",
|
version="2.1.1",
|
||||||
goarch=GoArch.AMD64,
|
goarch=GoArch.AMD64,
|
||||||
_quantity=7,
|
_quantity=7,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -422,13 +422,13 @@ class TestAgentViews(TacticalTestCase):
|
|||||||
url = f"{base_url}/{self.agent.agent_id}/reboot/"
|
url = f"{base_url}/{self.agent.agent_id}/reboot/"
|
||||||
|
|
||||||
# ensure we don't allow dates in past
|
# ensure we don't allow dates in past
|
||||||
data = {"datetime": "2022-07-11T01:51:11"}
|
data = {"datetime": "2022-07-11T01:51"}
|
||||||
r = self.client.patch(url, data, format="json")
|
r = self.client.patch(url, data, format="json")
|
||||||
self.assertEqual(r.status_code, 400)
|
self.assertEqual(r.status_code, 400)
|
||||||
self.assertEqual(r.data, "Date cannot be set in the past")
|
self.assertEqual(r.data, "Date cannot be set in the past")
|
||||||
|
|
||||||
# test with date in future
|
# test with date in future
|
||||||
data["datetime"] = "2027-08-29T18:41:02"
|
data["datetime"] = "2027-08-29T18:41"
|
||||||
r = self.client.patch(url, data, format="json")
|
r = self.client.patch(url, data, format="json")
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assertEqual(r.data["time"], "August 29, 2027 at 06:41 PM")
|
self.assertEqual(r.data["time"], "August 29, 2027 at 06:41 PM")
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from django.shortcuts import get_object_or_404
|
|||||||
from django.utils import timezone as djangotime
|
from django.utils import timezone as djangotime
|
||||||
from meshctrl.utils import get_login_token
|
from meshctrl.utils import get_login_token
|
||||||
from packaging import version as pyver
|
from packaging import version as pyver
|
||||||
|
from rest_framework import serializers
|
||||||
from rest_framework.decorators import api_view, permission_classes
|
from rest_framework.decorators import api_view, permission_classes
|
||||||
from rest_framework.exceptions import PermissionDenied
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
@@ -29,6 +30,7 @@ from scripts.models import Script
|
|||||||
from scripts.tasks import handle_bulk_command_task, handle_bulk_script_task
|
from scripts.tasks import handle_bulk_command_task, handle_bulk_script_task
|
||||||
from tacticalrmm.constants import (
|
from tacticalrmm.constants import (
|
||||||
AGENT_DEFER,
|
AGENT_DEFER,
|
||||||
|
AGENT_TABLE_DEFER,
|
||||||
AGENT_STATUS_OFFLINE,
|
AGENT_STATUS_OFFLINE,
|
||||||
AGENT_STATUS_ONLINE,
|
AGENT_STATUS_ONLINE,
|
||||||
AgentHistoryType,
|
AgentHistoryType,
|
||||||
@@ -114,7 +116,7 @@ class GetAgents(APIView):
|
|||||||
Agent.objects.filter_by_role(request.user) # type: ignore
|
Agent.objects.filter_by_role(request.user) # type: ignore
|
||||||
.filter(monitoring_type_filter)
|
.filter(monitoring_type_filter)
|
||||||
.filter(client_site_filter)
|
.filter(client_site_filter)
|
||||||
.defer(*AGENT_DEFER)
|
.defer(*AGENT_TABLE_DEFER)
|
||||||
.select_related(
|
.select_related(
|
||||||
"site__server_policy",
|
"site__server_policy",
|
||||||
"site__workstation_policy",
|
"site__workstation_policy",
|
||||||
@@ -166,6 +168,22 @@ class GetAgents(APIView):
|
|||||||
class GetUpdateDeleteAgent(APIView):
|
class GetUpdateDeleteAgent(APIView):
|
||||||
permission_classes = [IsAuthenticated, AgentPerms]
|
permission_classes = [IsAuthenticated, AgentPerms]
|
||||||
|
|
||||||
|
class InputSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Agent
|
||||||
|
fields = [
|
||||||
|
"monitoring_type",
|
||||||
|
"description",
|
||||||
|
"overdue_email_alert",
|
||||||
|
"overdue_text_alert",
|
||||||
|
"overdue_dashboard_alert",
|
||||||
|
"offline_time",
|
||||||
|
"overdue_time",
|
||||||
|
"check_interval",
|
||||||
|
"time_zone",
|
||||||
|
"site",
|
||||||
|
]
|
||||||
|
|
||||||
# get agent details
|
# get agent details
|
||||||
def get(self, request, agent_id):
|
def get(self, request, agent_id):
|
||||||
agent = get_object_or_404(Agent, agent_id=agent_id)
|
agent = get_object_or_404(Agent, agent_id=agent_id)
|
||||||
@@ -175,9 +193,9 @@ class GetUpdateDeleteAgent(APIView):
|
|||||||
def put(self, request, agent_id):
|
def put(self, request, agent_id):
|
||||||
agent = get_object_or_404(Agent, agent_id=agent_id)
|
agent = get_object_or_404(Agent, agent_id=agent_id)
|
||||||
|
|
||||||
a_serializer = AgentSerializer(instance=agent, data=request.data, partial=True)
|
s = self.InputSerializer(instance=agent, data=request.data, partial=True)
|
||||||
a_serializer.is_valid(raise_exception=True)
|
s.is_valid(raise_exception=True)
|
||||||
a_serializer.save()
|
s.save()
|
||||||
|
|
||||||
if "winupdatepolicy" in request.data.keys():
|
if "winupdatepolicy" in request.data.keys():
|
||||||
policy = agent.winupdatepolicy.get() # type: ignore
|
policy = agent.winupdatepolicy.get() # type: ignore
|
||||||
@@ -460,7 +478,7 @@ class Reboot(APIView):
|
|||||||
return notify_error(f"Not currently implemented for {agent.plat}")
|
return notify_error(f"Not currently implemented for {agent.plat}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
obj = dt.datetime.strptime(request.data["datetime"], "%Y-%m-%dT%H:%M:%S")
|
obj = dt.datetime.strptime(request.data["datetime"], "%Y-%m-%dT%H:%M")
|
||||||
except Exception:
|
except Exception:
|
||||||
return notify_error("Invalid date")
|
return notify_error("Invalid date")
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,11 @@ from rest_framework.serializers import (
|
|||||||
from agents.serializers import AgentHostnameSerializer
|
from agents.serializers import AgentHostnameSerializer
|
||||||
from autotasks.models import TaskResult
|
from autotasks.models import TaskResult
|
||||||
from checks.models import CheckResult
|
from checks.models import CheckResult
|
||||||
from clients.models import Client
|
from clients.models import Client, Site
|
||||||
from clients.serializers import ClientMinimumSerializer, SiteMinimumSerializer
|
from clients.serializers import (
|
||||||
|
ClientMinimumSerializer,
|
||||||
|
SiteMinimumSerializer,
|
||||||
|
)
|
||||||
from winupdate.serializers import WinUpdatePolicySerializer
|
from winupdate.serializers import WinUpdatePolicySerializer
|
||||||
|
|
||||||
from .models import Policy
|
from .models import Policy
|
||||||
@@ -85,11 +88,29 @@ class PolicyRelatedSerializer(ModelSerializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PolicyOverviewSiteSerializer(ModelSerializer):
|
||||||
|
workstation_policy = PolicySerializer(read_only=True)
|
||||||
|
server_policy = PolicySerializer(read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Site
|
||||||
|
fields = ("pk", "name", "workstation_policy", "server_policy")
|
||||||
|
|
||||||
|
|
||||||
class PolicyOverviewSerializer(ModelSerializer):
|
class PolicyOverviewSerializer(ModelSerializer):
|
||||||
|
sites = SerializerMethodField()
|
||||||
|
workstation_policy = PolicySerializer(read_only=True)
|
||||||
|
server_policy = PolicySerializer(read_only=True)
|
||||||
|
|
||||||
|
def get_sites(self, obj):
|
||||||
|
return PolicyOverviewSiteSerializer(
|
||||||
|
obj.filtered_sites,
|
||||||
|
many=True,
|
||||||
|
).data
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Client
|
model = Client
|
||||||
fields = ("pk", "name", "sites", "workstation_policy", "server_policy")
|
fields = ("pk", "name", "sites", "workstation_policy", "server_policy")
|
||||||
depth = 2
|
|
||||||
|
|
||||||
|
|
||||||
class PolicyCheckStatusSerializer(ModelSerializer):
|
class PolicyCheckStatusSerializer(ModelSerializer):
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ from itertools import cycle
|
|||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from model_bakery import baker, seq
|
from model_bakery import baker, seq
|
||||||
|
from django.db.models import Prefetch
|
||||||
from agents.models import Agent
|
from agents.models import Agent
|
||||||
|
from clients.models import Site
|
||||||
from core.utils import get_core_settings
|
from core.utils import get_core_settings
|
||||||
from tacticalrmm.constants import AgentMonType, TaskSyncStatus
|
from tacticalrmm.constants import AgentMonType, TaskSyncStatus
|
||||||
from tacticalrmm.test import TacticalTestCase
|
from tacticalrmm.test import TacticalTestCase
|
||||||
@@ -186,7 +187,17 @@ class TestPolicyViews(TacticalTestCase):
|
|||||||
|
|
||||||
baker.make("clients.Site", client=cycle(clients), _quantity=3)
|
baker.make("clients.Site", client=cycle(clients), _quantity=3)
|
||||||
resp = self.client.get(url, format="json")
|
resp = self.client.get(url, format="json")
|
||||||
clients = Client.objects.all()
|
clients = Client.objects.select_related(
|
||||||
|
"workstation_policy", "server_policy"
|
||||||
|
).prefetch_related(
|
||||||
|
Prefetch(
|
||||||
|
"sites",
|
||||||
|
queryset=Site.objects.select_related(
|
||||||
|
"workstation_policy", "server_policy"
|
||||||
|
),
|
||||||
|
to_attr="filtered_sites",
|
||||||
|
)
|
||||||
|
)
|
||||||
serializer = PolicyOverviewSerializer(clients, many=True)
|
serializer = PolicyOverviewSerializer(clients, many=True)
|
||||||
|
|
||||||
self.assertEqual(resp.status_code, 200)
|
self.assertEqual(resp.status_code, 200)
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ from rest_framework.views import APIView
|
|||||||
from agents.models import Agent
|
from agents.models import Agent
|
||||||
from autotasks.models import TaskResult
|
from autotasks.models import TaskResult
|
||||||
from checks.models import CheckResult
|
from checks.models import CheckResult
|
||||||
from clients.models import Client
|
from clients.models import Client, Site
|
||||||
from tacticalrmm.permissions import _has_perm_on_client, _has_perm_on_site
|
from tacticalrmm.permissions import _has_perm_on_client, _has_perm_on_site
|
||||||
from winupdate.models import WinUpdatePolicy
|
from winupdate.models import WinUpdatePolicy
|
||||||
from winupdate.serializers import WinUpdatePolicySerializer
|
from winupdate.serializers import WinUpdatePolicySerializer
|
||||||
|
from django.db.models import Prefetch
|
||||||
|
|
||||||
from .models import Policy
|
from .models import Policy
|
||||||
from .permissions import AutomationPolicyPerms
|
from .permissions import AutomationPolicyPerms
|
||||||
@@ -108,8 +109,18 @@ class PolicyCheck(APIView):
|
|||||||
class OverviewPolicy(APIView):
|
class OverviewPolicy(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
|
||||||
clients = Client.objects.filter_by_role(request.user).select_related(
|
clients = (
|
||||||
|
Client.objects.filter_by_role(request.user)
|
||||||
|
.select_related("workstation_policy", "server_policy")
|
||||||
|
.prefetch_related(
|
||||||
|
Prefetch(
|
||||||
|
"sites",
|
||||||
|
queryset=Site.objects.select_related(
|
||||||
"workstation_policy", "server_policy"
|
"workstation_policy", "server_policy"
|
||||||
|
),
|
||||||
|
to_attr="filtered_sites",
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return Response(PolicyOverviewSerializer(clients, many=True).data)
|
return Response(PolicyOverviewSerializer(clients, many=True).data)
|
||||||
|
|
||||||
|
|||||||
@@ -70,11 +70,11 @@ class Client(BaseAuditModel):
|
|||||||
sites = self.sites.all()
|
sites = self.sites.all()
|
||||||
if old_client.workstation_policy != self.workstation_policy:
|
if old_client.workstation_policy != self.workstation_policy:
|
||||||
for site in sites:
|
for site in sites:
|
||||||
cache.delete_many_pattern(f"site_workstation_{site.pk}_*")
|
cache.delete_many_pattern(f"site_workstation_*{site.pk}_*")
|
||||||
|
|
||||||
if old_client.server_policy != self.server_policy:
|
if old_client.server_policy != self.server_policy:
|
||||||
for site in sites:
|
for site in sites:
|
||||||
cache.delete_many_pattern(f"site_server_{site.pk}_*")
|
cache.delete_many_pattern(f"site_server_*{site.pk}_*")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ("name",)
|
ordering = ("name",)
|
||||||
@@ -145,10 +145,10 @@ class Site(BaseAuditModel):
|
|||||||
cache_agents_alert_template.delay()
|
cache_agents_alert_template.delay()
|
||||||
|
|
||||||
if old_site.workstation_policy != self.workstation_policy:
|
if old_site.workstation_policy != self.workstation_policy:
|
||||||
cache.delete_many_pattern(f"site_workstation_{self.pk}_*")
|
cache.delete_many_pattern(f"site_workstation_*{self.pk}_*")
|
||||||
|
|
||||||
if old_site.server_policy != self.server_policy:
|
if old_site.server_policy != self.server_policy:
|
||||||
cache.delete_many_pattern(f"site_server_{self.pk}_*")
|
cache.delete_many_pattern(f"site_server_*{self.pk}_*")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ("name",)
|
ordering = ("name",)
|
||||||
|
|||||||
@@ -39,9 +39,8 @@ If (Get-Service $serviceName -ErrorAction SilentlyContinue) {
|
|||||||
$DefenderStatus = Get-MpComputerStatus | select AntivirusEnabled
|
$DefenderStatus = Get-MpComputerStatus | select AntivirusEnabled
|
||||||
if ($DefenderStatus -match "True") {
|
if ($DefenderStatus -match "True") {
|
||||||
Add-MpPreference -ExclusionPath 'C:\Program Files\TacticalAgent\*'
|
Add-MpPreference -ExclusionPath 'C:\Program Files\TacticalAgent\*'
|
||||||
Add-MpPreference -ExclusionPath 'C:\Windows\Temp\winagent-v*.exe'
|
|
||||||
Add-MpPreference -ExclusionPath 'C:\Program Files\Mesh Agent\*'
|
Add-MpPreference -ExclusionPath 'C:\Program Files\Mesh Agent\*'
|
||||||
Add-MpPreference -ExclusionPath 'C:\Windows\Temp\trmm*\*'
|
Add-MpPreference -ExclusionPath 'C:\ProgramData\TacticalRMM\*'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Catch {
|
Catch {
|
||||||
|
|||||||
@@ -182,10 +182,10 @@ class CoreSettings(BaseAuditModel):
|
|||||||
test: bool = False,
|
test: bool = False,
|
||||||
) -> tuple[str, bool]:
|
) -> tuple[str, bool]:
|
||||||
if test and not self.email_is_configured:
|
if test and not self.email_is_configured:
|
||||||
return ("There needs to be at least one email recipient configured", False)
|
return "There needs to be at least one email recipient configured", False
|
||||||
# return since email must be configured to continue
|
# return since email must be configured to continue
|
||||||
elif not self.email_is_configured:
|
elif not self.email_is_configured:
|
||||||
return ("SMTP messaging not configured.", False)
|
return "SMTP messaging not configured.", False
|
||||||
|
|
||||||
# override email from if alert_template is passed and is set
|
# override email from if alert_template is passed and is set
|
||||||
if alert_template and alert_template.email_from:
|
if alert_template and alert_template.email_from:
|
||||||
@@ -199,7 +199,7 @@ class CoreSettings(BaseAuditModel):
|
|||||||
elif self.email_alert_recipients:
|
elif self.email_alert_recipients:
|
||||||
email_recipients = ", ".join(cast(List[str], self.email_alert_recipients))
|
email_recipients = ", ".join(cast(List[str], self.email_alert_recipients))
|
||||||
else:
|
else:
|
||||||
return ("There needs to be at least one email recipient configured", False)
|
return "There needs to be at least one email recipient configured", False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
msg = EmailMessage()
|
msg = EmailMessage()
|
||||||
@@ -226,12 +226,12 @@ class CoreSettings(BaseAuditModel):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
DebugLog.error(message=f"Sending email failed with error: {e}")
|
DebugLog.error(message=f"Sending email failed with error: {e}")
|
||||||
if test:
|
if test:
|
||||||
return (str(e), False)
|
return str(e), False
|
||||||
|
|
||||||
if test:
|
if test:
|
||||||
return ("Email test ok!", True)
|
return "Email test ok!", True
|
||||||
|
|
||||||
return ("ok", True)
|
return "ok", True
|
||||||
|
|
||||||
def send_sms(
|
def send_sms(
|
||||||
self,
|
self,
|
||||||
@@ -240,7 +240,7 @@ class CoreSettings(BaseAuditModel):
|
|||||||
test: bool = False,
|
test: bool = False,
|
||||||
) -> tuple[str, bool]:
|
) -> tuple[str, bool]:
|
||||||
if not self.sms_is_configured:
|
if not self.sms_is_configured:
|
||||||
return ("Sms alerting is not setup correctly.", False)
|
return "Sms alerting is not setup correctly.", False
|
||||||
|
|
||||||
# override email recipients if alert_template is passed and is set
|
# override email recipients if alert_template is passed and is set
|
||||||
if alert_template and alert_template.text_recipients:
|
if alert_template and alert_template.text_recipients:
|
||||||
@@ -248,7 +248,7 @@ class CoreSettings(BaseAuditModel):
|
|||||||
elif self.sms_alert_recipients:
|
elif self.sms_alert_recipients:
|
||||||
text_recipients = cast(List[str], self.sms_alert_recipients)
|
text_recipients = cast(List[str], self.sms_alert_recipients)
|
||||||
else:
|
else:
|
||||||
return ("No sms recipients found", False)
|
return "No sms recipients found", False
|
||||||
|
|
||||||
tw_client = TwClient(self.twilio_account_sid, self.twilio_auth_token)
|
tw_client = TwClient(self.twilio_account_sid, self.twilio_auth_token)
|
||||||
for num in text_recipients:
|
for num in text_recipients:
|
||||||
@@ -257,12 +257,12 @@ class CoreSettings(BaseAuditModel):
|
|||||||
except TwilioRestException as e:
|
except TwilioRestException as e:
|
||||||
DebugLog.error(message=f"SMS failed to send: {e}")
|
DebugLog.error(message=f"SMS failed to send: {e}")
|
||||||
if test:
|
if test:
|
||||||
return (str(e), False)
|
return str(e), False
|
||||||
|
|
||||||
if test:
|
if test:
|
||||||
return ("SMS Test sent successfully!", True)
|
return "SMS Test sent successfully!", True
|
||||||
|
|
||||||
return ("ok", True)
|
return "ok", True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def serialize(core):
|
def serialize(core):
|
||||||
|
|||||||
@@ -7,33 +7,33 @@ channels_redis==3.4.1
|
|||||||
chardet==4.0.0
|
chardet==4.0.0
|
||||||
cryptography==37.0.4
|
cryptography==37.0.4
|
||||||
daphne==3.0.2
|
daphne==3.0.2
|
||||||
Django==4.0.6
|
Django==4.1
|
||||||
django-cors-headers==3.13.0
|
django-cors-headers==3.13.0
|
||||||
django-ipware==4.0.2
|
django-ipware==4.0.2
|
||||||
django-rest-knox==4.2.0
|
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.4
|
msgpack==1.0.4
|
||||||
nats-py==2.1.4
|
nats-py==2.1.7
|
||||||
psutil==5.9.1
|
psutil==5.9.1
|
||||||
psycopg2-binary==2.9.3
|
psycopg2-binary==2.9.3
|
||||||
pycparser==2.21
|
pycparser==2.21
|
||||||
pycryptodome==3.15.0
|
pycryptodome==3.15.0
|
||||||
pyotp==2.6.0
|
pyotp==2.6.0
|
||||||
pyparsing==3.0.9
|
pyparsing==3.0.9
|
||||||
pytz==2022.1
|
pytz==2022.2.1
|
||||||
qrcode==7.3.1
|
qrcode==7.3.1
|
||||||
redis==4.3.4
|
redis==4.3.4
|
||||||
hiredis==2.0.0
|
hiredis==2.0.0
|
||||||
requests==2.28.1
|
requests==2.28.1
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
sqlparse==0.4.2
|
sqlparse==0.4.2
|
||||||
twilio==7.12.0
|
twilio==7.12.1
|
||||||
urllib3==1.26.11
|
urllib3==1.26.11
|
||||||
uWSGI==2.0.20
|
uWSGI==2.0.20
|
||||||
validators==0.20.0
|
validators==0.20.0
|
||||||
vine==5.0.0
|
vine==5.0.0
|
||||||
websockets==10.3
|
websockets==10.3
|
||||||
zipp==3.8.1
|
zipp==3.8.1
|
||||||
drf_spectacular==0.22.1
|
drf-spectacular==0.23.1
|
||||||
meshctrl==0.1.15
|
meshctrl==0.1.15
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ def process_nats_response(data: Union[str, Dict]) -> Tuple[bool, bool, str]:
|
|||||||
else "timeout"
|
else "timeout"
|
||||||
)
|
)
|
||||||
|
|
||||||
return (success, natserror, errormsg)
|
return success, natserror, errormsg
|
||||||
|
|
||||||
|
|
||||||
class GetServices(APIView):
|
class GetServices(APIView):
|
||||||
|
|||||||
@@ -61,4 +61,4 @@ class APIAuthentication(BaseAuthentication):
|
|||||||
if apikey.expiration and apikey.expiration < djangotime.now():
|
if apikey.expiration and apikey.expiration < djangotime.now():
|
||||||
raise exceptions.AuthenticationFailed(_("The token as expired."))
|
raise exceptions.AuthenticationFailed(_("The token as expired."))
|
||||||
|
|
||||||
return (apikey.user, apikey.key)
|
return apikey.user, apikey.key
|
||||||
|
|||||||
@@ -240,6 +240,14 @@ AGENT_DEFER = (
|
|||||||
"modified_time",
|
"modified_time",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
AGENT_TABLE_DEFER = (
|
||||||
|
"services",
|
||||||
|
"created_by",
|
||||||
|
"created_time",
|
||||||
|
"modified_by",
|
||||||
|
"modified_time",
|
||||||
|
)
|
||||||
|
|
||||||
ONLINE_AGENTS = (
|
ONLINE_AGENTS = (
|
||||||
"pk",
|
"pk",
|
||||||
"agent_id",
|
"agent_id",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ def get_certs() -> tuple[str, str]:
|
|||||||
cert_file = settings.CERT_FILE
|
cert_file = settings.CERT_FILE
|
||||||
key_file = settings.KEY_FILE
|
key_file = settings.KEY_FILE
|
||||||
|
|
||||||
return (cert_file, key_file)
|
return cert_file, key_file
|
||||||
|
|
||||||
|
|
||||||
def notify_error(msg: str) -> Response:
|
def notify_error(msg: str) -> Response:
|
||||||
@@ -33,7 +33,7 @@ def get_nats_ports() -> tuple[int, int]:
|
|||||||
nats_standard_port = getattr(settings, "NATS_STANDARD_PORT", 4222)
|
nats_standard_port = getattr(settings, "NATS_STANDARD_PORT", 4222)
|
||||||
nats_websocket_port = getattr(settings, "NATS_WEBSOCKET_PORT", 9235)
|
nats_websocket_port = getattr(settings, "NATS_WEBSOCKET_PORT", 9235)
|
||||||
|
|
||||||
return (nats_standard_port, nats_websocket_port)
|
return nats_standard_port, nats_websocket_port
|
||||||
|
|
||||||
|
|
||||||
def date_is_in_past(*, datetime_obj: "datetime", agent_tz: str) -> bool:
|
def date_is_in_past(*, datetime_obj: "datetime", agent_tz: str) -> bool:
|
||||||
|
|||||||
@@ -17,26 +17,26 @@ 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.14.5"
|
TRMM_VERSION = "0.14.7"
|
||||||
|
|
||||||
# https://github.com/amidaware/tacticalrmm-web
|
# https://github.com/amidaware/tacticalrmm-web
|
||||||
WEB_VERSION = "0.100.7"
|
WEB_VERSION = "0.100.9"
|
||||||
|
|
||||||
# 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.168"
|
APP_VER = "0.0.170"
|
||||||
|
|
||||||
# https://github.com/amidaware/rmmagent
|
# https://github.com/amidaware/rmmagent
|
||||||
LATEST_AGENT_VER = "2.2.1"
|
LATEST_AGENT_VER = "2.3.0"
|
||||||
|
|
||||||
MESH_VER = "1.0.60"
|
MESH_VER = "1.0.72"
|
||||||
|
|
||||||
NATS_SERVER_VER = "2.8.4"
|
NATS_SERVER_VER = "2.8.4"
|
||||||
|
|
||||||
# for the update script, bump when need to recreate venv
|
# for the update script, bump when need to recreate venv
|
||||||
PIP_VER = "31"
|
PIP_VER = "32"
|
||||||
|
|
||||||
SETUPTOOLS_VER = "62.6.0"
|
SETUPTOOLS_VER = "65.2.0"
|
||||||
WHEEL_VER = "0.37.1"
|
WHEEL_VER = "0.37.1"
|
||||||
|
|
||||||
AGENT_BASE_URL = "https://agents.tacticalrmm.com"
|
AGENT_BASE_URL = "https://agents.tacticalrmm.com"
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ nginxdefaultconf='/etc/nginx/nginx.conf'
|
|||||||
# increase default nginx worker connections
|
# increase default nginx worker connections
|
||||||
/bin/bash -c "sed -i 's/worker_connections.*/worker_connections ${WORKER_CONNECTIONS};/g' $nginxdefaultconf"
|
/bin/bash -c "sed -i 's/worker_connections.*/worker_connections ${WORKER_CONNECTIONS};/g' $nginxdefaultconf"
|
||||||
|
|
||||||
sed -i '1s/^/worker_rlimit_nofile 1000000;\
|
grep -q -e 'worker_rlimit_nofile' "${nginxdefaultconf}" || sed -i -e '/worker_processes.*/a\' -e 'worker_rlimit_nofile 1000000;' "${nginxdefaultconf}"
|
||||||
/' $nginxdefaultconf
|
|
||||||
|
|
||||||
if [[ $DEV -eq 1 ]]; then
|
if [[ $DEV -eq 1 ]]; then
|
||||||
API_NGINX="
|
API_NGINX="
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# creates python virtual env
|
# creates python virtual env
|
||||||
FROM python:3.10.4-slim AS CREATE_VENV_STAGE
|
FROM python:3.10.6-slim AS CREATE_VENV_STAGE
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
@@ -21,14 +21,14 @@ RUN apt-get update && \
|
|||||||
pip install --no-cache-dir -r ${TACTICAL_TMP_DIR}/api/requirements.txt
|
pip install --no-cache-dir -r ${TACTICAL_TMP_DIR}/api/requirements.txt
|
||||||
|
|
||||||
# pulls community scripts from git repo
|
# pulls community scripts from git repo
|
||||||
FROM python:3.10.4-slim AS GET_SCRIPTS_STAGE
|
FROM python:3.10.6-slim AS GET_SCRIPTS_STAGE
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y --no-install-recommends git && \
|
apt-get install -y --no-install-recommends git && \
|
||||||
git clone https://github.com/amidaware/community-scripts.git /community-scripts
|
git clone https://github.com/amidaware/community-scripts.git /community-scripts
|
||||||
|
|
||||||
# runtime image
|
# runtime image
|
||||||
FROM python:3.10.4-slim
|
FROM python:3.10.6-slim
|
||||||
|
|
||||||
# set env variables
|
# set env variables
|
||||||
ENV VIRTUAL_ENV /opt/venv
|
ENV VIRTUAL_ENV /opt/venv
|
||||||
|
|||||||
4
go.mod
4
go.mod
@@ -13,11 +13,11 @@ require (
|
|||||||
google.golang.org/protobuf v1.28.0 // indirect
|
google.golang.org/protobuf v1.28.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require github.com/sirupsen/logrus v1.8.1
|
require github.com/sirupsen/logrus v1.9.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/nats-io/nkeys v0.3.0 // indirect
|
github.com/nats-io/nkeys v0.3.0 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
|
||||||
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
18
go.sum
18
go.sum
@@ -1,3 +1,4 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
@@ -26,10 +27,11 @@ github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
|||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||||
@@ -39,10 +41,9 @@ golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm
|
|||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||||
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
|
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
|
||||||
@@ -52,3 +53,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
|||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
SCRIPT_VERSION="66"
|
SCRIPT_VERSION="67"
|
||||||
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
|
||||||
@@ -12,7 +12,7 @@ RED='\033[0;31m'
|
|||||||
NC='\033[0m'
|
NC='\033[0m'
|
||||||
|
|
||||||
SCRIPTS_DIR='/opt/trmm-community-scripts'
|
SCRIPTS_DIR='/opt/trmm-community-scripts'
|
||||||
PYTHON_VER='3.10.4'
|
PYTHON_VER='3.10.6'
|
||||||
SETTINGS_FILE='/rmm/api/tacticalrmm/tacticalrmm/settings.py'
|
SETTINGS_FILE='/rmm/api/tacticalrmm/tacticalrmm/settings.py'
|
||||||
|
|
||||||
TMP_FILE=$(mktemp -p "" "rmminstall_XXXXXXXXXX")
|
TMP_FILE=$(mktemp -p "" "rmminstall_XXXXXXXXXX")
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "3.1.0"
|
version = "3.2.0"
|
||||||
log = logrus.New()
|
log = logrus.New()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -74,24 +74,15 @@ func Svc(logger *logrus.Logger, cfg string) {
|
|||||||
stmt := `
|
stmt := `
|
||||||
UPDATE agents_agent
|
UPDATE agents_agent
|
||||||
SET hostname=$1, operating_system=$2,
|
SET hostname=$1, operating_system=$2,
|
||||||
plat=$3, total_ram=$4, boot_time=$5, needs_reboot=$6, logged_in_username=$7
|
plat=$3, total_ram=$4, boot_time=$5, needs_reboot=$6, logged_in_username=$7, goarch=$8
|
||||||
WHERE agents_agent.agent_id=$8;`
|
WHERE agents_agent.agent_id=$9;`
|
||||||
|
|
||||||
logger.Debugln("Info", r)
|
logger.Debugln("Info", r)
|
||||||
_, err = db.Exec(stmt, r.Hostname, r.OS, r.Platform, r.TotalRAM, r.BootTime, r.RebootNeeded, r.Username, r.Agentid)
|
_, err = db.Exec(stmt, r.Hostname, r.OS, r.Platform, r.TotalRAM, r.BootTime, r.RebootNeeded, r.Username, r.GoArch, r.Agentid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorln(err)
|
logger.Errorln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO add this to main stmt once agent 2.0.0 has been out for a while
|
|
||||||
if r.GoArch != "" {
|
|
||||||
stmt = `UPDATE agents_agent SET goarch=$1 WHERE agents_agent.agent_id=$2;`
|
|
||||||
_, err = db.Exec(stmt, r.GoArch, r.Agentid)
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorln(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Username != "None" {
|
if r.Username != "None" {
|
||||||
stmt = `UPDATE agents_agent SET last_logged_in_user=$1 WHERE agents_agent.agent_id=$2;`
|
stmt = `UPDATE agents_agent SET last_logged_in_user=$1 WHERE agents_agent.agent_id=$2;`
|
||||||
logger.Debugln("Updating last logged in user:", r.Username)
|
logger.Debugln("Updating last logged in user:", r.Username)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
SCRIPT_VERSION="40"
|
SCRIPT_VERSION="41"
|
||||||
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
|
||||||
@@ -13,7 +13,7 @@ RED='\033[0;31m'
|
|||||||
NC='\033[0m'
|
NC='\033[0m'
|
||||||
|
|
||||||
SCRIPTS_DIR='/opt/trmm-community-scripts'
|
SCRIPTS_DIR='/opt/trmm-community-scripts'
|
||||||
PYTHON_VER='3.10.4'
|
PYTHON_VER='3.10.6'
|
||||||
SETTINGS_FILE='/rmm/api/tacticalrmm/tacticalrmm/settings.py'
|
SETTINGS_FILE='/rmm/api/tacticalrmm/tacticalrmm/settings.py'
|
||||||
|
|
||||||
TMP_FILE=$(mktemp -p "" "rmmrestore_XXXXXXXXXX")
|
TMP_FILE=$(mktemp -p "" "rmmrestore_XXXXXXXXXX")
|
||||||
@@ -175,7 +175,7 @@ print_green 'Restoring systemd services'
|
|||||||
sudo cp $tmp_dir/systemd/* /etc/systemd/system/
|
sudo cp $tmp_dir/systemd/* /etc/systemd/system/
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
|
|
||||||
print_green 'Installing Python 3.10.4'
|
print_green "Installing Python ${PYTHON_VER}"
|
||||||
|
|
||||||
sudo apt install -y build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev libbz2-dev
|
sudo apt install -y build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev libbz2-dev
|
||||||
numprocs=$(nproc)
|
numprocs=$(nproc)
|
||||||
|
|||||||
@@ -309,9 +309,9 @@ fi
|
|||||||
printf >&2 "\n\n"
|
printf >&2 "\n\n"
|
||||||
|
|
||||||
#SSL Certificate check
|
#SSL Certificate check
|
||||||
cert=$(openssl verify -CAfile /etc/letsencrypt/live/$domain/chain.pem /etc/letsencrypt/live/$domain/cert.pem)
|
cert=$(sudo certbot certificates)
|
||||||
|
|
||||||
if [[ "$cert" == *"OK"* ]]; then
|
if [[ "$cert" != *"INVALID"* ]]; then
|
||||||
echo -ne ${GREEN} SSL Certificate for $domain is fine | tee -a checklog.log
|
echo -ne ${GREEN} SSL Certificate for $domain is fine | tee -a checklog.log
|
||||||
printf >&2 "\n\n"
|
printf >&2 "\n\n"
|
||||||
|
|
||||||
@@ -319,6 +319,10 @@ else
|
|||||||
echo -ne ${RED} SSL Certificate has expired or doesnt exist for $domain | tee -a checklog.log
|
echo -ne ${RED} SSL Certificate has expired or doesnt exist for $domain | tee -a checklog.log
|
||||||
printf >&2 "\n\n"
|
printf >&2 "\n\n"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Get List of Certbot Certificates
|
||||||
|
sudo certbot certificates | tee -a checklog.log
|
||||||
|
|
||||||
echo -ne ${YELLOW} Getting summary output of logs | tee -a checklog.log
|
echo -ne ${YELLOW} Getting summary output of logs | tee -a checklog.log
|
||||||
|
|
||||||
tail /rmm/api/tacticalrmm/tacticalrmm/private/log/django_debug.log | tee -a checklog.log
|
tail /rmm/api/tacticalrmm/tacticalrmm/private/log/django_debug.log | tee -a checklog.log
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
SCRIPT_VERSION="138"
|
SCRIPT_VERSION="139"
|
||||||
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'
|
||||||
@@ -10,7 +10,7 @@ NC='\033[0m'
|
|||||||
THIS_SCRIPT=$(readlink -f "$0")
|
THIS_SCRIPT=$(readlink -f "$0")
|
||||||
|
|
||||||
SCRIPTS_DIR='/opt/trmm-community-scripts'
|
SCRIPTS_DIR='/opt/trmm-community-scripts'
|
||||||
PYTHON_VER='3.10.4'
|
PYTHON_VER='3.10.6'
|
||||||
SETTINGS_FILE='/rmm/api/tacticalrmm/tacticalrmm/settings.py'
|
SETTINGS_FILE='/rmm/api/tacticalrmm/tacticalrmm/settings.py'
|
||||||
|
|
||||||
TMP_FILE=$(mktemp -p "" "rmmupdate_XXXXXXXXXX")
|
TMP_FILE=$(mktemp -p "" "rmmupdate_XXXXXXXXXX")
|
||||||
|
|||||||
Reference in New Issue
Block a user